/* 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 "support.h" #include "../analysis/exporter.h" #include "../common/dllist.h" #define MARGIN_SPACE 4 /* TODO : delete me ! */ 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 */ gint line_height; /* Hauteur maximale des lignes */ gint left_margin; /* Marge gauche + espace */ gint left_text; /* Début d'impression du code */ const exe_format *format; /* Format du contenu bianire */ }; struct _GtkBlockViewClass { GtkBinViewClass parent; /* A laisser en premier */ GdkPixbuf *entry_pix; /* Image du point d'entrée */ GdkPixbuf *breakpoint_pix; /* Image de point d'arrêt */ GdkPixbuf *stopped_pix; /* Image de point actif */ }; 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; gchar *filename; /* Fichier d'image à charger */ 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; /* Image de la marge gauche */ filename = find_pixmap_file("entry.png"); if (filename == NULL) klass->entry_pix = NULL; else { klass->entry_pix = gdk_pixbuf_new_from_file(filename, NULL); g_free(filename); } filename = find_pixmap_file("breakpoint.png"); if (filename == NULL) klass->breakpoint_pix = NULL; else { klass->breakpoint_pix = gdk_pixbuf_new_from_file(filename, NULL); g_free(filename); } filename = find_pixmap_file("stopped.png"); if (filename == NULL) klass->stopped_pix = NULL; else { klass->stopped_pix = gdk_pixbuf_new_from_file(filename, NULL); g_free(filename); } } /****************************************************************************** * * * 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 */ PangoFontDescription *font_desc; 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); 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->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)); } } 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); 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 */ GtkTextIter iter; /* Point d'insertion */ GtkTextMark *mark; /* Marquage de ligne associée */ GRenderingLine *line; /* Ligne de rendu */ result = FALSE; view = GTK_BLOCK_VIEW(widget); gtk_text_layout_get_line_at_y(view->layout, &iter, event->y, NULL); mark = gtk_text_iter_get_marks(&iter)->data; line = g_object_get_data(G_OBJECT(mark), "line"); /* Clic dans la marge */ if (event->type == GDK_BUTTON_PRESS && event->x < view->left_margin) { 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) { GtkBlockView *view; //GtkWidget *widget; /* Autre version du composant */ GtkBinView *bview; /* Autre version du composant */ GtkStyle *style; /* Style associé au composant */ GtkBinViewClass *parent_class; /* Version pure du parent */ GtkTextIter iter; /* Point d'insertion */ GdkRectangle rect; /* Zone d'un point */ GtkTextMark *mark; /* Marquage de ligne associée */ GRenderingLine *line; /* Ligne de rendu */ RenderingLineFlag flags; /* Propriétés de la ligne */ GdkPixbuf *pixbuf; /* Données utiles au dessin */ //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 */ GList *child_exposes; view = GTK_BLOCK_VIEW(widget); widget = GTK_WIDGET(view); bview = GTK_BIN_VIEW(widget); gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region); gdk_gc_set_clip_region(bview->gc, event->region); style = gtk_widget_get_style(GTK_WIDGET(view)); //gtk_widget_get_size_request(widget, &width, &height); /* Dessin de la marge gauche */ gdk_gc_set_foreground(bview->gc, &style->mid[GTK_WIDGET_STATE(widget)]); gdk_draw_rectangle(GDK_DRAWABLE(widget->window), bview->gc, TRUE, 0, event->area.y, view->left_margin, event->area.y + event->area.height); gdk_gc_set_foreground(bview->gc, &style->dark[GTK_WIDGET_STATE(widget)]); gdk_draw_line(GDK_DRAWABLE(widget->window), bview->gc, view->left_margin, event->area.y, view->left_margin, event->area.y + event->area.height); /* Eventuelle bordure globale */ parent_class = GTK_BIN_VIEW_CLASS(g_type_class_peek_parent(GTK_BLOCK_VIEW_GET_CLASS(view))); GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event); /* Informations individuelles des lignes */ gtk_text_layout_get_line_at_y(view->layout, &iter, event->area.y, NULL); gtk_text_layout_get_iter_location(view->layout, &iter, &rect); while (rect.y < (event->area.y + event->area.height)) { mark = gtk_text_iter_get_marks(&iter)->data; line = g_object_get_data(G_OBJECT(mark), "line"); flags = g_rendering_line_get_flags(line); if (flags & RLF_RUNNING_BP) pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->stopped_pix; else if (flags & RLF_BREAK_POINT) pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->breakpoint_pix; else pixbuf = NULL; if (pixbuf != NULL) gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), bview->gc, pixbuf, 0, 0, 10, rect.y, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), GDK_RGB_DITHER_NORMAL, 0, 0); /* Le point d'entrée prime */ if (flags & RLF_ENTRY_POINT) { pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->entry_pix; gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), bview->gc, pixbuf, 0, 0, 10, rect.y, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), GDK_RGB_DITHER_NORMAL, 0, 0); } if (!gtk_text_layout_move_iter_to_next_line(view->layout, &iter)) break; gtk_text_layout_get_iter_location(view->layout, &iter, &rect); } /* Impression du désassemblage */ #if 0 gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region); gdk_gc_set_clip_region(bview->gc, event->region); gdk_gc_get_values(bview->gc, &values); gdk_color_white(gtk_widget_get_colormap(widget), &white); gdk_gc_set_foreground(bview->gc, &white); gtk_widget_get_size_request(widget, &width, &height); gdk_draw_rectangle(GDK_DRAWABLE(widget->window), bview->gc, TRUE, 0, 0, width, height); gdk_gc_set_foreground(bview->gc, &values.foreground); /* y = event->area.y; iter = g_rendering_line_find_by_y(bview->lines, bview->last, &y); y = event->area.y - y; for ( ; iter != NULL && y < (event->area.y + event->area.height); iter = g_rendering_line_get_next_iter(bview->lines, iter, bview->last)) { g_rendering_line_draw(iter, GDK_DRAWABLE(widget->window), bview->gc, MARGIN_SPACE, 2 * MARGIN_SPACE + GTK_BLOCK_VIEW(bview)->line_height, y, GTK_BLOCK_VIEW(bview)->line_height, GTK_BLOCK_VIEW(bview)->rendering); y += GTK_BLOCK_VIEW(bview)->line_height; } */ gdk_window_end_paint(GDK_DRAWABLE(widget->window)); #endif gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(bview)->layout, MAX (1, 1000)); gtk_text_layout_draw(GTK_BLOCK_VIEW(bview)->layout, widget, GDK_DRAWABLE(widget->window), NULL, view->left_text, 0, 0, 0, 5000, 50000, &child_exposes); gdk_window_end_paint(GDK_DRAWABLE(widget->window)); 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) { size_t lengths[SAR_COUNT]; /* Différentes tailles de zone */ GRenderingLine *iter; /* Boucle de parcours */ GtkTextIter pos; /* Point d'insertion */ GtkTextMark *mark; /* Marquage de ligne associée */ PangoTabArray *tabs; /* Tailles de tabulation */ GdkRectangle rect; /* Zone d'un point */ lengths[SAR_ADDRESS] = 0; lengths[SAR_CODE] = 0; lengths[SAR_INSTRUCTION] = 0; 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); 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); mark = gtk_text_buffer_create_mark(view->buffer, NULL, &pos, TRUE); g_object_set_data(G_OBJECT(mark), "line", iter); g_content_exporter_add_to_gtk_buffer(G_CONTENT_EXPORTER(iter), view->rendering, view->buffer, &pos, lengths); } gtk_text_layout_move_iter_visually(view->layout, &pos, -1); gtk_text_layout_get_iter_location(view->layout, &pos, &rect); /* Taille des tabulations */ if (lengths[SAR_ADDRESS] > 0 && lengths[SAR_CODE] > 0) tabs = pango_tab_array_new_with_positions(3, TRUE, PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3), PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_CODE] + 3), PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_CODE] + 3 + lengths[SAR_INSTRUCTION] + 4)); else if (lengths[SAR_ADDRESS] > 0 && lengths[SAR_CODE] == 0) tabs = pango_tab_array_new_with_positions(2, TRUE, PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3), PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_INSTRUCTION] + 4)); else if (lengths[SAR_ADDRESS] == 0 && lengths[SAR_CODE] > 0) tabs = pango_tab_array_new_with_positions(2, TRUE, PANGO_TAB_LEFT, rect.width * (lengths[SAR_CODE] + 3), PANGO_TAB_LEFT, rect.width * (lengths[SAR_CODE] + 3 + lengths[SAR_INSTRUCTION] + 4)); else tabs = pango_tab_array_new_with_positions(1, TRUE, PANGO_TAB_LEFT, rect.width * (lengths[SAR_INSTRUCTION] + 4)); if (view->layout->default_style->tabs) pango_tab_array_free(view->layout->default_style->tabs); view->layout->default_style->tabs = tabs; gtk_text_layout_default_style_changed(view->layout); /* Taille des marges */ view->line_height = rect.height; view->left_margin = 2 * rect.height; view->left_text = -2.5 * rect.height; /* Validation finale */ 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) { gint width; /* Largeur de l'objet actuelle */ gint height; /* Hauteur de l'objet actuelle */ gtk_text_layout_get_size(view->layout, &width, &height); width += -view->left_text + 1; height += 1; gtk_widget_set_size_request(GTK_WIDGET(view), width, 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; }