/* OpenIDA - Outil d'analyse de fichiers binaires
* gtkgraphview.c - affichage de morceaux de code sous forme graphique
*
* Copyright (C) 2009 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* OpenIDA 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.
*
* OpenIDA 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 .
*/
#include "gtkgraphview.h"
#include "gtkbinview-int.h"
#include "gtkblockview.h"
#include "../format/format.h"
#include "../graph/layout.h"
#include
/* Représentation de code binaire sous forme graphique (instace) */
struct _GtkGraphView
{
GtkBinView parent; /* A laisser en premier */
GtkFixed *support; /* Support des vues en bloc */
GtkRequisition requisition; /* Espace requis d'affichage */
vmpa_t start; /* Début de la portion vue */
vmpa_t end; /* Fin de la portion affichée */
GtkBinView **childs; /* Liste des sous-blocs */
size_t childs_count; /* Taille de cette liste */
size_t ready; /* Construction complète */
GMutex *mutex; /* Accès à la variable */
GCond *cond; /* Attente de changement */
GtkLinkRenderer **links; /* Liste des liens graphiques */
size_t links_count; /* Nombre de ces liens */
};
/* Représentation de code binaire sous forme graphique (classe) */
struct _GtkGraphViewClass
{
GtkBinViewClass parent; /* A laisser en premier */
};
/* Initialise la classe générique des graphiques de code. */
static void gtk_graph_view_class_init(GtkGraphViewClass *);
/* Initialise une instance d'afficheur de code en graphique. */
static void gtk_graph_view_init(GtkGraphView *);
/* Fournit la taille de composant requise pour un plein rendu. */
static void gtk_graph_view_size_request(GtkWidget *, GtkRequisition *);
/* S'adapte à la surface concédée par le composant parent. */
static void gtk_graph_view_size_allocate(GtkWidget *, GtkAllocation *);
/* Met à jour l'affichage de la vue sous forme graphique. */
static gboolean gtk_graph_view_expose(GtkWidget *, GdkEventExpose *, GtkGraphView *);
/* Supprime tout contenu de l'afficheur de code en graphique. */
static void gtk_graph_view_reset(GtkGraphView *);
/* Réagit à un défilement quelconque. */
static void gtk_graph_view_scroll(GtkGraphView *);
/* Définit les lignes du graphique de représentation. */
static void gtk_graph_view_set_rendering_lines(GtkGraphView *, GRenderingLine *, GRenderingLine *);
/* Réagit à la sélection externe d'une adresse. */
static void gtk_graph_view_define_main_address(GtkGraphView *, vmpa_t);
/* Indique la position d'affichage d'une adresse donnée. */
static bool gtk_graph_view_get_address_coordinates(const GtkGraphView *, vmpa_t, gint *, gint *);
/* Définit la liste complète des éléments du futur graphique. */
static GtkBinView **gtk_graph_view_load_nodes(GtkGraphView *, GOpenidaBinary *, GRenderingLine *, GRenderingLine *);
/* Prend note de la fin d'une construction d'une visualisation. */
static void notify_graph_view(GtkBinView *, GtkGraphView *);
/* Détermine le type du composant d'affichage en graphique. */
G_DEFINE_TYPE(GtkGraphView, gtk_graph_view, GTK_TYPE_BIN_VIEW)
/******************************************************************************
* *
* Paramètres : klass = classe GTK à initialiser. *
* *
* Description : Initialise la classe générique des graphiques de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_class_init(GtkGraphViewClass *klass)
{
GtkWidgetClass *widget_class; /* Classe version Widget */
widget_class = (GtkWidgetClass *)klass;
widget_class->size_request = gtk_graph_view_size_request;
widget_class->size_allocate = gtk_graph_view_size_allocate;
}
/******************************************************************************
* *
* Paramètres : view = instance GTK à initialiser. *
* *
* Description : Initialise une instance d'afficheur de code en graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_init(GtkGraphView *view)
{
GtkViewPanel *viewpanel; /* Instance parente #1 */
GtkBinView *binview; /* Instance parente #2 */
GdkColor white; /* Couleur de fond normale */
viewpanel = GTK_VIEW_PANEL(view);
viewpanel->scroll = (scroll_fc)gtk_graph_view_scroll;
binview = GTK_BIN_VIEW(view);
binview->set_lines = (set_rendering_lines_fc)gtk_graph_view_set_rendering_lines;
binview->define_address = (define_main_address_fc)gtk_graph_view_define_main_address;
binview->get_coordinates = (get_addr_coordinates_fc)gtk_graph_view_get_address_coordinates;
view->support = GTK_FIXED(gtk_fixed_new());
gtk_fixed_set_has_window(view->support, TRUE);
g_signal_connect(G_OBJECT(view->support), "expose-event",
G_CALLBACK(gtk_graph_view_expose), view);
gtk_widget_show(GTK_WIDGET(view->support));
gdk_color_white(gtk_widget_get_colormap(GTK_WIDGET(view->support)), &white);
gtk_widget_modify_bg(GTK_WIDGET(view->support), GTK_STATE_NORMAL, &white);
gtk_fixed_put(GTK_FIXED(view), GTK_WIDGET(view->support), 0, 0);
view->mutex = g_mutex_new();
view->cond = g_cond_new();
}
/******************************************************************************
* *
* Paramètres : widget = composant GTK à consulter. *
* requisition = dimensions souhaitées. [OUT] *
* *
* Description : Fournit la taille de composant requise pour un plein rendu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_size_request(GtkWidget *widget, GtkRequisition *requisition)
{
gpointer fixed_class; /* Classe parente */
GtkGraphView *view; /* Autre vision du composant */
fixed_class = g_type_class_peek_parent(GTK_GRAPH_VIEW_GET_CLASS(widget));
GTK_WIDGET_CLASS(fixed_class)->size_request(GTK_FIXED(widget), requisition);
view = GTK_GRAPH_VIEW(widget);
if (view->requisition.width == 0 && view->requisition.height == 0)
view->requisition = *requisition;
}
/******************************************************************************
* *
* Paramètres : view = 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_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
gpointer fixed_class; /* Classe parente */
GtkViewPanel *panel; /* Autre version du composant */
GtkAllocation valloc; /* Surface utilisable */
gboolean changed; /* Changement de valeur ? */
/* Mise à jour GTK */
fixed_class = g_type_class_peek_parent(GTK_GRAPH_VIEW_GET_CLASS(widget));
GTK_WIDGET_CLASS(fixed_class)->size_allocate(GTK_FIXED(widget), allocation);
panel = GTK_VIEW_PANEL(widget);
if (panel->hadjustment == NULL || panel->vadjustment == NULL)
return;
gtk_view_panel_compute_allocation(panel, &valloc);
/* Défilement horizontal */
panel->hadjustment->page_size = valloc.width;
panel->hadjustment->step_increment = valloc.width * 0.1;
panel->hadjustment->page_increment = valloc.width * 0.9;
panel->hadjustment->upper = MAX(GTK_GRAPH_VIEW(widget)->requisition.width, valloc.width);
gtk_view_panel_reclamp_adjustment(panel->hadjustment, &changed);
gtk_adjustment_changed(panel->hadjustment);
if (changed)
gtk_adjustment_value_changed(panel->hadjustment);
/* Défilement vertical */
panel->vadjustment->page_size = valloc.height;
panel->vadjustment->step_increment = valloc.width * 0.1;
panel->vadjustment->page_increment = valloc.width * 0.9;
panel->vadjustment->upper = MAX(GTK_GRAPH_VIEW(widget)->requisition.height, valloc.height);
gtk_view_panel_reclamp_adjustment(panel->vadjustment, &changed);
gtk_adjustment_changed(panel->vadjustment);
if (changed)
gtk_adjustment_value_changed(panel->vadjustment);
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Crée un nouveau composant pour l'affichage en graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GtkWidget* gtk_graph_view_new(void)
{
return g_object_new(GTK_TYPE_GRAPH_VIEW, NULL);
}
/******************************************************************************
* *
* Paramètres : widget = composant GTK à redessiner. *
* event = informations liées à l'événement. *
* view = 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_view_expose(GtkWidget *widget, GdkEventExpose *event, GtkGraphView *view)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < view->links_count; i++)
gtk_link_renderer_draw(view->links[i],
GDK_DRAWABLE(widget->window),
GTK_VIEW_PANEL(view)->gc);
return FALSE;
}
/******************************************************************************
* *
* Paramètres : view = instance GTK à réinitialiser. *
* *
* Description : Supprime tout contenu de l'afficheur de code en graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_reset(GtkGraphView *view)
{
size_t i; /* Boucle de parcours */
view->requisition.width = 0;
view->requisition.height = 0;
view->start = 0;
view->end = 0;
for (i = 0; i < view->links_count; i++)
gtk_object_destroy(view->links[i]);
if (view->links_count > 0)
{
free(view->links);
view->links = NULL;
view->links_count = 0;
}
for (i = 0; i < view->childs_count; i++)
gtk_widget_destroy(view->childs[i]);
if (view->childs_count > 0)
{
free(view->childs);
view->childs = NULL;
view->childs_count = 0;
}
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* *
* Description : Réagit à un défilement quelconque. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_scroll(GtkGraphView *view)
{
gtk_fixed_move(GTK_FIXED(view), GTK_WIDGET(view->support),
-GTK_VIEW_PANEL(view)->hadjustment->value,
-GTK_VIEW_PANEL(view)->vadjustment->value);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* lines = informations à intégrer. *
* last = dernière ligne à intégrer ou NULL pour toutes. *
* *
* Description : Définit les lignes du graphique de représentation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_graph_view_set_rendering_lines(GtkGraphView *view, GRenderingLine *lines, GRenderingLine *last)
{
gtk_graph_view_reset(view);
g_signal_emit_by_name(view, "lines-set");
}
/******************************************************************************
* *
* Paramètres : view = 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_view_define_main_address(GtkGraphView *view, vmpa_t addr)
{
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 */
vmpa_t start; /* Début d'une routine */
vmpa_t end; /* Fin d'une routine */
GRenderingLine *first; /* Première ligne à traiter */
GRenderingLine *last; /* Dernière ligne à traiter */
if (!(view->start <= addr && addr < view->end))
{
gtk_graph_view_reset(view);
format = g_openida_binary_get_format(GTK_BIN_VIEW(view)->binary);
routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count);
for (i = 0; i < routines_count; i++)
{
start = g_binary_routine_get_address(routines[i]);
end = start + g_binary_routine_get_size(routines[i]);
if (start <= addr && addr < end)
{
view->start = start;
view->end = end;
first = g_rendering_line_find_by_address(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, start);
last = g_rendering_line_find_by_address(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, end - 1);
view->childs = gtk_graph_view_load_nodes(view, GTK_BIN_VIEW(view)->binary, first, last);
build_graph_view(view, view->childs, view->childs_count);
break;
}
}
}
}
/******************************************************************************
* *
* Paramètres : view = 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] *
* *
* 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_view_get_address_coordinates(const GtkGraphView *view, vmpa_t addr, gint *x, gint *y)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
result = false;
*x = 0;
*y = 0;
for (i = 0; i < view->childs_count && !result; i++)
if (view->childs[i]->get_coordinates(view->childs[i], addr, x, y))
{
*x += GTK_WIDGET(view->childs[i])->allocation.x;
*y += GTK_WIDGET(view->childs[i])->allocation.y;
result = true;
}
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant d'affichage GTK à mettre à jour. *
* binary = contenu binaire à l'origine des lignes. *
* first = première ligne à analyser. *
* last = dernière ligne à analyser. *
* *
* Description : Définit la liste complète des éléments du futur graphique. *
* *
* Retour : Liste d'éléments du graphique à placer. *
* *
* Remarques : - *
* *
******************************************************************************/
static GtkBinView **gtk_graph_view_load_nodes(GtkGraphView *view, GOpenidaBinary *binary, GRenderingLine *first, GRenderingLine *last)
{
GtkBinView **result; /* Liste à retourner */
size_t *count; /* Nombre d'éléments créés. */
GRenderingLine *begin; /* Début d'un groupe de lignes */
GRenderingLine *end; /* Fin d'un groupe de lignes */
GRenderingLine *iter; /* Boucle de parcours */
result = NULL;
view->ready = 0;
count = &view->childs_count;
*count = 0;
/**
* Comme la fonction est appelée depuis un événement et utilise des threads et GTK,
* on doit lever temporairement le verrou GDK.
*/
gdk_flush ();
gdk_threads_leave();
begin = NULL;
for (iter = first; iter != NULL; iter = g_rendering_line_get_next_iter(first, iter, last))
{
if (begin != NULL && g_rendering_line_has_sources(iter))
{
result = (GtkBinView **)realloc(result, ++(*count) * sizeof(GtkBinView *));
result[*count - 1] = GTK_BIN_VIEW(gtk_block_view_new(MRD_GRAPH));
g_signal_connect(result[*count - 1], "lines-set", G_CALLBACK(notify_graph_view), view);
gtk_bin_view_show_border(result[*count - 1], true);
gtk_bin_view_set_rendering_lines(result[*count - 1], binary, begin, end);
begin = NULL;
}
if (begin == NULL) begin = iter;
end = iter;
if (g_rendering_line_has_destinations(iter))
{
result = (GtkBinView **)realloc(result, ++(*count) * sizeof(GtkBinView *));
result[*count - 1] = GTK_BIN_VIEW(gtk_block_view_new(MRD_GRAPH));
g_signal_connect(result[*count - 1], "lines-set", G_CALLBACK(notify_graph_view), view);
gtk_bin_view_show_border(result[*count - 1], true);
gtk_bin_view_set_rendering_lines(result[*count - 1], binary, begin, end);
begin = NULL;
}
}
if (begin != NULL)
{
result = (GtkBinView **)realloc(result, ++(*count) * sizeof(GtkBinView *));
result[*count - 1] = GTK_BIN_VIEW(gtk_block_view_new(MRD_GRAPH));
g_signal_connect(result[*count - 1], "lines-set", G_CALLBACK(notify_graph_view), view);
gtk_bin_view_show_border(result[*count - 1], true);
gtk_bin_view_set_rendering_lines(result[*count - 1], binary, begin, end);
}
/* Attente de la fin de construction */
g_mutex_lock(view->mutex);
while (view->ready < *count)
g_cond_wait(view->cond, view->mutex);
g_mutex_unlock(view->mutex);
gdk_threads_enter();
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant d'affichage prêt à utilisation. *
* parent = composant d'affichage supérieur. *
* *
* Description : Prend note de la fin d'une construction d'une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void notify_graph_view(GtkBinView *view, GtkGraphView *parent)
{
g_mutex_lock(parent->mutex);
parent->ready++;
g_cond_signal(parent->cond);
g_mutex_unlock(parent->mutex);
g_signal_handlers_disconnect_by_func(view, G_CALLBACK(notify_graph_view), parent);
gtk_widget_show(GTK_WIDGET(view));
}
/******************************************************************************
* *
* Paramètres : view = 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_view_put(GtkGraphView *view, GtkWidget *widget, gint x, gint y)
{
gtk_fixed_put(view->support, widget, x, y);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* links = liens graphiques entre les blocs à intégrer. *
* count = quantité de ces liens graphiques. *
* *
* Description : Définit les liens graphiques à présenter avec la vue. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_graph_view_attach_links(GtkGraphView *view, GtkLinkRenderer **links, size_t count)
{
view->links = links;
view->links_count = count;
}