diff options
Diffstat (limited to 'src/gtkext/gtkgraphdisplay.c')
-rw-r--r-- | src/gtkext/gtkgraphdisplay.c | 1226 |
1 files changed, 1226 insertions, 0 deletions
diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c new file mode 100644 index 0000000..1d4f831 --- /dev/null +++ b/src/gtkext/gtkgraphdisplay.c @@ -0,0 +1,1226 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gtkgraphdisplay.c - affichage de morceaux de code sous forme graphique + * + * Copyright (C) 2009-2016 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "gtkgraphdisplay.h" + + +#include <assert.h> + + +#include "gtkblockdisplay.h" +#include "gtkbufferdisplay.h" +#include "gtkdisplaypanel-int.h" +#include "graph/cluster.h" +#include "../analysis/blocks/flow.h" +#include "../format/format.h" +#include "../gui/editem.h" + + + +/* Composant d'affichage sous forme graphique (instance) */ +struct _GtkGraphDisplay +{ + GtkDisplayPanel parent; /* A laisser en premier */ + GtkWidget *support; /* Support des vues en bloc */ + GtkWidget *extender; /* Force la taille du support */ + + GBinRoutine *routine; /* Routine en cours d'affichage*/ + + segcnt_list *highlighted; /* Segments mis en évidence */ + + GGraphCluster *cluster; /* Disposition en graphique */ + + GGraphEdge **edges; /* Liens entre les noeuds */ + size_t edges_count; /* Quantité de ces liens */ + + gdouble start_x; /* Abscisse du point de souris */ + gdouble start_y; /* Ordonnée du point de souris */ + bool big_enough; /* Capacités de déplacement ? */ + gdouble ref_h; /* Position horizontale de ref.*/ + gdouble ref_v; /* Position verticale de ref. */ + +}; + +/* Composant d'affichage sous forme graphique (classe) */ +struct _GtkGraphDisplayClass +{ + GtkDisplayPanelClass parent; /* A laisser en premier */ + +}; + + +/* Profondeur de l'ombre */ +#define SHADOW_SIZE 4 + +/* Marges en bordure de graphique */ +#define GRAPH_MARGIN 23 + + +/* Initialise la classe générique des graphiques de code. */ +static void gtk_graph_display_class_init(GtkGraphDisplayClass *); + +/* Initialise une instance d'afficheur de code en graphique. */ +static void gtk_graph_display_init(GtkGraphDisplay *); + +/* Supprime toutes les références externes. */ +static void gtk_graph_display_dispose(GtkGraphDisplay *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_graph_display_finalize(GtkGraphDisplay *); + +/* S'adapte à la surface concédée par le composant parent. */ +static void gtk_graph_display_size_allocate(GtkWidget *, GtkAllocation *); + +/* Centre si possible le contenu du panneau d'affichage. */ +static void gtk_graph_display_update_support_margins(GtkGraphDisplay *, const GtkAllocation *); + +/* Indique les dimensions de travail du composant d'affichage. */ +static void gtk_graph_display_compute_requested_size(GtkGraphDisplay *, gint *, gint *); + +/* Réagit à un défilement chez une barre associée au composant. */ +static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *, GtkAdjustment *, GtkOrientation); + +/* Met à jour l'affichage de la vue sous forme graphique. */ +static gboolean gtk_graph_display_draw(GtkWidget *, cairo_t *, GtkGraphDisplay *); + +/* Assure la gestion des clics de souris sur le composant. */ +static gboolean gtk_graph_display_button_press(GtkWidget *, GdkEventButton *, GtkGraphDisplay *); + +/* Assure la gestion des clics de souris sur le composant. */ +static gboolean gtk_graph_display_button_release(GtkWidget *, GdkEventButton *, GtkGraphDisplay *); + +/* Assure la suivi des déplacements de souris sur le composant. */ +static gboolean gtk_graph_display_motion_notify(GtkWidget *, GdkEventMotion *, GtkGraphDisplay *); + +/* Réagit à la sélection externe d'une adresse. */ +static void gtk_graph_display_define_main_address(GtkGraphDisplay *, const vmpa2t *); + +/* Indique la position courante du curseur. */ +static const vmpa2t *gtk_graph_display_get_caret_location(const GtkGraphDisplay *); + +/* Indique la position d'affichage d'une adresse donnée. */ +static bool gtk_graph_display_get_address_coordinates(const GtkGraphDisplay *, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak); + +/* Déplace le curseur à un emplacement défini. */ +static bool gtk_graph_display_move_caret_to(GtkGraphDisplay *, gint, gint); + +/* Place en cache un rendu destiné à l'aperçu graphique rapide. */ +static void gtk_graph_display_cache_glance(GtkGraphDisplay *, cairo_t *, const GtkAllocation *, double); + +/* Supprime tout contenu de l'afficheur de code en graphique. */ +static void gtk_graph_display_reset(GtkGraphDisplay *); + +/* Notifie un changement de surbrillance au sein d'un noeud. */ +static void gtk_graph_display_changed_highlights(GtkBlockDisplay *, GtkGraphDisplay *); + +/* Notifie une incapacité de déplacement au sein d'un noeud. */ +static void gtk_graph_display_reach_caret_limit(GtkBufferDisplay *, GdkScrollDirection, GtkGraphDisplay *); + + + +/* Détermine le type du composant d'affichage en graphique. */ +G_DEFINE_TYPE(GtkGraphDisplay, gtk_graph_display, GTK_TYPE_DISPLAY_PANEL) + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe générique des graphiques de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_class_init(GtkGraphDisplayClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget_class; /* Classe de haut niveau */ + GtkDisplayPanelClass *panel_class; /* Classe parente */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_graph_display_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_graph_display_finalize; + + widget_class = GTK_WIDGET_CLASS(class); + + widget_class->size_allocate = gtk_graph_display_size_allocate; + + panel_class = GTK_DISPLAY_PANEL_CLASS(class); + + panel_class->compute_size = (compute_requested_size_fc)gtk_graph_display_compute_requested_size; + panel_class->adjust = (adjust_scroll_value_fc)gtk_graph_display_adjust_scroll_value; + panel_class->define = (define_address_fc)gtk_graph_display_define_main_address; + + panel_class->get_caret_loc = (get_caret_location_fc)gtk_graph_display_get_caret_location; + panel_class->get_coordinates = (get_addr_coordinates_fc)gtk_graph_display_get_address_coordinates; + panel_class->move_caret_to = (move_caret_to_fc)gtk_graph_display_move_caret_to; + panel_class->cache_glance = (cache_glance_fc)gtk_graph_display_cache_glance; + +} + + +/****************************************************************************** +* * +* Paramètres : display = instance GTK à initialiser. * +* * +* Description : Initialise une instance d'afficheur de code en graphique. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_init(GtkGraphDisplay *display) +{ + display->support = gtk_fixed_new(); + gtk_widget_set_has_window(display->support, TRUE); + gtk_widget_set_can_focus(display->support, TRUE); + + g_signal_connect(G_OBJECT(display->support), "draw", + G_CALLBACK(gtk_graph_display_draw), display); + + g_signal_connect(G_OBJECT(display->support), "button-press-event", + G_CALLBACK(gtk_graph_display_button_press), display); + g_signal_connect(G_OBJECT(display->support), "button-release-event", + G_CALLBACK(gtk_graph_display_button_release), display); + g_signal_connect(G_OBJECT(display->support), "motion-notify-event", + G_CALLBACK(gtk_graph_display_motion_notify), display); + + gtk_widget_add_events(display->support, + GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + gtk_widget_show(display->support); + + gtk_fixed_put(GTK_FIXED(display), display->support, 0, 0); + + display->extender = gtk_fixed_new(); + + gtk_widget_set_margin_end(display->extender, 1); + gtk_widget_set_margin_top(display->extender, 1); + + gtk_widget_show(display->extender); + +} + + +/****************************************************************************** +* * +* Paramètres : display = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_dispose(GtkGraphDisplay *display) +{ + g_object_unref(G_OBJECT(display->extender)); + + gtk_graph_display_reset(display); + + G_OBJECT_CLASS(gtk_graph_display_parent_class)->dispose(G_OBJECT(display)); + +} + + +/****************************************************************************** +* * +* Paramètres : display = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_finalize(GtkGraphDisplay *display) +{ + G_OBJECT_CLASS(gtk_graph_display_parent_class)->finalize(G_OBJECT(display)); + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant GTK à mettre à jour. * +* allocation = étendue accordée à la vue. * +* * +* Description : S'adapte à la surface concédée par le composant parent. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + GtkGraphDisplay *display; /* Autre version du composant */ + + GTK_WIDGET_CLASS(gtk_graph_display_parent_class)->size_allocate(widget, allocation); + + display = GTK_GRAPH_DISPLAY(widget); + + gtk_graph_display_update_support_margins(display, allocation); + +} + + +/****************************************************************************** +* * +* Paramètres : display = panneau dont le contenu est à déplacer. * +* allocation = étendue accordée à la vue. * +* * +* Description : Centre si possible le contenu du panneau d'affichage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_update_support_margins(GtkGraphDisplay *display, const GtkAllocation *allocation) +{ + gint width; /* Largeur totale du support */ + gint height; /* Hauteur totale du support */ + gint start; /* Bordure horizontale */ + gint top; /* Bordure verticale */ + + gtk_graph_display_compute_requested_size(display, &width, &height); + + if (width > allocation->width) + start = 0; + else + start = (allocation->width - width) / 2; + + if (height > allocation->height) + top = 0; + else + top = (allocation->height - height) / 2; + + gtk_widget_set_margin_start(display->support, start); + gtk_widget_set_margin_top(display->support, top); + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à consulter. * +* width = largeur requise à renseigner ou NULL. [OUT] * +* height = hauteur requise à renseigner ou NULL. [OUT] * +* * +* Description : Indique les dimensions de travail du composant d'affichage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_compute_requested_size(GtkGraphDisplay *display, gint *width, gint *height) +{ + GtkAllocation needed; /* Taille requise */ + + if (display->cluster != NULL) + { + g_graph_cluster_compute_needed_alloc(display->cluster, &needed); + assert(needed.x == 0 && needed.y == 0); + + needed.width += 2 * GRAPH_MARGIN; + needed.height += 2 * GRAPH_MARGIN; + + } + else + { + needed.width = 0; + needed.height = 0; + } + + if (width != NULL) *width = needed.width; + if (height != NULL) *height = needed.height; + +} + + +/****************************************************************************** +* * +* Paramètres : display = panneau d'affichage concerné. * +* adj = défilement dont une valeur a changé. * +* orientation = indication sur le défilement à traiter. * +* * +* Description : Réagit à un défilement chez une barre associée au composant. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *display, GtkAdjustment *adj, GtkOrientation orientation) +{ + gint fake_x; /* Abscisse virtuelle */ + gint fake_y; /* Ordonnée virtuelle */ + + fake_x = 0; + fake_y = 0; + gtk_display_panel_compute_fake_coord(GTK_DISPLAY_PANEL(display), &fake_x, &fake_y); + + gtk_fixed_move(GTK_FIXED(display), display->support, fake_x, -fake_y); + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant GTK à redessiner. * +* cr = contexte graphique associé à l'événement. * +* display = support maître à consulter. * +* * +* Description : Met à jour l'affichage de la vue sous forme graphique. * +* * +* Retour : FALSE pour poursuivre la propagation de l'événement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphDisplay *display) +{ + size_t i; /* Boucle de parcours */ + + void draw_shadow(GtkWidget *child, gpointer unused) + { + GtkAllocation alloc; /* Emplacement de l'enfant */ + gint j; /* Boucle de parcours */ + cairo_pattern_t *pattern; /* Zones d'application */ + + /* On évite l'extenseur de support... */ + if (!GTK_IS_DISPLAY_PANEL(child)) + return; + + gtk_widget_get_allocation(child, &alloc); + + for (j = 1; j < SHADOW_SIZE; j++) + { + cairo_push_group(cr); + + gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x + j, alloc.y + j); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + cairo_fill(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x, alloc.y); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_fill(cr); + + pattern = cairo_pop_group(cr); + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.3); + cairo_mask(cr, pattern); + cairo_fill(cr); + + cairo_pattern_destroy(pattern); + + } + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_shadow, NULL); + + for (i = 0; i < display->edges_count; i++) + g_graph_edge_draw(display->edges[i], cr, true); + + return FALSE; + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant GTK visé par l'opération. * +* event = informations liées à l'événement. * +* display = support maître à consulter. * +* * +* Description : Assure la gestion des clics de souris sur le composant. * +* * +* Retour : FALSE pour poursuivre la propagation de l'événement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_graph_display_button_press(GtkWidget *widget, GdkEventButton *event, GtkGraphDisplay *display) +{ + gboolean result; /* Poursuite à faire suivre */ + GtkScrolledWindow *support; /* Support défilant associé */ + GtkAdjustment *hadj; /* Gestionnaire du défilement */ + GtkAdjustment *vadj; /* Gestionnaire du défilement */ + GdkCursor *cursor; /* Pointeur pour la surface */ + + result = FALSE; + + if (event->button == 1) + { + support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(display))); + + hadj = gtk_scrolled_window_get_hadjustment(support); + vadj = gtk_scrolled_window_get_vadjustment(support); + + display->big_enough = (gtk_adjustment_get_upper(hadj) > gtk_adjustment_get_page_size(hadj) + || gtk_adjustment_get_upper(vadj) > gtk_adjustment_get_page_size(vadj)); + + if (display->big_enough) + { + display->start_x = event->x_root; + display->start_y = event->y_root; + + display->ref_h = gtk_adjustment_get_value(hadj); + display->ref_v = gtk_adjustment_get_value(vadj); + + cursor = gdk_cursor_new(GDK_FLEUR); + gdk_window_set_cursor(gtk_widget_get_window(widget), cursor); + g_object_unref(G_OBJECT(cursor)); + + result = TRUE; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant GTK visé par l'opération. * +* event = informations liées à l'événement. * +* display = support maître à consulter. * +* * +* Description : Assure la gestion des clics de souris sur le composant. * +* * +* Retour : FALSE pour poursuivre la propagation de l'événement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_graph_display_button_release(GtkWidget *widget, GdkEventButton *event, GtkGraphDisplay *display) +{ + if (event->button == 1 && display->big_enough) + gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); + + return FALSE; + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant GTK visé par l'opération. * +* event = informations liées à l'événement. * +* display = support maître à consulter. * +* * +* Description : Assure la suivi des déplacements de souris sur le composant. * +* * +* Retour : FALSE pour poursuivre la propagation de l'événement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotion *event, GtkGraphDisplay *display) +{ + gdouble diff_x; /* Evolution sur les abscisses */ + gdouble diff_y; /* Evolution sur les ordonnées */ + GtkScrolledWindow *support; /* Support défilant associé */ + GtkAdjustment *hadj; /* Gestionnaire du défilement */ + GtkAdjustment *vadj; /* Gestionnaire du défilement */ + gdouble value; /* Nouvelle valeur bornée */ + + if (event->state & GDK_BUTTON1_MASK && display->big_enough) + { + diff_x = display->start_x - event->x_root; + diff_y = display->start_y - event->y_root; + + support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(display))); + + hadj = gtk_scrolled_window_get_hadjustment(support); + vadj = gtk_scrolled_window_get_vadjustment(support); + + value = CLAMP(display->ref_h + diff_x, gtk_adjustment_get_lower(hadj), + gtk_adjustment_get_upper(hadj) - gtk_adjustment_get_page_size(hadj)); + gtk_adjustment_set_value(hadj, value); + + value = CLAMP(display->ref_v + diff_y, gtk_adjustment_get_lower(vadj), + gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj)); + gtk_adjustment_set_value(vadj, value); + + } + + return FALSE; + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à mettre à jour. * +* addr = adresse sélectionnée de manière externe. * +* * +* Description : Réagit à la sélection externe d'une adresse. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_define_main_address(GtkGraphDisplay *display, const vmpa2t *addr) +{ + bool need_update; /* Mise à jour du contenu ? */ + const mrange_t *range; /* Couverture courante */ + GExeFormat *format; /* Type de fichier chargé */ + GBinRoutine **routines; /* Liste des routines trouvées */ + size_t routines_count; /* Nombre de ces routines */ + size_t i; /* Boucle de parcours */ + gint right; /* Abscisse du coin droit */ + gint bottom; /* Ordonnée du coin inférieur */ + GtkAllocation allocation; /* Espace alloué au panneau */ + + if (display->routine == NULL) + need_update = true; + else + { + range = g_binary_routine_get_range(display->routine); + need_update = !mrange_contains_addr(range, addr); + } + + if (need_update) + { + gtk_graph_display_reset(display); + + format = g_loaded_binary_get_format(GTK_DISPLAY_PANEL(display)->binary); + routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count); + + for (i = 0; i < routines_count; i++) + { + range = g_binary_routine_get_range(routines[i]); + + if (mrange_contains_addr(range, addr)) + { + display->routine = routines[i]; + g_object_ref(G_OBJECT(display->routine)); + + display->highlighted = init_segment_content_list(); + + /* + display->children = gtk_graph_display_load_nodes(display, GTK_DISPLAY_PANEL(display)->binary, + routines[i]); + + display->allocs = (GtkAllocation *)calloc(display->children_count, + sizeof(GtkAllocation)); + + display->layout = g_graph_layout_new(g_binary_routine_get_basic_blocks(display->routine), + display->children, display->children_count); + + g_graph_layout_place(display->layout, display); + */ + + do + { + + GBlockList *list; + + list = g_binary_routine_get_basic_blocks(routines[i]); + +#if 0 + display->cluster = g_graph_cluster_new(GTK_DISPLAY_PANEL(display)->binary, + list, 0/* FIXME */, display->highlighted); +#endif + + + display->cluster = bootstrap_graph_cluster(GTK_DISPLAY_PANEL(display)->binary, + list, display->highlighted); + + + + g_graph_cluster_place(display->cluster, display); + + /** + * Comme la taille du support ne peut pas être forcée et + * étendue pour comprendre les ombres, on place un composant + * minuscule à l'extrémité de ce support. + */ + + gtk_graph_display_compute_requested_size(display, &right, &bottom); + + g_object_ref(G_OBJECT(display->extender)); + gtk_fixed_put(GTK_FIXED(display->support), display->extender, right - 1, bottom - 1); + + /** + * Si possible, on centre le contenu obtenu. + */ + + gtk_widget_get_allocation(GTK_WIDGET(display), &allocation); + + gtk_graph_display_update_support_margins(display, &allocation); + + + + } + while (0); + + + + break; + + } + + } + + change_editor_items_current_view_content(GTK_DISPLAY_PANEL(display)); + + g_object_unref(G_OBJECT(format)); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à manipuler. * +* * +* Description : Indique la position courante du curseur. * +* * +* Retour : Emplacement courant du curseur ou NULL si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static const vmpa2t *gtk_graph_display_get_caret_location(const GtkGraphDisplay *display) +{ + return NULL; /* FIXME */ + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à consulter. * +* addr = adresse à présenter à l'écran. * +* x = position horizontale au sein du composant. [OUT] * +* y = position verticale au sein du composant. [OUT] * +* tweak = adaptation finale à effectuer. * +* * +* Description : Indique la position d'affichage d'une adresse donnée. * +* * +* Retour : true si l'adresse fait partie du composant, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool gtk_graph_display_get_address_coordinates(const GtkGraphDisplay *display, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak) +{ + /* TODO */ + + return false; + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à manipuler. * +* x = abscisse proposée pour le nouvel emplacement. * +* y = ordonnée proposée pour le nouvel emplacement. * +* * +* Description : Déplace le curseur à un emplacement défini. * +* * +* Retour : true si un traitement a été effectué, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool gtk_graph_display_move_caret_to(GtkGraphDisplay *display, gint x, gint y) +{ + bool result; /* Bilan à retourner */ + + result = false; + + void move_caret_to_sub_block(GtkWidget *child, gpointer unused) + { + GtkAllocation alloc; /* Emplacement réservé */ + GtkDisplayPanel *panel; /* Autre vision d'enfance */ + gint sub_x; /* Abscisse relative à l'enfant*/ + gint sub_y; /* Ordonnée relative à l'enfant*/ + + if (result) + return; + + if (!GTK_IS_BUFFER_DISPLAY(child)) + return; + + gtk_widget_get_allocation(child, &alloc); + + if (x < alloc.x || x >= (alloc.x + alloc.width)) return; + if (y < alloc.y || y >= (alloc.y + alloc.height)) return; + + panel = GTK_DISPLAY_PANEL(child); + + sub_x = x - alloc.x; + sub_y = y - alloc.y; + + result = GTK_DISPLAY_PANEL_GET_CLASS(panel)->move_caret_to(panel, sub_x, sub_y); + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)move_caret_to_sub_block, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à manipuler. * +* cr = assistant pour la création de rendus. * +* area = taille de la surface réduite à disposition. * +* scale = échelle vis à vis de la taille réelle. * +* * +* Description : Place en cache un rendu destiné à l'aperçu graphique rapide. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr, const GtkAllocation *area, double scale) +{ + size_t i; /* Boucle de parcours */ + + void draw_child_glance(GtkWidget *child, gpointer unused) + { + GtkAllocation sub_area; /* Emplacement réservé */ + + if (!GTK_IS_BUFFER_DISPLAY(child)) + return; + + gtk_widget_get_allocation(child, &sub_area); + + sub_area.x *= scale; + sub_area.y *= scale; + sub_area.width = sub_area.width * scale + 1; + sub_area.height = sub_area.height * scale + 1; + + gtk_display_panel_cache_glance(GTK_DISPLAY_PANEL(child), cr, &sub_area, scale); + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_child_glance, NULL); + + cairo_scale(cr, scale, scale); + + for (i = 0; i < display->edges_count; i++) + g_graph_edge_draw(display->edges[i], cr, false); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un nouveau composant pour l'affichage en graphique. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *gtk_graph_display_new(void) +{ + return g_object_new(GTK_TYPE_GRAPH_DISPLAY, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à mettre à jour. * +* widget = composant GTK à insérer. * +* x = abscisse du point d'insertion. * +* y = ordonnée du point d'insertion. * +* * +* Description : Place une vue sous forme de bloc dans le graphique. * +* * +* Retour : Plutôt que de redéfinir *toutes* les méthodes de * +* GtkContainer, on étend ! * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_graph_display_put(GtkGraphDisplay *display, GtkWidget *widget, const GtkAllocation *alloc) +{ + g_signal_connect(widget, "reach-limit", G_CALLBACK(gtk_graph_display_reach_caret_limit), display); + g_signal_connect(widget, "highlight-changed", G_CALLBACK(gtk_graph_display_changed_highlights), display); + + gtk_fixed_put(GTK_FIXED(display->support), widget, GRAPH_MARGIN + alloc->x, GRAPH_MARGIN + alloc->y); + +} + + + +/****************************************************************************** +* * +* Paramètres : display = composant GTK à mettre à jour. * +* edge = lien entre noeuds à conserver. * +* * +* Description : Intègre un lien entre blocs graphiques dans l'afficheur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_graph_display_add_edge(GtkGraphDisplay *display, GGraphEdge *edge) +{ + g_graph_edge_offset(edge, GRAPH_MARGIN, GRAPH_MARGIN); + + display->edges = (GGraphEdge **)realloc(display->edges, + ++display->edges_count * sizeof(GGraphEdge *)); + + display->edges[display->edges_count - 1] = edge; + +} + + +/****************************************************************************** +* * +* Paramètres : display = instance GTK à réinitialiser. * +* * +* Description : Supprime tout contenu de l'afficheur de code en graphique. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_reset(GtkGraphDisplay *display) +{ + size_t i; /* Boucle de parcours */ + + + void detach_all_blocks(GtkWidget *widget, GtkContainer *container) + { + + + + gtk_container_remove(container, widget); + + + } + + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)detach_all_blocks, display->support); + + + + if (display->routine != NULL) + { + g_object_unref(G_OBJECT(display->routine)); + display->routine = NULL; + } + + if (display->highlighted != NULL) + exit_segment_content_list(display->highlighted); + + if (display->cluster != NULL) + { + g_object_unref(G_OBJECT(display->cluster)); + display->cluster = NULL; + } + + for (i = 0; i < display->edges_count; i++) + g_object_unref(G_OBJECT(display->edges[i])); + + if (display->edges_count > 0) + { + free(display->edges); + display->edges = NULL; + + display->edges_count = 0; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : node = composant d'affichage impliqué dans la procédure. * +* display = support graphique de tous les noeuds. * +* * +* Description : Notifie un changement de surbrillance au sein d'un noeud. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_changed_highlights(GtkBlockDisplay *node, GtkGraphDisplay *display) +{ + void refresh_highlights(GtkWidget *child, gpointer unused) + { + if (!GTK_IS_BUFFER_DISPLAY(child)) + return; + + if (child != GTK_WIDGET(node)) + gtk_widget_queue_draw(child); + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)refresh_highlights, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : node = composant d'affichage impliqué dans la procédure. * +* dir = direction du déplacement souhaité et impossible. * +* display = support graphique de tous les noeuds. * +* * +* Description : Notifie une incapacité de déplacement au sein d'un noeud. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_reach_caret_limit(GtkBufferDisplay *node, GdkScrollDirection dir, GtkGraphDisplay *display) +{ + GBufferView *view; /* Vue d'un tampon global */ + vmpa2t first; /* Début d'un groupe de lignes */ + vmpa2t last; /* Fin d'un groupe de lignes */ + const mrange_t *range; /* Couverture courante */ + GArchProcessor *proc; /* Processeur pour instructions*/ + vmpa2t iaddr; /* Position de l'instructin */ + instr_iter_t *iter; /* Boucle de parcours */ + GArchInstruction *instr; /* Instruction à venir visiter */ +#ifndef NDEBUG + bool is_return; /* Est-ce une instruc. finale ?*/ +#endif + GtkBufferDisplay *target; /* Bloc suivant pour le focus */ + + /* Détermination de l'instruction à cibler */ + + view = gtk_buffer_display_get_view(node); + g_buffer_view_get_restrictions(view, &first, &last); + g_object_unref(G_OBJECT(view)); + + range = g_binary_routine_get_range(display->routine); + + proc = g_loaded_binary_get_processor(GTK_DISPLAY_PANEL(display)->binary); + + init_vmpa(&iaddr, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + +#ifndef NDEBUG + is_return = false; +#endif + + switch (dir) + { + case GDK_SCROLL_LEFT: + case GDK_SCROLL_UP: + + if (cmp_vmpa(get_mrange_addr(range), &first) != 0) + { + iter = g_arch_processor_get_iter_from_address(proc, &first); + + if (iter != NULL) + { + instr = get_instruction_iterator_prev(iter); + + if (instr != NULL) + { + /* TODO : boucler si !HAS_CODE */ + + if (mrange_contains_addr(range, &iaddr)) + copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(instr))); + + g_object_unref(G_OBJECT(instr)); + + } + + delete_instruction_iterator(iter); + + } + + } + + break; + + case GDK_SCROLL_RIGHT: + case GDK_SCROLL_DOWN: + + iter = g_arch_processor_get_iter_from_address(proc, &last); + + if (iter != NULL) + { +#ifndef NDEBUG + instr = get_instruction_iterator_current(iter); + if (instr != NULL) + { + is_return = (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT); + g_object_unref(G_OBJECT(instr)); + } +#endif + + instr = get_instruction_iterator_next(iter); + + if (instr != NULL) + { + /* TODO : boucler si !HAS_CODE */ + + copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(instr))); + + if (!mrange_contains_addr(range, &iaddr)) + init_vmpa(&iaddr, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + + g_object_unref(G_OBJECT(instr)); + + } + + delete_instruction_iterator(iter); + + } + + break; + + case GDK_SCROLL_SMOOTH: + assert(false); /* Argument jamais généré */ + break; + + } + + g_object_unref(G_OBJECT(proc)); + + /* Recherche du bloc parent */ + + if (is_invalid_vmpa(&iaddr)) + return; + + target = NULL; + + void find_target_block(GtkWidget *child, gpointer unused) + { + GtkBufferDisplay *test; /* Candidat potentiel à tester */ + + if (!GTK_IS_BUFFER_DISPLAY(child)) + return; + + test = GTK_BUFFER_DISPLAY(child); + + view = gtk_buffer_display_get_view(test); + g_buffer_view_get_restrictions(view, &first, &last); + g_object_unref(G_OBJECT(view)); + + if (cmp_vmpa(&first, &iaddr) <= 0 && cmp_vmpa(&iaddr, &last) <= 0) + { + assert(target == NULL); + assert(node != test); + target = test; + return; + } + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)find_target_block, NULL); + + assert(target != NULL || is_return); + + /* Affichage du nouveau curseur */ + + /** + * Il se peut qu'aucune adresse suivante ne soit disponible : c'est typiquement + * le cas sous ARM, avec les valeurs brutes référencées dans le code. Ces valeurs + * sont incluses dans la surface couverte par la routine concernée, mais ne sont + * pas intégrées dans les blocs basiques associés. + */ + + if (target == NULL) + return; + + gtk_widget_grab_focus(GTK_WIDGET(target)); + + switch (dir) + { + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + gtk_buffer_display_move_caret_to(target, false, NULL); + break; + + break; + + case GDK_SCROLL_RIGHT: + case GDK_SCROLL_DOWN: + gtk_buffer_display_move_caret_to(target, true, NULL); + break; + + break; + + case GDK_SCROLL_SMOOTH: + assert(false); /* Argument jamais généré */ + break; + + } + + /* TODO : scrolling... */ + +} |