/* OpenIDA - Outil d'analyse de fichiers binaires * gtkblockview.c - affichage d'un fragment de code d'assemblage * * Copyright (C) 2008 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 "gtkblockview.h" #include #include #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API #include #include "gtkbinview-int.h" #include "../analysis/exporter.h" #include "../common/dllist.h" #define MARGIN_SPACE 4 struct _GtkBlockView { GtkBinView parent; /* A laisser en premier */ MainRendering rendering; /* Support final des lignes */ bool show_vaddress; /* Affichage des adresses ? */ bool show_code; /* Affichage du code brut ? */ GtkTextBuffer *buffer; /* Code sous forme de texte */ GtkTextLayout *_layout; /* Disposition du texte */ PangoLayout *layout; /* Moteur de rendu du code ASM */ int line_height; /* Hauteur maximale des lignes */ const exe_format *format; /* Format du contenu bianire */ }; struct _GtkBlockViewClass { GtkBinViewClass parent; /* A laisser en premier */ }; static void gtk_text_view2_set_attributes_from_style (GtkTextAttributes *values, GtkStyle *style) { PangoFontDescription *font_desc; values->appearance.bg_color = style->base[GTK_STATE_NORMAL]; values->appearance.fg_color = style->text[GTK_STATE_NORMAL]; if (values->font) pango_font_description_free (values->font); font_desc = pango_font_description_from_string ("mono 10"); values->font = pango_font_description_copy (/*style->*/font_desc); pango_font_description_free (font_desc); } /* Procède à l'initialisation de l'afficheur d'un bloc binaire. */ static void gtk_block_view_init(GtkBlockView *); /* Encadre la construction graphique initiale de la visualisation. */ static void gtk_block_view_realize(GtkWidget *); /* Définit les lignes du bloc de représentation. */ static void gtk_block_view_set_rendering_lines(GtkBlockView *, GRenderingLine *, GRenderingLine *); /* Redessine l'affichage suite une mise à jour dans la marge. */ void gtk_block_view_update_margin(GRenderingLine *, GtkBlockView *); /* Réclame une nouvelle taille adaptée au contenu présent. */ void gtk_block_view_recompute_size_request(GtkBlockView *); /* Indique la position d'affichage d'une adresse donnée. */ static bool gtk_block_view_get_address_coordinates(const GtkBlockView *, vmpa_t, gint *, gint *); static void gtk_block_view_class_init(GtkBlockViewClass *klass); static void gtk_block_view_init(GtkBlockView *cpu); static void gtk_block_view_size_request(GtkWidget *widget, GtkRequisition *requisition); static void gtk_block_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation); static gboolean gtk_block_view_button_press(GtkWidget *, GdkEventButton *event); /* Met à jour l'affichage de la vue sous forme de bloc. */ static gboolean gtk_block_view_expose(GtkWidget *, GdkEventExpose *); static void gtk_block_view_destroy(GtkObject *object); /* Détermine le type du composant d'affichage en block. */ G_DEFINE_TYPE(GtkBlockView, gtk_block_view, GTK_TYPE_BIN_VIEW) /****************************************************************************** * * * Paramètres : rendering = support effectif des lignes au final. * * * * Description : Crée un nouveau composant pour l'affichage en block. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget * gtk_block_view_new(MainRendering rendering) { GtkBlockView *result; result = gtk_type_new(gtk_block_view_get_type()); result->rendering = rendering; return GTK_WIDGET(result); } static void gtk_block_view_class_init(GtkBlockViewClass *klass) { GtkWidgetClass *widget_class; /* Classe version Widget */ GtkObjectClass *object_class; widget_class = (GtkWidgetClass *) klass; object_class = (GtkObjectClass *) klass; widget_class->button_press_event = gtk_block_view_button_press; widget_class->realize = gtk_block_view_realize; widget_class->size_request = gtk_block_view_size_request; widget_class->size_allocate = gtk_block_view_size_allocate; widget_class->expose_event = gtk_block_view_expose; object_class->destroy = gtk_block_view_destroy; } /****************************************************************************** * * * Paramètres : view = composant GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur d'un bloc binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_block_view_init(GtkBlockView *view) { GtkBinView *binview; /* Instance parente */ PangoTabArray *tabs; gint current_y; PangoFontDescription *font_desc; GList *child_exposes; PangoContext *ltr_context, *rtl_context; GtkTextAttributes *style; static bool done = false; binview = GTK_BIN_VIEW(view); binview->set_lines = (set_rendering_lines_fc)gtk_block_view_set_rendering_lines; binview->get_coordinates = (get_addr_coordinates_fc)gtk_block_view_get_address_coordinates; view->buffer = gtk_text_buffer_new(get_gtk_tag_table()); view->_layout = gtk_text_layout_new(); gtk_text_layout_set_buffer(view->_layout, view->buffer); if (!done || 1) { done = true; gtk_text_layout_set_cursor_visible(GTK_BLOCK_VIEW(view)->_layout, FALSE); gtk_text_layout_set_overwrite_mode(GTK_BLOCK_VIEW(view)->_layout, FALSE); ltr_context = gtk_widget_create_pango_context(view); pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR); rtl_context = gtk_widget_create_pango_context(view); pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL); gtk_text_layout_set_contexts(GTK_BLOCK_VIEW(view)->_layout, ltr_context, rtl_context); tabs = pango_tab_array_new_with_positions(5, TRUE, PANGO_TAB_LEFT, 120, PANGO_TAB_LEFT, 290, PANGO_TAB_LEFT, 50, PANGO_TAB_LEFT, 50, PANGO_TAB_LEFT, 50); style = gtk_text_attributes_new (); gtk_widget_ensure_style(view); font_desc = pango_font_description_from_string ("mono 10"); gtk_widget_modify_font (view, font_desc); pango_font_description_free (font_desc); gtk_text_view2_set_attributes_from_style(style, GTK_WIDGET(view)->style); /* style->pixels_above_lines = 5; style->pixels_below_lines = 5; style->pixels_inside_wrap = 10; style->left_margin = 10; style->right_margin = 10; style->indent = 10;*/ style->left_margin = 10; style->right_margin = 10; style->tabs = tabs; style->wrap_mode = GTK_WRAP_WORD; style->justification = GTK_JUSTIFY_LEFT; style->direction = gtk_widget_get_direction(view); gtk_text_layout_set_default_style(GTK_BLOCK_VIEW(view)->_layout, style); gtk_text_attributes_unref(style); //gtk_text_layout_get_size (GTK_BLOCK_VIEW(view)->_layout, &width, &height); gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(view)->_layout, MAX (1, 1000)); } gtk_text_buffer_set_text(view->buffer, "\t|\t|\t|\t|\t|\n", -1); } static void gtk_block_view_size_request(GtkWidget *widget, GtkRequisition *requisition) { g_return_if_fail(widget != NULL); g_return_if_fail(GTK_IS_BLOCK_VIEW(widget)); g_return_if_fail(requisition != NULL); requisition->width = 80; requisition->height = 100; } static void gtk_block_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { g_return_if_fail(widget != NULL); g_return_if_fail(GTK_IS_BLOCK_VIEW(widget)); g_return_if_fail(allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED(widget)) { gdk_window_move_resize( widget->window, allocation->x, allocation->y, allocation->width, allocation->height ); } } /****************************************************************************** * * * Paramètres : widget = composant GTK à redessiner. * * * * Description : Encadre la construction graphique initiale de la visualisat°.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_block_view_realize(GtkWidget *widget) { GtkBinViewClass *parent_class; /* Version pure du parent */ PangoFontDescription *font_desc; parent_class = GTK_BIN_VIEW_CLASS(g_type_class_peek_parent(GTK_BLOCK_VIEW_GET_CLASS(widget))); GTK_WIDGET_CLASS(parent_class)->realize(widget); GTK_BLOCK_VIEW(widget)->layout = gtk_widget_create_pango_layout(widget, NULL); font_desc = pango_font_description_from_string("mono 10"); gtk_widget_modify_font(widget, font_desc); pango_font_description_free(font_desc); } static gboolean gtk_block_view_button_press(GtkWidget *widget, GdkEventButton *event) { gboolean result; /* Décision à retourner */ GtkBlockView *view; /* Composant GTK réel */ int y; /* Position à manipuler */ GRenderingLine *line; /* Ligne de rendu visée */ result = FALSE; view = GTK_BLOCK_VIEW(widget); y = event->y; line = g_rendering_line_find_by_y(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, &y); if (line != NULL) { /* Clic dans la marge */ if (event->type == GDK_BUTTON_PRESS && event->x < (2 * MARGIN_SPACE + view->line_height)) { result = TRUE; g_rendering_line_toggle_flag(line, RLF_BREAK_POINT); } } return result; } /****************************************************************************** * * * Paramètres : line = ligne dont un drapeau a évolué. * * view = composant GTK à mettre à jour. * * * * Description : Redessine l'affichage suite une mise à jour dans la marge. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_block_view_update_margin(GRenderingLine *line, GtkBlockView *view) { /* TODO : à améliorer ! */ gtk_widget_queue_draw(GTK_WIDGET(view)); } /****************************************************************************** * * * Paramètres : view = composant GTK à redessiner. * * event = informations liées à l'événement. * * * * Description : Met à jour l'affichage de la vue sous forme de bloc. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_block_view_expose(GtkWidget *widget, GdkEventExpose *event) { GtkBinView *view; /* Autre version du composant */ GdkGCValues values; /* Propriétés du contexte */ GdkColor white; /* Couleur du fond */ int width; /* Largeur de l'élément */ int height; /* Hauteur de l'élément */ int y; /* Ordonnée du haut d'une ligne*/ GRenderingLine *iter; /* Boucle de parcours */ PangoFontDescription *font_desc; GList *child_exposes; view = GTK_BIN_VIEW(widget); #if 0 gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region); gdk_gc_set_clip_region(view->gc, event->region); gdk_gc_get_values(view->gc, &values); gdk_color_white(gtk_widget_get_colormap(widget), &white); gdk_gc_set_foreground(view->gc, &white); gtk_widget_get_size_request(widget, &width, &height); gdk_draw_rectangle(GDK_DRAWABLE(widget->window), view->gc, TRUE, 0, 0, width, height); gdk_gc_set_foreground(view->gc, &values.foreground); /* y = event->area.y; iter = g_rendering_line_find_by_y(view->lines, view->last, &y); y = event->area.y - y; for ( ; iter != NULL && y < (event->area.y + event->area.height); iter = g_rendering_line_get_next_iter(view->lines, iter, view->last)) { g_rendering_line_draw(iter, GDK_DRAWABLE(widget->window), view->gc, MARGIN_SPACE, 2 * MARGIN_SPACE + GTK_BLOCK_VIEW(view)->line_height, y, GTK_BLOCK_VIEW(view)->line_height, GTK_BLOCK_VIEW(view)->rendering); y += GTK_BLOCK_VIEW(view)->line_height; } */ gdk_window_end_paint(GDK_DRAWABLE(widget->window)); #endif gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(view)->_layout, MAX (1, 1000)); //gtk_text_buffer_set_text(GTK_BLOCK_VIEW(view)->buffer, "Hello, this some text\n", -1); font_desc = pango_font_description_from_string ("mono 10"); gtk_widget_modify_font (view, font_desc); pango_font_description_free (font_desc); gtk_text_layout_draw(GTK_BLOCK_VIEW(view)->_layout, widget, GDK_DRAWABLE(widget->window), NULL, 0, 0, 0, 0, 5000, 50000, &child_exposes); return TRUE; } static void gtk_block_view_destroy(GtkObject *object) { GtkBlockView *cpu; GtkBlockViewClass *klass; g_return_if_fail(object != NULL); g_return_if_fail(GTK_IS_BLOCK_VIEW(object)); cpu = GTK_BLOCK_VIEW(object); klass = gtk_type_class(gtk_widget_get_type()); if (GTK_OBJECT_CLASS(klass)->destroy) { (* GTK_OBJECT_CLASS(klass)->destroy) (object); } } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * show = état de l'affichage auquel parvenir. * * * * Description : Choisit d'afficher les adresses virtuelles ou non. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_block_view_show_vaddress(GtkBlockView *view, gboolean show) { view->show_vaddress = show; //gtk_block_view_build_content(view); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * show = état de l'affichage auquel parvenir. * * * * Description : Choisit d'afficher le code brut ou non. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_block_view_show_code(GtkBlockView *view, gboolean show) { view->show_code = show; //gtk_block_view_build_content(view); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * format = format du binaire affiché. * * * * Description : Définit le format auquel le contenu est lié. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_block_view_set_format(GtkBlockView *view, const exe_format *format) { view->format = format; } /****************************************************************************** * * * 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 bloc de représentation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_block_view_set_rendering_lines(GtkBlockView *view, GRenderingLine *lines, GRenderingLine *last) { GRenderingLine *iter; /* Boucle de parcours */ GtkTextIter pos; /* Point d'insertion */ for (iter = GTK_BIN_VIEW(view)->lines; iter != NULL; iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last)) { g_signal_connect(iter, "rendering-line-flags-changed", G_CALLBACK(gtk_block_view_update_margin), view); } g_rendering_line_update_bin_len(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, view->rendering); for (iter = GTK_BIN_VIEW(view)->lines; iter != NULL; iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last)) { if (iter != GTK_BIN_VIEW(view)->lines) { gtk_text_buffer_get_end_iter(view->buffer, &pos); gtk_text_buffer_insert_with_tags(view->buffer, &pos, "\n", 1, NULL); } gtk_text_buffer_get_end_iter(view->buffer, &pos); g_content_exporter_add_to_gtk_buffer(G_CONTENT_EXPORTER(iter), view->rendering, view->buffer, &pos); } gtk_text_layout_validate(GTK_BLOCK_VIEW(view)->_layout, 100000); gtk_block_view_recompute_size_request(view); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * * * Description : Réclame une nouvelle taille adaptée au contenu présent. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_block_view_recompute_size_request(GtkBlockView *view) { int width; /* Largeur de l'objet actuelle */ int height; /* Hauteur de l'objet actuelle */ gtk_text_layout_get_size(view->_layout, &width, &height); gtk_widget_set_size_request(GTK_WIDGET(view), width + 2 * MARGIN_SPACE + 20/*view->line_height*/, height); printf("req size :: (%d ; %d)\n", width, height); /* g_rendering_line_get_size(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, view->rendering, &width, &height, &view->line_height); gtk_widget_set_size_request(GTK_WIDGET(view), width + 2 * MARGIN_SPACE + view->line_height, height); */ } /****************************************************************************** * * * 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_block_view_get_address_coordinates(const GtkBlockView *view, vmpa_t addr, gint *x, gint *y) { bool result; /* Bilan à retourner */ GRenderingLine *iter; /* Boucle de parcours */ result = false; *x = 0; *y = 0; for (iter = GTK_BIN_VIEW(view)->lines; iter != NULL && !result; iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last)) { if (get_rendering_line_address(iter) == addr) result = true; else *y += view->line_height; } return result; }