From b69c48df5ae7b42e5d5ef6f1720727f6693efee3 Mon Sep 17 00:00:00 2001 From: Jakub Zawadzki Date: Sun, 15 Jul 2012 20:24:48 +0000 Subject: [PATCH] Fix bug #6690: Wireshark quite slow displaying frames with many detail pane nodes and large byteviews Add custom widget to render hexdump or bitsdump. svn path=/trunk/; revision=43728 --- ui/gtk/CMakeLists.txt | 1 + ui/gtk/Makefile.common | 2 + ui/gtk/bytes_view.c | 1156 +++++++++++++++++++++++++++++++++++++++ ui/gtk/bytes_view.h | 26 + ui/gtk/old-gtk-compat.h | 3 + ui/gtk/packet_panes.c | 752 +------------------------ 6 files changed, 1205 insertions(+), 735 deletions(-) create mode 100644 ui/gtk/bytes_view.c create mode 100644 ui/gtk/bytes_view.h diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt index 48e7d42a33..efc0bb4130 100644 --- a/ui/gtk/CMakeLists.txt +++ b/ui/gtk/CMakeLists.txt @@ -26,6 +26,7 @@ set(WIRESHARK_GTK_SRC about_dlg.c airpcap_dlg.c airpcap_gui_utils.c + bytes_view.c capture_dlg.c capture_file_dlg.c capture_if_dlg.c diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common index ed70d9fdfb..09f906ee16 100644 --- a/ui/gtk/Makefile.common +++ b/ui/gtk/Makefile.common @@ -51,6 +51,7 @@ WIRESHARK_GTK_SRC = \ about_dlg.c \ airpcap_dlg.c \ airpcap_gui_utils.c \ + bytes_view.c \ capture_dlg.c \ capture_file_dlg.c \ capture_if_dlg.c \ @@ -241,6 +242,7 @@ noinst_HEADERS = \ about_dlg.h \ airpcap_dlg.h \ airpcap_gui_utils.h \ + bytes_view.h \ capture_comment_icons.h \ capture_dlg.h \ capture_file_dlg.h \ diff --git a/ui/gtk/bytes_view.c b/ui/gtk/bytes_view.c new file mode 100644 index 0000000000..816fac6aa6 --- /dev/null +++ b/ui/gtk/bytes_view.c @@ -0,0 +1,1156 @@ +/* bytes_view.c + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +/* Code based on: + * xtext, the text widget used by X-Chat. Peter Zelezny . + * GtkTextView. Copyright (C) 2000 Red Hat, Inc. + * GtkHex. Jaka Mocnik . + * pango-layout.c: High-level layout driver. Copyright (C) 2000, 2001, 2006 Red Hat Software + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#undef GTK_DISABLE_DEPRECATED +#undef GSEAL_ENABLE + +#include +#include "ui/gtk/old-gtk-compat.h" + +#include + +#include "../isprint.h" + +#include +#include +#include + +#include "ui/recent.h" + +#include "packet_panes.h" + +#define MARGIN 2 +#define REFRESH_TIMEOUT 10 + +static GtkWidgetClass *parent_class = NULL; + +struct _BytesView +{ + GtkWidget widget; + + PangoContext *context; + + PangoFontDescription *font; + int font_ascent; + int font_descent; + int fontsize; + + gint adj_tag; + GtkAdjustment *vadj; + GtkAdjustment *hadj; + int max_width; + + gboolean bold_highlight; + int state; + +/* data */ + int encoding; + int format; + guint8 *pd; + int len; +/* data-highlight */ + int start[2]; + int end[2]; + + int per_line; + int use_digits; +}; + +#include "bytes_view.h" + +typedef struct _BytesViewClass +{ + GtkWidgetClass parent_class; + + void (*set_scroll_adjustments)(BytesView *, GtkAdjustment *, GtkAdjustment *); + +} BytesViewClass; + +static void bytes_view_set_scroll_adjustments(BytesView *, GtkAdjustment *, GtkAdjustment *); +static void bytes_view_adjustment_set(BytesView *); + +static void +bytes_view_init(BytesView *bv) +{ + bv->context = NULL; + + bv->encoding = PACKET_CHAR_ENC_CHAR_ASCII; + bv->format = BYTES_HEX; + + bv->per_line = 16; + bv->use_digits = 4; + + bv->max_width = 0; +} + +static void +bytes_view_destroy(GtkObject *object) +{ + BytesView *bv = BYTES_VIEW(object); + + if (bv->pd) { + g_free(bv->pd); + bv->pd = NULL; + } + if (bv->adj_tag) { + g_source_remove(bv->adj_tag); + bv->adj_tag = 0; + } + if (bv->vadj) { + g_object_unref(G_OBJECT(bv->vadj)); + bv->vadj = NULL; + } + if (bv->hadj) { + g_object_unref(G_OBJECT(bv->hadj)); + bv->hadj = NULL; + } + if (bv->font) { + pango_font_description_free(bv->font); + bv->font = NULL; + } + if (bv->context) { + g_object_unref(bv->context); + bv->context = NULL; + } + if (GTK_OBJECT_CLASS(parent_class)->destroy) + (*GTK_OBJECT_CLASS(parent_class)->destroy)(object); +} + +static void +bytes_view_ensure_layout(BytesView *bv) +{ + if (bv->context == NULL) { + bv->context = gtk_widget_get_pango_context(GTK_WIDGET(bv)); + g_object_ref(bv->context); + + { + PangoLanguage *lang; + PangoFontMetrics *metrics; + + /* vte and xchat does it this way */ + lang = pango_context_get_language(bv->context); + metrics = pango_context_get_metrics(bv->context, bv->font, lang); + bv->font_ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + bv->font_descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE; + pango_font_metrics_unref(metrics); + + bv->fontsize = bv->font_ascent + bv->font_descent; + } + g_assert(bv->context); + bytes_view_adjustment_set(bv); + } +} + +static void +bytes_view_realize(GtkWidget *widget) +{ + BytesView *bv; + GdkWindowAttr attributes; + + _gtk_widget_set_realized_true(widget); + bv = BYTES_VIEW(widget); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.colormap = gtk_widget_get_colormap(widget); + + attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK; + + widget->window = gdk_window_new(widget->parent->window, &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); + + gdk_window_set_user_data(widget->window, widget); + + gdk_window_set_back_pixmap(widget->window, NULL, FALSE); + widget->style = gtk_style_attach(widget->style, widget->window); + bytes_view_ensure_layout(bv); +} + +static void +bytes_view_unrealize(GtkWidget *widget) +{ + BytesView *bv = BYTES_VIEW(widget); + + if (bv->context) { + g_object_unref(bv->context); + bv->context = NULL; + } + /* if there are still events in the queue, this'll avoid segfault */ + gdk_window_set_user_data(widget->window, NULL); + + if (parent_class->unrealize) + (*GTK_WIDGET_CLASS(parent_class)->unrealize)(widget); +} + +static GtkAdjustment * +bytes_view_ensure_vadj(BytesView *bv) +{ + if (bv->vadj == NULL) { + bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj); + g_assert(bv->vadj != NULL); + } + return bv->vadj; +} + +static GtkAdjustment * +bytes_view_ensure_hadj(BytesView *bv) +{ + if (bv->hadj == NULL) { + bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj); + g_assert(bv->hadj != NULL); + } + return bv->hadj; +} + +static gboolean +bytes_view_scroll(GtkWidget *widget, GdkEventScroll *event) +{ + BytesView *bv = BYTES_VIEW(widget); + + gfloat new_value; + + if (event->direction == GDK_SCROLL_UP) { /* mouse wheel pageUp */ + bytes_view_ensure_vadj(bv); + + new_value = bv->vadj->value - (bv->vadj->page_increment / 10); + if (new_value < bv->vadj->lower) + new_value = bv->vadj->lower; + gtk_adjustment_set_value(bv->vadj, new_value); + + } else if (event->direction == GDK_SCROLL_DOWN) { /* mouse wheel pageDn */ + bytes_view_ensure_vadj(bv); + + new_value = bv->vadj->value + (bv->vadj->page_increment / 10); + if (new_value > (bv->vadj->upper - bv->vadj->page_size)) + new_value = bv->vadj->upper - bv->vadj->page_size; + gtk_adjustment_set_value(bv->vadj, new_value); + } + return FALSE; +} + +static void +bytes_view_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + widget->allocation = *allocation; + if (gtk_widget_get_realized(widget)) { + BytesView *bv = BYTES_VIEW(widget); + + gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height); + bytes_view_adjustment_set(bv); + } +} + +static void +bytes_view_size_request(GtkWidget *widget _U_, GtkRequisition *requisition) +{ + requisition->width = 200; + requisition->height = 90; +} + +static GSList * +_pango_runs_build(BytesView *bv, const char *str, int len) +{ + GSList *runs = NULL; + + PangoAttrList *attrs; + PangoAttrIterator *iter; + + GList *run_list; + GList *tmp_list; + + attrs = pango_attr_list_new(); + pango_attr_list_insert_before(attrs, pango_attr_font_desc_new(bv->font)); + + iter = pango_attr_list_get_iterator(attrs); + + run_list = pango_itemize(bv->context, str, 0, len, attrs, iter); + + for (tmp_list = run_list; tmp_list; tmp_list = tmp_list->next) { + PangoLayoutRun *run = g_slice_new(PangoLayoutRun); + PangoItem *run_item = tmp_list->data; + + run->item = run_item; + + /* XXX pango_layout_get_item_properties(run_item, &state->properties); */ + + run->glyphs = pango_glyph_string_new(); + pango_shape(str + run_item->offset, run_item->length, &run_item->analysis, run->glyphs); + + runs = g_slist_prepend(runs, run); + } + + g_list_free(run_list); + + pango_attr_iterator_destroy(iter); + pango_attr_list_unref(attrs); + + return g_slist_reverse(runs); +} + +static int +_pango_glyph_string_to_pixels(PangoGlyphString *glyphs, PangoFont *font _U_) +{ +#if PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 14 + return pango_glyph_string_get_width(glyphs) / PANGO_SCALE; +#else + PangoRectangle logical_rect; + + pango_glyph_string_extents(glyphs, font, NULL, &logical_rect); + /* pango_extents_to_pixels(&logical_rect, NULL); */ + + return (logical_rect.width / PANGO_SCALE); +#endif +} + +static int +xtext_draw_layout_line(cairo_t *cr, gint x, gint y, GSList *runs) +{ + while (runs) { + PangoLayoutRun *run = runs->data; + + cairo_move_to(cr, x, y); + pango_cairo_show_glyph_string(cr, run->item->analysis.font, run->glyphs); + + x += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font); + runs = runs->next; + } + return x; +} + +static int +_pango_runs_width(GSList *runs) +{ + int width = 0; + + while (runs) { + PangoLayoutRun *run = runs->data; + + width += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font); + runs = runs->next; + } + return width; +} + +static void +_pango_runs_free(GSList *runs) +{ + GSList *list = runs; + + while (list) { + PangoLayoutRun *run = list->data; + + pango_item_free(run->item); + pango_glyph_string_free(run->glyphs); + g_slice_free(PangoLayoutRun, run); + + list = list->next; + } + g_slist_free(runs); +} + +typedef int bytes_view_line_cb(BytesView *, void *data, int x, int arg1, const char *str, int len); + +static int +bytes_view_flush_render(BytesView *bv, void *data, int x, int y, const char *str, int len) +{ + cairo_t *cr = data; + GSList *line_runs; + int str_width; + + if (len < 1) + return 0; + + line_runs = _pango_runs_build(bv, str, len); + + /* XXX, cliping */ + + if (bv->state != GTK_STATE_NORMAL) { + str_width = _pango_runs_width(line_runs); + + /* background */ + gdk_cairo_set_source_color(cr, >k_widget_get_style(GTK_WIDGET(bv))->base[bv->state]); + cairo_rectangle(cr, x, y - bv->font_ascent, str_width, bv->fontsize); + cairo_fill(cr); + } + + /* text */ + gdk_cairo_set_source_color(cr, >k_widget_get_style(GTK_WIDGET(bv))->text[bv->state]); + str_width = xtext_draw_layout_line(cr, x, y, line_runs)-x; + + _pango_runs_free(line_runs); + + return str_width; +} + +static int +_pango_runs_find_index(GSList *runs, int x_pos, const char *str) +{ + int start_pos = 0; + + while (runs) { + PangoLayoutRun *run = runs->data; + int width; + + width = _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font); + + if (x_pos >= start_pos && x_pos < start_pos + width) { + gboolean char_trailing; + int pos; + + pango_glyph_string_x_to_index(run->glyphs, + (char *) str + run->item->offset, run->item->length, + &run->item->analysis, + (x_pos - start_pos) * PANGO_SCALE, + &pos, &char_trailing); + + return run->item->offset + pos; + } + + start_pos += width; + runs = runs->next; + } + return -1; +} + +static int +bytes_view_flush_pos(BytesView *bv, void *data, int x, int search_x, const char *str, int len) +{ + int *pos_x = data; + GSList *line_runs; + int line_width; + + if (len < 1) + return 0; + + line_runs = _pango_runs_build(bv, str, len); + + line_width = _pango_runs_width(line_runs); + + if (x <= search_x && x + line_width > search_x) { + int off_x = search_x - x; + int pos_run; + + if ((pos_run = _pango_runs_find_index(line_runs, off_x, str)) != -1) + *pos_x = (-*pos_x) + pos_run; + + return -1; /* terminate */ + } else + *pos_x -= len; + + _pango_runs_free(line_runs); + + return line_width; +} + +static void +bytes_view_render_state(BytesView *bv, int state) +{ + g_assert(state == GTK_STATE_NORMAL || state == GTK_STATE_SELECTED); + + if (bv->bold_highlight) { + pango_font_description_set_weight(bv->font, + (state == GTK_STATE_SELECTED) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + bv->state = GTK_STATE_NORMAL; + } else + bv->state = state; +} + +#define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */ + +static void +_bytes_view_line_common(BytesView *bv, void *data, const int org_off, int xx, int arg1, bytes_view_line_cb flush) +{ + static const guchar hexchars[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + const guint8 *pd = bv->pd; + const int len = bv->len; + + int state; + + char str[128]; + int cur = 0; + + int off; + guchar c; + int byten; + int j; + + int scroll_x; + int dx; + + g_assert(org_off >= 0); + + scroll_x = bytes_view_ensure_hadj(bv)->value; + + state = GTK_STATE_NORMAL; + bytes_view_render_state(bv, GTK_STATE_NORMAL); + + /* Print the line number */ + j = bv->use_digits; + do { + j--; + c = (org_off >> (j*4)) & 0xF; + str[cur++] = hexchars[c]; + } while (j != 0); + str[cur++] = ' '; + str[cur++] = ' '; + + /* Print the hex bit */ + for (byten = 0, off = org_off; byten < bv->per_line; byten++) { + gboolean byte_highlighted = + (off >= bv->start[0] && off < bv->end[0]) || + (off >= bv->start[1] && off < bv->end[1]); + int state_cur = (off < len && byte_highlighted) ? + GTK_STATE_SELECTED : GTK_STATE_NORMAL; + + if (state_cur != state) { + if (state == GTK_STATE_NORMAL && byten) { + str[cur++] = ' '; + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0) + return; + xx += dx; + cur = 0; + bytes_view_render_state(bv, state_cur); + state = state_cur; + + if (state == GTK_STATE_NORMAL && byten) { + str[cur++] = ' '; + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + } else if (byten) { + str[cur++] = ' '; + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + if (off < len) { + switch (bv->format) { + case BYTES_HEX: + str[cur++] = hexchars[(pd[off] & 0xf0) >> 4]; + str[cur++] = hexchars[pd[off] & 0x0f]; + break; + case BYTES_BITS: + /* XXX, bitmask */ + for (j = 7; j >= 0; j--) + str[cur++] = (pd[off] & (1 << j)) ? '1' : '0'; + break; + } + } else { + switch (bv->format) { + case BYTES_HEX: + str[cur++] = ' '; + str[cur++] = ' '; + break; + case BYTES_BITS: + for (j = 7; j >= 0; j--) + str[cur++] = ' '; + break; + } + } + off++; + } + + if (state != GTK_STATE_NORMAL) { + if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0) + return; + xx += dx; + cur = 0; + bytes_view_render_state(bv, GTK_STATE_NORMAL); + state = GTK_STATE_NORMAL; + } + + /* Print some space at the end of the line */ + str[cur++] = ' '; str[cur++] = ' '; str[cur++] = ' '; + + /* Print the ASCII bit */ + for (byten = 0, off = org_off; byten < bv->per_line; byten++) { + gboolean byte_highlighted = + (off >= bv->start[0] && off < bv->end[0]) || + (off >= bv->start[1] && off < bv->end[1]); + int state_cur = (off < len && byte_highlighted) ? + GTK_STATE_SELECTED : GTK_STATE_NORMAL; + + if (state_cur != state) { + if (state == GTK_STATE_NORMAL && byten) { + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0) + return; + xx += dx; + cur = 0; + bytes_view_render_state(bv, state_cur); + state = state_cur; + + if (state == GTK_STATE_NORMAL && byten) { + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + } else if (byten) { + /* insert a space every BYTE_VIEW_SEP bytes */ + if ((off % BYTE_VIEW_SEP) == 0) + str[cur++] = ' '; + } + + if (off < len) { + c = (bv->encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) ? + EBCDIC_to_ASCII1(pd[off]) : + pd[off]; + + str[cur++] = isprint(c) ? c : '.'; + } else + str[cur++] = ' '; + + off++; + } + + if (cur) { + if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0) + return; + xx += dx; + /* cur = 0; */ + } + + if (state != GTK_STATE_NORMAL) { + bytes_view_render_state(bv, GTK_STATE_NORMAL); + /* state = GTK_STATE_NORMAL; */ + } + + if (bv->max_width < xx) + bv->max_width = xx; +} + +static void +_bytes_view_render_line(BytesView *bv, cairo_t *cr, const int org_off, int yy) +{ + _bytes_view_line_common(bv, cr, org_off, MARGIN, yy, bytes_view_flush_render); +} + +static int +_bytes_view_find_pos(BytesView *bv, const int org_off, int search_x) +{ + int pos_x = 0; + + _bytes_view_line_common(bv, &pos_x, org_off, MARGIN, search_x, bytes_view_flush_pos); + + return pos_x; +} + +static void +bytes_view_render(BytesView *bv, GdkRectangle *area) +{ + const int old_max_width = bv->max_width; + + int width, height; + GdkDrawable *draw_buf; + cairo_t *cr; + int y; + int off; + + guint line, lines_max; + guint lines_max_full; + + if (!gtk_widget_get_realized(GTK_WIDGET(bv))) + return; + + bytes_view_ensure_layout(bv); + + draw_buf = GTK_WIDGET(bv)->window; + gdk_drawable_get_size(draw_buf, &width, &height); + + if (width < 32 + MARGIN || height < bv->fontsize) + return; + + if (area) { + line = area->y / bv->fontsize; + lines_max = 1 + (area->y + area->height) / bv->fontsize; + } else { + line = 0; + lines_max = (guint) -1; + } + + off = 0; + y = (bv->fontsize * line); + + cr = gdk_cairo_create(draw_buf); + if (area) { + gdk_cairo_rectangle(cr, area); + /* gdk_cairo_area(cr, rectangle); */ + cairo_clip(cr); + } + + /* clear */ + gdk_cairo_set_source_color(cr, >k_widget_get_style(GTK_WIDGET(bv))->base[GTK_STATE_NORMAL]); + if (area) + cairo_rectangle(cr, area->x, area->y, area->x + area->width, area->y + area->height); + else + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); + + if (bv->pd) { + guint real_line = line + bytes_view_ensure_vadj(bv)->value; + + lines_max_full = (height / bv->fontsize) + 1; + if (lines_max_full < lines_max) + lines_max = lines_max_full; + + off = real_line * bv->per_line; + + while (off < bv->len) { + _bytes_view_render_line(bv, cr, off, y + bv->font_ascent); + line++; + if (line >= lines_max) + break; + + off += bv->per_line; + y += bv->fontsize; + } + } + + cairo_destroy(cr); + + if (old_max_width != bv->max_width) + bytes_view_adjustment_set(bv); +} + +static void +bytes_view_render_full(BytesView *bv) +{ + if (bv->adj_tag) { + g_source_remove(bv->adj_tag); + bv->adj_tag = 0; + } + bytes_view_render(bv, NULL); +} + +static void +bytes_view_paint(GtkWidget *widget, GdkRectangle *area) +{ + BytesView *bv = BYTES_VIEW(widget); + + if (area->x == 0 && area->y == 0 && area->height == widget->allocation.height && area->width == widget->allocation.width) + bytes_view_render_full(bv); + else + bytes_view_render(bv, area); +} + +static gboolean +bytes_view_expose(GtkWidget *widget, GdkEventExpose *event) +{ + bytes_view_paint(widget, &event->area); + return FALSE; +} + +static void +bytes_view_adjustment_set(BytesView *bv) +{ + if (bv->vadj) { + bv->vadj->lower = 0; + bv->vadj->upper = (int) (bv->len / bv->per_line); + if ((bv->len % bv->per_line)) + bv->vadj->upper++; + + bytes_view_ensure_layout(bv); + + bv->vadj->page_size = + (GTK_WIDGET(bv)->allocation.height - bv->font_descent) / bv->fontsize; + bv->vadj->step_increment = 1; + bv->vadj->page_increment = bv->vadj->page_size; + + if (bv->vadj->value > bv->vadj->upper - bv->vadj->page_size) + bv->vadj->value = bv->vadj->upper - bv->vadj->page_size; + + if (bv->vadj->value < 0) + bv->vadj->value = 0; + + gtk_adjustment_changed(bv->vadj); + } + + if (bv->hadj) { + bv->hadj->lower = 0; + bv->hadj->upper = bv->max_width; + + bytes_view_ensure_layout(bv); + + bv->hadj->page_size = (GTK_WIDGET(bv)->allocation.width); + bv->hadj->step_increment = bv->hadj->page_size / 10.0; + bv->hadj->page_increment = bv->hadj->page_size; + + if (bv->hadj->value > bv->hadj->upper - bv->hadj->page_size) + bv->hadj->value = bv->hadj->upper - bv->hadj->page_size; + + if (bv->hadj->value < 0) + bv->hadj->value = 0; + + gtk_adjustment_changed(bv->hadj); + } +} + +static gint +bytes_view_adjustment_timeout(BytesView *bv) +{ + bv->adj_tag = 0; + bytes_view_render_full(bv); + return 0; +} + +static void +bytes_view_adjustment_changed(GtkAdjustment *adj, BytesView *bv) +{ + /* delay rendering when scrolling (10ms) */ + if (adj && ((adj == bv->vadj) || (adj == bv->hadj))) { + if (!bv->adj_tag) + bv->adj_tag = g_timeout_add(REFRESH_TIMEOUT, (GSourceFunc) bytes_view_adjustment_timeout, bv); + } +} + +static void +bytes_view_set_scroll_adjustments(BytesView *bv, GtkAdjustment *hadj, GtkAdjustment *vadj) +{ + gboolean need_adjust = FALSE; + + g_return_if_fail(!hadj || GTK_IS_ADJUSTMENT(hadj)); + g_return_if_fail(!vadj || GTK_IS_ADJUSTMENT(vadj)); + + if (!vadj) + vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + + if (!hadj) + hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + + if (bv->vadj && (bv->vadj != vadj)) { + g_signal_handlers_disconnect_by_func(bv->vadj, bytes_view_adjustment_changed, bv); + g_object_unref(bv->vadj); + } + if (bv->vadj != vadj) { + bv->vadj = vadj; + g_object_ref_sink(bv->vadj); + + g_signal_connect(bv->vadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv); + need_adjust = TRUE; + } + + if (bv->hadj && (bv->hadj != hadj)) { + g_signal_handlers_disconnect_by_func(bv->hadj, bytes_view_adjustment_changed, bv); + g_object_unref(bv->hadj); + } + if (bv->hadj != hadj) { + bv->hadj = hadj; + g_object_ref_sink(bv->hadj); + + g_signal_connect(bv->hadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv); + need_adjust = TRUE; + } + + if (need_adjust) + bytes_view_adjustment_set(bv); +} + +static void +bytes_view_class_init(BytesViewClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = (GtkWidgetClass *) g_type_class_peek_parent(klass); + + object_class = (GtkObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + + object_class->destroy = bytes_view_destroy; + widget_class->realize = bytes_view_realize; + widget_class->unrealize = bytes_view_unrealize; + widget_class->size_request = bytes_view_size_request; + widget_class->size_allocate = bytes_view_allocate; + widget_class->expose_event = bytes_view_expose; + widget_class->scroll_event = bytes_view_scroll; + + klass->set_scroll_adjustments = bytes_view_set_scroll_adjustments; + + widget_class->set_scroll_adjustments_signal = + g_signal_new(g_intern_static_string("set-scroll-adjustments"), + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(BytesViewClass, set_scroll_adjustments), + NULL, NULL, + gtk_marshal_VOID__POINTER_POINTER, +/* _gtk_marshal_VOID__OBJECT_OBJECT, */ + G_TYPE_NONE, 2, + GTK_TYPE_ADJUSTMENT, + GTK_TYPE_ADJUSTMENT); +} + +GType +bytes_view_get_type(void) +{ + static GType bytes_view_type = 0; + + if (!bytes_view_type) { + static const GTypeInfo bytes_view_info = { + sizeof (BytesViewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) bytes_view_class_init, + NULL, /* class finalize */ + NULL, /* class_data */ + sizeof(BytesView), + 0, /* n_preallocs */ + (GInstanceInitFunc) bytes_view_init, + NULL /* value_table */ + }; + + bytes_view_type = g_type_register_static(GTK_TYPE_WIDGET, + "BytesView", + &bytes_view_info, + (GTypeFlags)0); + } + return bytes_view_type; +} + +int +bytes_view_byte_from_xy(BytesView *bv, int x, int y) +{ + /* hex_pos_byte array generated with hex_view_get_byte(0, 0, 0...70) */ + static const int hex_pos_byte[70] = { + -1, -1, + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, + 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + -1, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, + 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, + -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, + -1, + 8, 9, 10, 11, 12, 13, 14, 15 + }; + + /* bits_pos_byte array generated with bit_view_get_byte(0, 0, 0...84) */ + static const int bits_pos_byte[84] = { + -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7 + }; + + int char_x, off_x = 1; + int char_y, off_y; + + if (x < MARGIN) + return -1; + + bytes_view_ensure_layout(bv); + + char_y = bytes_view_ensure_vadj(bv)->value + (y / bv->fontsize); + off_y = char_y * bv->per_line; + + char_x = _bytes_view_find_pos(bv, off_y, x); + if (/* char_x < 0 || */ char_x < bv->use_digits) + return -1; + char_x -= bv->use_digits; + + switch (bv->format) { + case BYTES_BITS: + g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(bits_pos_byte), -1); + off_x = bits_pos_byte[char_x]; + break; + + case BYTES_HEX: + g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(hex_pos_byte), -1); + off_x = hex_pos_byte[char_x]; + break; + } + + if (off_x == -1) + return -1; + + return off_y + off_x; +} + +void +bytes_view_scroll_to_byte(BytesView *bv, int byte) +{ + int line; + + g_return_if_fail(byte >= 0 && byte < bv->len); + + line = byte / bv->per_line; + + bytes_view_ensure_vadj(bv); + + if (line > bv->vadj->upper - bv->vadj->page_size) { + line = bv->vadj->upper - bv->vadj->page_size; + + if (line < 0) + line = 0; + } + + /* after bytes_view_scroll_to_byte() we always do bytes_view_refresh() so we can block it */ + g_signal_handlers_block_by_func(bv->vadj, bytes_view_adjustment_changed, bv); + gtk_adjustment_set_value(bv->vadj, line); + g_signal_handlers_unblock_by_func(bv->vadj, bytes_view_adjustment_changed, bv); + + /* XXX, scroll hadj? */ +} + +void +bytes_view_set_font(BytesView *bv, PangoFontDescription *font) +{ + if (bv->font) + pango_font_description_free(bv->font); + + bv->font = pango_font_description_copy(font); + bv->max_width = 0; + + if (bv->context) { + g_object_unref(bv->context); + bv->context = NULL; + bytes_view_ensure_layout(bv); + } +} + +void +bytes_view_set_data(BytesView *bv, const guint8 *data, int len) +{ + g_free(bv->pd); + bv->pd = g_memdup(data, len); + bv->len = len; + + /* + * How many of the leading digits of the offset will we supply? + * We always supply at least 4 digits, but if the maximum offset + * won't fit in 4 digits, we use as many digits as will be needed. + */ + if (((len - 1) & 0xF0000000) != 0) + bv->use_digits = 8; /* need all 8 digits */ + else if (((len - 1) & 0x0F000000) != 0) + bv->use_digits = 7; /* need 7 digits */ + else if (((len - 1) & 0x00F00000) != 0) + bv->use_digits = 6; /* need 6 digits */ + else if (((len - 1) & 0x000F0000) != 0) + bv->use_digits = 5; /* need 5 digits */ + else + bv->use_digits = 4; /* we'll supply 4 digits */ + + bytes_view_ensure_vadj(bv); + + bytes_view_adjustment_set(bv); +} + +void +bytes_view_set_encoding(BytesView *bv, int enc) +{ + g_assert(enc == PACKET_CHAR_ENC_CHAR_ASCII || enc == PACKET_CHAR_ENC_CHAR_EBCDIC); + + bv->encoding = enc; +} + +void +bytes_view_set_format(BytesView *bv, int format) +{ + g_assert(format == BYTES_HEX || format == BYTES_BITS); + + bv->format = format; + + switch (format) { + case BYTES_BITS: + bv->per_line = 8; + break; + + case BYTES_HEX: + bv->per_line = 16; + break; + } +} + +void +bytes_view_set_highlight_style(BytesView *bv, gboolean inverse) +{ + bv->bold_highlight = !inverse; +} + +void +bytes_view_set_highlight(BytesView *bv, int start, int end, guint32 mask _U_, int maskle _U_) +{ + bv->start[0] = start; + bv->end[0] = end; +} + +void +bytes_view_set_highlight_appendix(BytesView *bv, int start, int end) +{ + bv->start[1] = start; + bv->end[1] = end; +} + +void +bytes_view_refresh(BytesView *bv) +{ + /* bytes_view_render_full(bv); */ + gtk_widget_queue_draw(GTK_WIDGET(bv)); +} + +GtkWidget * +bytes_view_new(void) +{ + GtkWidget *widget; + + widget = (GtkWidget *) g_object_new(BYTES_VIEW_TYPE, NULL); + + g_assert(widget != NULL); + + return widget; +} diff --git a/ui/gtk/bytes_view.h b/ui/gtk/bytes_view.h new file mode 100644 index 0000000000..e5decee01d --- /dev/null +++ b/ui/gtk/bytes_view.h @@ -0,0 +1,26 @@ +#ifndef __BYTES_VIEW_H__ +#define __BYTES_VIEW_H__ + +#define BYTES_VIEW_TYPE (bytes_view_get_type()) +#define BYTES_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST((object), BYTES_VIEW_TYPE, BytesView)) + +typedef struct _BytesView BytesView; + +GType bytes_view_get_type(void); + +GtkWidget *bytes_view_new(void); +void bytes_view_set_font(BytesView *bv, PangoFontDescription *font); + +void bytes_view_set_data(BytesView *bv, const guint8 *data, int len); +void bytes_view_set_encoding(BytesView *bv, int enc); +void bytes_view_set_format(BytesView *bv, int format); +void bytes_view_set_highlight_style(BytesView *bv, gboolean bold); + +void bytes_view_set_highlight(BytesView *bv, int start, int end, guint32 mask, int maskle); +void bytes_view_set_highlight_appendix(BytesView *bv, int start, int end); + +void bytes_view_refresh(BytesView *bv); +int bytes_view_byte_from_xy(BytesView *bv, int x, int y); +void bytes_view_scroll_to_byte(BytesView *bv, int byte); + +#endif /* __BYTES_VIEW_H__ */ diff --git a/ui/gtk/old-gtk-compat.h b/ui/gtk/old-gtk-compat.h index 30bdcdd4d6..50275e3069 100644 --- a/ui/gtk/old-gtk-compat.h +++ b/ui/gtk/old-gtk-compat.h @@ -58,6 +58,9 @@ #if !GTK_CHECK_VERSION (2, 20, 0) # define gtk_widget_get_realized(x) GTK_WIDGET_REALIZED(x) +# define _gtk_widget_set_realized_true(x) GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED) +#else +# define _gtk_widget_set_realized_true(x) gtk_widget_set_realized(x, TRUE) #endif #if !GTK_CHECK_VERSION (2, 22, 0) diff --git a/ui/gtk/packet_panes.c b/ui/gtk/packet_panes.c index d98889e4a2..c800c7bcd9 100644 --- a/ui/gtk/packet_panes.c +++ b/ui/gtk/packet_panes.c @@ -77,6 +77,7 @@ #include "ui/gtk/menus.h" #include "ui/gtk/packet_panes.h" #include "ui/gtk/proto_tree_model.h" +#include "ui/gtk/bytes_view.h" #ifdef _WIN32 #include @@ -87,7 +88,6 @@ #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr" #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr" -#define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits" #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff" #define E_BYTE_VIEW_START_KEY "byte_view_start" #define E_BYTE_VIEW_END_KEY "byte_view_end" @@ -243,40 +243,6 @@ collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter, } } -#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */ -#define BYTES_PER_LINE 16 /* max byte values in a line */ -#define BITS_PER_LINE 8 /* max bit values in a line */ -#define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */ -#define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1) -/* max number of characters hex dump takes - - 2 digits plus trailing blank - plus separator between first and - second 8 digits */ -#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE) -/* number of characters those bytes take; - 3 characters per byte of hex dump, - 2 blanks separating hex from ASCII, - 1 character per byte of ASCII dump */ -#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN) -/* number of characters per line; - offset, 2 blanks separating offset - from data dump, data dump */ -#define MAX_LINES 100 -#define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN) - -/* Which byte the offset is referring to. Associates - * whitespace with the preceding digits. */ -static int -byte_num(int offset, int start_point) -{ - return (offset - start_point) / 3; -} -static int -bit_num(int offset, int start_point) -{ - return (offset - start_point) / 9; -} - struct field_lookup_info { field_info *fi; GtkTreeIter iter; @@ -312,174 +278,6 @@ GtkTreePath return gtk_tree_model_get_path(model, &fli.iter); } -static int -hex_view_get_byte(guint ndigits, int row, int column) -{ - int byte; - int digits_start_1; - int digits_end_1; - int digits_start_2; - int digits_end_2; - int text_start_1; - int text_end_1; - int text_start_2; - int text_end_2; - - /* - * The column of the first hex digit in the first half. - * That starts after "ndigits" digits of offset and two - * separating blanks. - */ - digits_start_1 = ndigits + 2; - - /* - * The column of the last hex digit in the first half. - * There are BYTES_PER_LINE/2 bytes displayed in the first - * half; there are 2 characters per byte, plus a separating - * blank after all but the last byte's characters. - */ - digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 + - (BYTES_PER_LINE/2 - 1); - - /* - * The column of the first hex digit in the second half. - * Add 2 for the 2 separating blanks between the halves. - */ - digits_start_2 = digits_end_1 + 2; - - /* - * The column of the last hex digit in the second half. - * Add the same value we used to get "digits_end_1" from - * "digits_start_1". - */ - digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 + - (BYTES_PER_LINE/2 - 1); - - /* - * The column of the first "text dump" character in the first half. - * Add 3 for the 3 separating blanks between the hex and text dump. - */ - text_start_1 = digits_end_2 + 3; - - /* - * The column of the last "text dump" character in the first half. - * There are BYTES_PER_LINE/2 bytes displayed in the first - * half; there is 1 character per byte. - * - * Then subtract 1 to get the last column of the first half - * rather than the first column after the first half. - */ - text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1; - - /* - * The column of the first "text dump" character in the second half. - * Add back the 1 to get the first column after the first half, - * and then add 1 for the separating blank between the halves. - */ - text_start_2 = text_end_1 + 2; - - /* - * The column of the last "text dump" character in second half. - * Add the same value we used to get "text_end_1" from - * "text_start_1". - */ - text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1; - - /* Given the column and row, determine which byte offset - * the user clicked on. */ - if (column >= digits_start_1 && column <= digits_end_1) { - byte = byte_num(column, digits_start_1); - if (byte == -1) { - return byte; - } - } - else if (column >= digits_start_2 && column <= digits_end_2) { - byte = byte_num(column, digits_start_2); - if (byte == -1) { - return byte; - } - byte += 8; - } - else if (column >= text_start_1 && column <= text_end_1) { - byte = column - text_start_1; - } - else if (column >= text_start_2 && column <= text_end_2) { - byte = 8 + column - text_start_2; - } - else { - /* The user didn't select a hex digit or - * text-dump character. */ - return -1; - } - - /* Add the number of bytes from the previous rows. */ - byte += row * BYTES_PER_LINE; - - return byte; -} - -static int -bit_view_get_byte(guint ndigits, int row, int column) -{ - int byte; - int digits_start; - int digits_end; - int text_start; - int text_end; - - /* - * The column of the first bit digit. - * That starts after "ndigits" digits of offset and two - * separating blanks. - */ - digits_start = ndigits + 2; - - /* - * The column of the last bit digit. - * There are BITS_PER_LINE bytes displayed; there are - * 8 characters per byte, plus a separating blank - * after all but the last byte's characters. - */ - digits_end = digits_start + (BITS_PER_LINE)*8 + - (BITS_PER_LINE - 1); - - /* - * The column of the first "text dump" character. - * Add 3 for the 3 separating blanks between the bit and text dump. - */ - text_start = digits_end + 3; - - /* - * The column of the last "text dump" character. - * There are BITS_PER_LINE bytes displayed; there is 1 character per byte. - * - * Then subtract 1 to get the last column. - */ - text_end = text_start + BITS_PER_LINE - 1; - - /* Given the column and row, determine which byte offset - * the user clicked on. */ - if (column >= digits_start && column <= digits_end) { - byte = bit_num(column, digits_start); - if (byte == -1) { - return byte; - } - } - else if (column >= text_start && column <= text_end) { - byte = column - text_start; - } - else { - /* The user didn't select a hex digit or - * text-dump character. */ - return -1; - } - - /* Add the number of bytes from the previous rows. */ - byte += row * BITS_PER_LINE; - - return byte; -} - /* If the user selected a certain byte in the byte view, try to find * the item in the GUI proto_tree that corresponds to that byte, and: * @@ -488,13 +286,8 @@ bit_view_get_byte(guint ndigits, int row, int column) gboolean byte_view_select(GtkWidget *widget, GdkEventButton *event) { - GtkTextView *bv = GTK_TEXT_VIEW(widget); proto_tree *tree; GtkTreeView *tree_view; - GtkTextIter iter; - int row, column; - guint ndigits; - gint x, y; int byte = -1; tvbuff_t *tvb; @@ -508,30 +301,7 @@ byte_view_select(GtkWidget *widget, GdkEventButton *event) tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_VIEW_PTR)); - /* get the row/column selected */ - gtk_text_view_window_to_buffer_coords(bv, - gtk_text_view_get_window_type(bv, event->window), - (gint) event->x, (gint) event->y, &x, &y); - gtk_text_view_get_iter_at_location(bv, &iter, x, y); - row = gtk_text_iter_get_line(&iter); - column = gtk_text_iter_get_line_offset(&iter); - - /* - * Get the number of digits of offset being displayed, and - * compute the byte position in the buffer. - */ - ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY)); - - switch (recent.gui_bytes_view) { - case BYTES_HEX: - byte = hex_view_get_byte(ndigits, row, column); - break; - case BYTES_BITS: - byte = bit_view_get_byte(ndigits, row, column); - break; - default: - g_assert_not_reached(); - } + byte = bytes_view_byte_from_xy(BYTES_VIEW(widget), event->x, event->y); if (byte == -1) { return FALSE; @@ -686,19 +456,7 @@ GtkWidget * add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb, proto_tree *tree, GtkWidget *tree_view) { - GtkWidget *byte_view, *byte_scrollw, *label; - GtkTextBuffer *buf; -#if GTK_CHECK_VERSION(3,0,0) - GtkStyleContext *context; - GdkRGBA *rgba_bg_color; - GdkRGBA *rgba_fg_color; -#if !GTK_CHECK_VERSION(3,2,0) - GdkColor bg_color; - GdkColor fg_color; -#endif /* GTK_CHECK_VERSION(3,2,0) */ -#else - GtkStyle *style; -#endif + GtkWidget *byte_view, *byte_scrollw, *label; /* Byte view. Create a scrolled window for the text. */ byte_scrollw = scrolled_window_new(NULL, NULL); @@ -710,49 +468,9 @@ add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb, gtk_widget_show(byte_scrollw); - byte_view = gtk_text_view_new(); - gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE); - buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view)); -#if GTK_CHECK_VERSION(3,0,0) - context = gtk_widget_get_style_context (GTK_WIDGET(byte_view)); - gtk_style_context_get (context, GTK_STATE_FLAG_SELECTED, - GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &rgba_bg_color, - GTK_STYLE_PROPERTY_COLOR, &rgba_fg_color, - NULL); -#if GTK_CHECK_VERSION(3,2,0) - gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL); - gtk_text_buffer_create_tag(buf, "reverse", - "font-desc", user_font_get_regular(), - "foreground-gdk", rgba_fg_color, - "background-gdk", rgba_bg_color, - NULL); -#else - /* Hack */ - bg_color.red = rgba_bg_color->red * 65535; - bg_color.green = rgba_bg_color->green * 65535; - bg_color.blue = rgba_bg_color->blue * 65535; - fg_color.red = rgba_fg_color->red * 65535; - fg_color.green = rgba_fg_color->green * 65535; - fg_color.blue = rgba_fg_color->blue * 65535; + byte_view = bytes_view_new(); + bytes_view_set_font(BYTES_VIEW(byte_view), user_font_get_regular()); - gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL); - gtk_text_buffer_create_tag(buf, "reverse", - "font-desc", user_font_get_regular(), - "foreground-gdk", &fg_color, - "background-gdk", &bg_color, - NULL); -#endif /* GTK_CHECK_VERSION(3,2,0) */ -#else - style = gtk_widget_get_style(GTK_WIDGET(top_level)); - gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL); - gtk_text_buffer_create_tag(buf, "reverse", - "font-desc", user_font_get_regular(), - "foreground-gdk", &style->text[GTK_STATE_SELECTED], - "background-gdk", &style->base[GTK_STATE_SELECTED], - NULL); -#endif - gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL); g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb); gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view); @@ -1153,459 +871,23 @@ savehex_cb(GtkWidget * w _U_, gpointer data _U_) } #endif -static GtkTextMark * -packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int bstart, int bend, guint32 mask, int mask_le, int use_digits, int create_mark) -{ - GtkTextIter i_start, i_stop, iter; - - GtkTextTag *revstyle_tag; - const char *revstyle; - - int per_line = 0; - int per_one = 0; - int bits_per_one = 0; - int hex_offset, ascii_offset; - - int start_line, start_line_pos; - int stop_line, stop_line_pos; - - if (bstart == -1 || bend == -1) - return NULL; - - /* Display with inverse video ? */ - if (prefs.gui_hex_dump_highlight_style) - revstyle = "reverse"; - else - revstyle = "bold"; - - revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle); - - switch (recent.gui_bytes_view) { - case BYTES_HEX: - per_line = BYTES_PER_LINE; - per_one = 2+1; /* "ff " */ - bits_per_one = 4; - break; - case BYTES_BITS: - per_line = BITS_PER_LINE; - per_one = 8+1; /* "10101010 " */ - bits_per_one = 1; - break; - default: - g_assert_not_reached(); - } - - start_line = bstart / per_line; - start_line_pos = bstart % per_line; - - stop_line = bend / per_line; - stop_line_pos = bend % per_line; - -#define hex_fix(pos) hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line) -#define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line) - - hex_offset = use_digits + 2; - ascii_offset = hex_fix(per_line) + 2; - - gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos)); - - if (mask == 0x00) { - while (start_line <= stop_line) { - int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line; - int first_block_adjust = (recent.gui_bytes_view == BYTES_HEX) ? (line_pos_end == per_line/2) : 0; - - if (start_line_pos == line_pos_end) break; - - /* bits/hex */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos)); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1-first_block_adjust); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - - /* ascii */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos)); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end)-first_block_adjust); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - - start_line_pos = 0; - start_line++; - } - - } else if (mask_le) { /* LSB of mask first (little-endian) */ - while (start_line <= stop_line) { - int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line; - int line_pos = start_line_pos; - - while (line_pos < line_pos_end) { - int lop = 8 / bits_per_one; - int mask_per_one = (1 << bits_per_one) - 1; - int ascii_on = 0; - - while (lop--) { - if ((mask & mask_per_one)) { - /* bits/hex */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - - ascii_on = 1; - } - mask >>= bits_per_one; - } - - /* at least one bit of ascii was one -> turn ascii on */ - if (ascii_on) { - /* ascii */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos)); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - } - - if (!mask) - goto xend; - - line_pos++; - } - - start_line_pos = 0; - start_line++; - } - } else { /* mask starting from end (big-endian) */ - while (start_line <= stop_line) { - int line_pos_start = (start_line == stop_line) ? start_line_pos : 0; - int line_pos = stop_line_pos-1; - - while (line_pos >= line_pos_start) { - int lop = 8 / bits_per_one; - int mask_per_one = (1 << bits_per_one) - 1; - int ascii_on = 0; - - while (lop--) { - if ((mask & mask_per_one)) { - /* bits/hex */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - - ascii_on = 1; - } - mask >>= bits_per_one; - } - - /* at least one bit of ascii was one -> turn ascii on */ - if (ascii_on) { - /* ascii */ - gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos)); - gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1); - gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop); - } - - if (!mask) - goto xend; - - line_pos--; - } - - stop_line_pos = per_line; - stop_line--; - } - } -xend: - return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL; -#undef hex_fix -#undef ascii_fix -} - -/* Update the progress bar this many times when reading a file. */ -#define N_PROGBAR_UPDATES 100 -/* The minimum packet length required to check if a progress bar is needed or not */ -#define MIN_PACKET_LENGTH 1024 - -/* - * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a - * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct - * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's - * presumably why there's a progress bar for it.) - * - * Perhaps what's needed is a custom widget (either one that lets you stuff - * text into it more quickly, or one that's a "virtual" widget so that the - * text for a row is constructed, via a callback, when the row is to be - * displayed). A custom widget might also let us treat the offset, hex - * data, and ASCII data as three columns, so you can select purely in - * the hex dump column. - */ -static void -packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding) -{ - int i = 0, j, k = 0, b, cur; - guchar line[MAX_LINES_LEN + 1]; - static guchar hexchars[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - static const guint8 bitmask[8] = { - 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; - guchar c = '\0'; - unsigned int use_digits; - GtkTextIter iter; - - progdlg_t *progbar = NULL; - float progbar_val; - gboolean progbar_stop_flag; - GTimeVal progbar_start_time; - gchar progbar_status_str[100]; - int progbar_nextstep; - int progbar_quantum; - - gtk_text_buffer_set_text(buf, "", 0); - gtk_text_buffer_get_start_iter(buf, &iter); - - /* - * How many of the leading digits of the offset will we supply? - * We always supply at least 4 digits, but if the maximum offset - * won't fit in 4 digits, we use as many digits as will be needed. - */ - if (((len - 1) & 0xF0000000) != 0) - use_digits = 8; /* need all 8 digits */ - else if (((len - 1) & 0x0F000000) != 0) - use_digits = 7; /* need 7 digits */ - else if (((len - 1) & 0x00F00000) != 0) - use_digits = 6; /* need 6 digits */ - else if (((len - 1) & 0x000F0000) != 0) - use_digits = 5; /* need 5 digits */ - else - use_digits = 4; /* we'll supply 4 digits */ - - /* Record the number of digits in this text view. */ - g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits)); - - /* Update the progress bar when it gets to this value. */ - if (len > MIN_PACKET_LENGTH){ - progbar_nextstep = 0; - }else{ - /* If length =< MIN_PACKET_LENGTH - * there is no need to calculate the progress - */ - progbar_nextstep = len+1; - } - - /* When we reach the value that triggers a progress bar update, - bump that value by this amount. */ - progbar_quantum = len/N_PROGBAR_UPDATES; - /* Progress so far. */ - progbar_val = 0.0f; - - progbar_stop_flag = FALSE; - g_get_current_time(&progbar_start_time); - - cur = 0; - while (i < len) { - /* Create the progress bar if necessary. - We check on every iteration of the loop, so that it takes no - longer than the standard time to create it (otherwise, for a - large packet, we might take considerably longer than that standard - time in order to get to the next progress bar step). */ - if ((progbar == NULL) && (len > MIN_PACKET_LENGTH)) - progbar = delayed_create_progress_dlg("Processing", "Packet Details", - TRUE, - &progbar_stop_flag, - &progbar_start_time, - progbar_val); - - /* Update the progress bar, but do it only N_PROGBAR_UPDATES times; - when we update it, we have to run the GTK+ main loop to get it - to repaint what's pending, and doing so may involve an "ioctl()" - to see if there's any pending input from an X server, and doing - that for every packet can be costly, especially on a big file. */ - if (i >= progbar_nextstep) { - - if (progbar != NULL) { - /* let's not divide by zero. I should never be started - * with count == 0, so let's assert that - */ - g_assert(len > 0); - progbar_val = (gfloat) i / len; - g_snprintf(progbar_status_str, sizeof(progbar_status_str), - "%4u of %u bytes", i, len); - update_progress_dlg(progbar, progbar_val, progbar_status_str); - } - - progbar_nextstep += progbar_quantum; - } - - if (progbar_stop_flag) { - /* Well, the user decided to abort the operation. Just stop, - and arrange to return TRUE to our caller, so they know it - was stopped explicitly. */ - break; - } - - /* Print the line number */ - j = use_digits; - do { - j--; - c = (i >> (j*4)) & 0xF; - line[cur++] = hexchars[c]; - } while (j != 0); - line[cur++] = ' '; - line[cur++] = ' '; - - j = i; - switch (recent.gui_bytes_view) { - case BYTES_HEX: - k = i + BYTES_PER_LINE; - break; - case BYTES_BITS: - k = i + BITS_PER_LINE; - break; - default: - g_assert_not_reached(); - } - /* Print the hex bit */ - while (i < k) { - if (i < len) { - switch (recent.gui_bytes_view) { - case BYTES_HEX: - line[cur++] = hexchars[(pd[i] & 0xf0) >> 4]; - line[cur++] = hexchars[pd[i] & 0x0f]; - break; - case BYTES_BITS: - for (b = 0; b < 8; b++) { - line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0'; - } - break; - default: - g_assert_not_reached(); - } - } else { - switch (recent.gui_bytes_view) { - case BYTES_HEX: - line[cur++] = ' '; line[cur++] = ' '; - break; - case BYTES_BITS: - for (b = 0; b < 8; b++) { - line[cur++] = ' '; - } - break; - default: - g_assert_not_reached(); - } - } - i++; - /* Inter byte space if not at end of line */ - if (i < k) { - line[cur++] = ' '; - /* insert a space every BYTE_VIEW_SEP bytes */ - if( ( i % BYTE_VIEW_SEP ) == 0 ) { - line[cur++] = ' '; - } - } - } - - /* Print some space at the end of the line */ - line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' '; - - /* Print the ASCII bit */ - i = j; - - while (i < k) { - if (i < len) { - if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) { - c = pd[i]; - } - else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) { - c = EBCDIC_to_ASCII1(pd[i]); - } - else { - g_assert_not_reached(); - } - line[cur++] = isprint(c) ? c : '.'; - } else { - line[cur++] = ' '; - } - i++; - if (i < k) { - /* insert a space every BYTE_VIEW_SEP bytes */ - if( ( i % BYTE_VIEW_SEP ) == 0 ) { - line[cur++] = ' '; - } - } - } - line[cur++] = '\n'; - if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) { - gtk_text_buffer_insert(buf, &iter, line, cur); - cur = 0; - } - } - - /* We're done printing the packets; destroy the progress bar if - it was created. */ - if (progbar != NULL) - destroy_progress_dlg(progbar); - - if (cur) { - gtk_text_buffer_insert(buf, &iter, line, cur); - } -} - static void packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart, int bend, guint32 bmask, int bmask_le, int astart, int aend, int encoding) { - GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv); - GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view); - GtkTextBuffer *buf_tmp; - GtkTextMark *mark; - int ndigits; - - GtkTextIter start, end; - - g_object_ref(buf); - -#if 0 - /* XXX: Setting the text_view buffer to NULL is apparently - * not a good idea; If a progress_bar is displayed below - * in delayed_create_progress_dlg() there will then be - * a crash internally in the gtk library. - * (It appears that gtk_text_view_set_buffer - * queues a callback to be run when this - * thread is next idle. Unfortunately the call to - * gtk_main_iteration() in delayed_create_progress_dlg() - * causes the internal callback to be run which then - * crashes (because the textview has no buffer ?)) - */ - gtk_text_view_set_buffer(bv_text_view, NULL); -#endif - - /* attach a dummy buffer in place of the real buffer. - * (XXX: Presumably this is done so there's no attempt - * to display the real buffer until it has been - * completely generated). - */ - buf_tmp = gtk_text_buffer_new(NULL); - gtk_text_view_set_buffer(bv_text_view, buf_tmp); - g_object_unref(buf_tmp); - - packet_hex_print_common(buf, bv, pd, len, encoding); - - /* mark everything with "plain" tag */ - gtk_text_buffer_get_start_iter(buf, &start); - gtk_text_buffer_get_end_iter(buf, &end); - gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end); - - ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY)); - - /* mark reverse tags */ - mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1); - packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0); - - gtk_text_view_set_buffer(bv_text_view, buf); - - /* scroll text into position */ - if (mark) { - gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0); - gtk_text_buffer_delete_mark(buf, mark); - } - g_object_unref(buf); + bytes_view_set_encoding(BYTES_VIEW(bv), encoding); + bytes_view_set_format(BYTES_VIEW(bv), recent.gui_bytes_view); + bytes_view_set_data(BYTES_VIEW(bv), pd, len); + + bytes_view_set_highlight_style(BYTES_VIEW(bv), prefs.gui_hex_dump_highlight_style); + + bytes_view_set_highlight(BYTES_VIEW(bv), bstart, bend, bmask, bmask_le); + bytes_view_set_highlight_appendix(BYTES_VIEW(bv), astart, aend); + + if (bstart != -1 && bend != -1) + bytes_view_scroll_to_byte(BYTES_VIEW(bv), bstart); + bytes_view_refresh(BYTES_VIEW(bv)); } void