2 * TCP graph drawing code
3 * By Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include <gdk/gdkkeysyms.h>
33 #include <math.h> /* rint() */
36 #include <epan/ipproto.h>
37 #include "globals.h" /* cfile */
38 #include <epan/packet.h> /* frame_data */
39 #include <epan/emem.h>
40 #include "simple_dialog.h"
41 #include "gui_utils.h"
43 #include "compat_macros.h"
44 #include <epan/etypes.h>
45 #include <epan/ppptypes.h>
46 #include "dlg_utils.h"
47 #include <epan/epan_dissect.h>
48 #include "../stat_menu.h"
49 #include "gui_stat_menu.h"
50 #include <epan/dissectors/packet-tcp.h>
51 #include <epan/address.h>
61 #define TCP_SYN(flags) ( flags & TH_SYN )
62 #define TCP_ACK(flags) ( flags & TH_ACK )
63 #define TCP_FIN(flags) ( flags & TH_FIN )
66 #define TXT_HEIGHT 550
68 /* for compare_headers() */
69 /* segment went the same direction as the currently selected one */
70 #define COMPARE_CURR_DIR 0
71 #define COMPARE_ANY_DIR 1
73 /* initalize_axis() */
74 #define AXIS_HORIZONTAL 0
75 #define AXIS_VERTICAL 1
89 guint32 th_win; /* make it 32 bits so we can handle some scaling */
98 double x, y, width, height;
102 double x1, y1, x2, y2;
106 int x, y, width, height;
138 struct segment *parent;
140 struct arc_params arc;
141 struct rect_params rect;
142 struct line_params line;
146 struct element_list {
147 struct element_list *next;
148 struct element *elements;
152 struct graph *g; /* which graph we belong to */
153 GtkWidget *drawing_area;
154 GdkPixmap *pixmap[2];
156 #define AXIS_ORIENTATION 1 << 0
158 /* dim and orig (relative to origin of window) of axis' pixmap */
160 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
163 gdouble major, minor; /* major and minor ticks */
167 #define HAXIS_INIT_HEIGHT 70
168 #define VAXIS_INIT_WIDTH 100
169 #define TITLEBAR_HEIGHT 50
170 #define RMARGIN_WIDTH 30
172 struct style_tseq_tcptrace {
178 struct style_tseq_stevens {
196 #define SEQ_ORIGIN 0x1
197 /* show absolute sequence numbers (not differences from isn) */
198 #define SEQ_ORIGIN_ZERO 0x1
199 #define SEQ_ORIGIN_ISN 0x0
200 #define TIME_ORIGIN 0x10
201 /* show time from beginning of capture as opposed to time from beginning
202 * of the connection */
203 #define TIME_ORIGIN_CAP 0x10
204 #define TIME_ORIGIN_CONN 0x0
206 /* this is used by rtt module only */
215 int draw; /* indicates whether we should draw cross at all */
217 GtkToggleButton *on_toggle;
218 GtkToggleButton *off_toggle;
222 double x0, y0, width, height;
231 double step_x, step_y;
233 #define ZOOM_OUT (1 << 0)
234 #define ZOOM_HLOCK (1 << 1)
235 #define ZOOM_VLOCK (1 << 2)
236 #define ZOOM_STEPS_SAME (1 << 3)
237 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
239 /* unfortunately, we need them both because gtk_toggle_button_set_active ()
240 * with second argument FALSE doesn't do anything, somehow */
242 GtkToggleButton *in_toggle;
243 GtkToggleButton *out_toggle;
246 GtkSpinButton *h_step;
247 GtkSpinButton *v_step;
259 struct ipoint offset;
263 #define MAGZOOMS_SAME (1 << 0)
264 #define MAGZOOMS_SAME_RATIO (1 << 1)
265 #define MAGZOOMS_IGNORE (1 << 31)
268 GtkSpinButton *h_zoom, *v_zoom;
274 #define GRAPH_TSEQ_STEVENS 0
275 #define GRAPH_TSEQ_TCPTRACE 1
276 #define GRAPH_THROUGHPUT 2
279 #define GRAPH_DESTROYED (1 << 0)
280 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
282 GtkWidget *toplevel; /* keypress handler needs this */
283 GtkWidget *drawing_area;
284 GtkWidget *text; /* text widget for seg list - probably
286 FONT_TYPE *font; /* font used for annotations etc. */
289 GdkPixmap *title_pixmap;
290 GdkPixmap *pixmap[2];
291 int displayed; /* which of both pixmaps is on screen right now */
293 GtkWidget *control_panel;
294 /* this belongs to style structs of graph types that make use of it */
295 GtkToggleButton *time_orig_conn, *seq_orig_isn;
298 /* Next 4 attribs describe the graph in natural units, before any scaling.
299 * For example, if we want to display graph of TCP conversation that
300 * started 112.309845 s after beginning of the capture and ran until
301 * 479.093582 s, 237019 B went through the connection (in one direction)
302 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
303 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
304 struct bounds bounds;
305 /* dimensions and position of the graph, both expressed already in pixels.
306 * x and y give the position of upper left corner of the graph relative
307 * to origin of the graph window, size is basically bounds*zoom */
309 /* viewport (=graph window area which is reserved for graph itself), its
310 * size and position relative to origin of the graph window */
313 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
314 * pixels, we have to scale the graph down by factor of 0.002109. This
315 * number would be zoom.y. Obviously, both directions have separate zooms.*/
318 struct magnify magnify;
319 struct axis *x_axis, *y_axis;
320 struct segment *segments;
321 struct segment *current;
322 struct element_list *elists; /* element lists */
324 struct style_tseq_stevens tseq_stevens;
325 struct style_tseq_tcptrace tseq_tcptrace;
326 struct style_tput tput;
327 struct style_rtt rtt;
331 static GdkGC *xor_gc = NULL;
334 #define debug(section) if (debugging & section)
335 /* print function entry points */
336 #define DBS_FENTRY (1 << 0)
337 #define DBS_AXES_TICKS (1 << 1)
338 #define DBS_AXES_DRAWING (1 << 2)
339 #define DBS_GRAPH_DRAWING (1 << 3)
340 #define DBS_TPUT_ELMTS (1 << 4)
341 /*int debugging = DBS_FENTRY;*/
343 /*int debugging = DBS_AXES_TICKS;*/
344 /*int debugging = DBS_AXES_DRAWING;*/
345 /*int debugging = DBS_GRAPH_DRAWING;*/
346 /*int debugging = DBS_TPUT_ELMTS;*/
348 static void create_gui (struct graph * );
350 static void create_text_widget (struct graph * );
351 static void display_text (struct graph * );
353 static void create_drawing_area (struct graph * );
354 static void control_panel_create (struct graph * );
355 static GtkWidget *control_panel_create_zoom_group (struct graph * );
356 static GtkWidget *control_panel_create_magnify_group (struct graph * );
357 static GtkWidget *control_panel_create_cross_group (struct graph * );
358 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
359 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
360 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
361 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
362 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
363 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
364 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
365 static void callback_toplevel_destroy (GtkWidget * , gpointer );
366 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
367 static void callback_close (GtkWidget * , gpointer );
368 static void callback_time_origin (GtkWidget * , gpointer );
369 static void callback_seq_origin (GtkWidget * , gpointer );
370 static void callback_zoomlock_h (GtkWidget * , gpointer );
371 static void callback_zoomlock_v (GtkWidget * , gpointer );
372 static void callback_zoom_inout (GtkWidget * , gpointer );
373 static void callback_zoom_step (GtkWidget * , gpointer );
374 static void callback_zoom_flags (GtkWidget * , gpointer );
375 static void callback_cross_on_off (GtkWidget * , gpointer );
376 static void callback_mag_width (GtkWidget * , gpointer );
377 static void callback_mag_height (GtkWidget * , gpointer );
378 static void callback_mag_x (GtkWidget * , gpointer );
379 static void callback_mag_y (GtkWidget * , gpointer );
380 static void callback_mag_zoom (GtkWidget * , gpointer );
381 static void callback_mag_flags (GtkWidget * , gpointer );
382 static void callback_graph_type (GtkWidget * , gpointer );
383 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
384 static void callback_create_help (GtkWidget * , gpointer );
385 static void update_zoom_spins (struct graph * );
386 static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
387 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
388 static int get_num_dsegs (struct graph * );
389 static int get_num_acks (struct graph * );
390 static void graph_type_dependent_initialize (struct graph * );
391 static struct graph *graph_new (void);
392 static void graph_destroy (struct graph * );
393 static void graph_initialize_values (struct graph * );
394 static void graph_init_sequence (struct graph * );
395 static void draw_element_line (struct graph * , struct element * );
396 static void draw_element_arc (struct graph * , struct element * );
397 static void graph_display (struct graph * );
398 static void graph_pixmaps_create (struct graph * );
399 static void graph_pixmaps_switch (struct graph * );
400 static void graph_pixmap_draw (struct graph * );
401 static void graph_pixmap_display (struct graph * );
402 static void graph_element_lists_make (struct graph * );
403 static void graph_element_lists_free (struct graph * );
404 static void graph_element_lists_initialize (struct graph * );
405 static void graph_title_pixmap_create (struct graph * );
406 static void graph_title_pixmap_draw (struct graph * );
407 static void graph_title_pixmap_display (struct graph * );
408 static void graph_segment_list_get (struct graph * );
409 static void graph_segment_list_free (struct graph * );
410 static void graph_select_segment (struct graph * , int , int );
411 static int line_detect_collision (struct element * , int , int );
412 static int arc_detect_collision (struct element * , int , int );
413 static void axis_pixmaps_create (struct axis * );
414 static void axis_pixmaps_switch (struct axis * );
415 static void axis_display (struct axis * );
416 static void v_axis_pixmap_draw (struct axis * );
417 static void h_axis_pixmap_draw (struct axis * );
418 static void axis_pixmap_display (struct axis * );
419 static void axis_compute_ticks (struct axis * , double , double , int );
420 static double axis_zoom_get (struct axis * , int );
421 static void axis_ticks_up (int * , int * );
422 static void axis_ticks_down (int * , int * );
423 static void axis_destroy (struct axis * );
424 static int get_label_dim (struct axis * , int , double );
425 static void toggle_time_origin (struct graph * );
426 static void toggle_seq_origin (struct graph * );
427 static void cross_xor (struct graph * , int , int );
428 static void cross_draw (struct graph * , int , int );
429 static void cross_erase (struct graph * );
430 static void magnify_create (struct graph * , int , int );
431 static void magnify_move (struct graph * , int , int );
432 static void magnify_destroy (struct graph * );
433 static void magnify_draw (struct graph * );
434 static void magnify_get_geom (struct graph * , int , int );
435 static gint configure_event (GtkWidget * , GdkEventConfigure * );
436 static gint expose_event (GtkWidget * , GdkEventExpose * );
437 static gint button_press_event (GtkWidget * , GdkEventButton * );
438 static gint button_release_event (GtkWidget * , GdkEventButton * );
439 static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
440 static gint key_press_event (GtkWidget * , GdkEventKey * );
441 static gint key_release_event (GtkWidget * , GdkEventKey * );
442 static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
443 static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
444 static void tseq_initialize (struct graph * );
445 static void tseq_get_bounds (struct graph * );
446 static void tseq_stevens_read_config (struct graph * );
447 static void tseq_stevens_make_elmtlist (struct graph * );
448 static void tseq_stevens_toggle_seq_origin (struct graph * );
449 static void tseq_stevens_toggle_time_origin (struct graph * );
450 static void tseq_tcptrace_read_config (struct graph * );
451 static void tseq_tcptrace_make_elmtlist (struct graph * );
452 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
453 static void tseq_tcptrace_toggle_time_origin (struct graph * );
454 static void tput_initialize (struct graph * );
455 static void tput_read_config (struct graph * );
456 static void tput_make_elmtlist (struct graph * );
457 static void tput_toggle_time_origin (struct graph * );
458 static void rtt_read_config (struct graph * );
459 static void rtt_initialize (struct graph * );
460 static int rtt_is_retrans (struct unack * , unsigned int );
461 static struct unack *rtt_get_new_unack (double , unsigned int );
462 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
463 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
464 static void rtt_make_elmtlist (struct graph * );
465 static void rtt_toggle_seq_origin (struct graph * );
466 #if defined(_WIN32) && !defined(__MINGW32__)
467 static int rint (double ); /* compiler template for Windows */
470 /* XXX - what about OS X? */
471 static char helptext[] =
473 "Here's what you can do:\n\
474 - Left Mouse Button selects segment in Wireshark's packet list\n\
475 - Middle Mouse Button zooms in\n\
476 - <shift>-Middle Button zooms out\n\
477 - Right Mouse Button moves the graph (if zoomed in)\n\
478 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
479 - Space toggles crosshairs\n\
480 - 's' toggles relative/absolute sequence numbers\n\
481 - 't' toggles time origin\n\
484 "Here's what you can do:\n\
485 - <ctrl>-Left Mouse Button selects segment in Wireshark's packet list\n\
486 - Left Mouse Button zooms in\n\
487 - <shift>-Left Mouse Button zooms out\n\
488 - Right Mouse Button moves the graph (if zoomed in)\n\
489 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
491 - Space bar toggles crosshairs\n\
492 - 's' - Toggles relative/absolute sequence numbers\n\
493 - 't' - Toggles time origin\n\
497 static void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
499 struct segment current;
501 struct tcpheader *thdr;
503 guint graph_type = GPOINTER_TO_INT(data);
505 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
507 if (! (g = graph_new()))
511 graph_initialize_values (g);
513 g->type = graph_type;
514 if (!(thdr=select_tcpip_session (&cfile, ¤t))) {
518 graph_segment_list_get(g);
520 /* display_text(g); */
521 graph_init_sequence(g);
524 static void create_gui (struct graph *g)
526 debug(DBS_FENTRY) puts ("create_gui()");
527 /* create_text_widget(g); */
528 control_panel_create (g);
529 create_drawing_area(g);
533 static void create_text_widget (struct graph *g)
535 GtkWidget *streamwindow, *txt_scrollw, *box;
537 debug(DBS_FENTRY) puts ("create_text_widget()");
538 streamwindow = dlg_window_new ("Wireshark: Packet chain");
539 gtk_widget_set_name (streamwindow, "Packet chain");
540 WIDGET_SET_SIZE(streamwindow, TXT_WIDTH, TXT_HEIGHT);
541 gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
543 box = gtk_vbox_new (FALSE, 0);
544 gtk_container_add (GTK_CONTAINER (streamwindow), box);
545 gtk_widget_show (box);
547 txt_scrollw = scrolled_window_new (NULL, NULL);
548 #if GTK_MAJOR_VERSION >= 2
549 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
552 gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
553 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
554 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
555 gtk_widget_show (txt_scrollw);
557 #if GTK_MAJOR_VERSION < 2
558 g->text = gtk_text_new(NULL, NULL);
559 gtk_text_set_editable(GTK_TEXT(g->text), FALSE);
561 g->text = gtk_text_view_new();
562 gtk_text_view_set_editable(GTK_TEXT_VIEW(g->text), FALSE);
564 gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
565 gtk_widget_show (g->text);
566 gtk_widget_show (streamwindow);
568 static void display_text (struct graph *g)
572 double first_time, prev_time;
573 unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
575 #if GTK_MAJOR_VERSION >= 2
580 debug(DBS_FENTRY) puts ("display_text()");
581 if (!gdk_color_parse ("SlateGray", &color)) {
583 * XXX - do more than just warn.
585 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
586 "Could not parse color SlateGray.");
588 #if GTK_MAJOR_VERSION < 2
589 gtk_text_freeze (GTK_TEXT (g->text));
591 g_snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
592 "pkt num", "time", "delta first", "delta prev",
593 "seqno", "delta first", "delta prev", "data (B)");
594 gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL,
595 (const char *)line, -1);
597 first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
598 prev_time = first_time;
599 /* we have to find Initial Sequence Number for both ends of connection */
600 for (ptr=g->segments; ptr; ptr=ptr->next) {
601 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
602 isn_this = ptr->th_seq;
606 for (ptr=g->segments; ptr; ptr=ptr->next) {
607 if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
608 isn_opposite = ptr->th_seq;
612 seq_this_prev = isn_this;
613 seq_opposite_prev = isn_opposite;
614 for (ptr=g->segments; ptr; ptr=ptr->next) {
615 double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
616 unsigned int seq = ptr->th_seq;
617 int seq_delta_isn, seq_delta_prev;
619 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
620 seq_delta_isn = seq - isn_this;
621 seq_delta_prev = seq - seq_this_prev;
625 seq_delta_isn = seq - isn_opposite;
626 seq_delta_prev = seq - seq_opposite_prev;
627 seq_opposite_prev = seq;
630 g_snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
631 ptr->num, time, time-first_time, time-prev_time,
632 seq, seq_delta_isn, seq_delta_prev,
634 #if GTK_MAJOR_VERSION < 2
635 gtk_text_insert(GTK_TEXT(g->text), g->font, c, NULL,
636 (const char * )line, -1);
638 gtk_text_buffer_insert(buf, &iter, (const char *)line, -1);
642 #if GTK_MAJOR_VERSION < 2
643 gtk_text_thaw (GTK_TEXT (g->text));
648 static void create_drawing_area (struct graph *g)
650 GdkColormap *colormap;
652 #define WINDOW_TITLE_LENGTH 64
653 char window_title[WINDOW_TITLE_LENGTH];
654 struct segment current;
655 struct tcpheader *thdr;
657 debug(DBS_FENTRY) puts ("create_drawing_area()");
659 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
661 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
662 "-*-*-m-*-iso8859-2");
664 thdr=select_tcpip_session (&cfile, ¤t);
665 g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
667 cf_get_display_name(&cfile),
668 address_to_str(&(thdr->ip_src)),
670 address_to_str(&(thdr->ip_dst)),
673 g->toplevel = dlg_window_new ("Tcp Graph");
674 gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
675 gtk_widget_set_name (g->toplevel, "Test Graph");
676 OBJECT_SET_DATA(g->toplevel, "graph", g);
678 /* Create the drawing area */
679 g->drawing_area = gtk_drawing_area_new ();
680 OBJECT_SET_DATA(g->drawing_area, "graph", g);
681 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
682 gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
683 g->wp.width + g->wp.x + RMARGIN_WIDTH,
684 g->wp.height + g->wp.y + g->x_axis->s.height);
685 gtk_widget_show (g->drawing_area);
687 SIGNAL_CONNECT(g->drawing_area, "expose_event", expose_event, NULL);
688 /* this has to be done later, after the widget has been shown */
690 SIGNAL_CONNECT(g->drawing_area,"configure_event", configure_event,
693 SIGNAL_CONNECT(g->drawing_area, "motion_notify_event",
694 motion_notify_event, NULL);
695 SIGNAL_CONNECT(g->drawing_area, "button_press_event",
696 button_press_event, NULL);
697 SIGNAL_CONNECT(g->drawing_area, "button_release_event",
698 button_release_event, NULL);
699 SIGNAL_CONNECT(g->drawing_area, "leave_notify_event",
700 leave_notify_event, NULL);
701 SIGNAL_CONNECT(g->drawing_area, "enter_notify_event",
702 enter_notify_event, NULL);
703 SIGNAL_CONNECT(g->toplevel, "destroy", callback_toplevel_destroy, g);
704 /* why doesn't drawing area send key_press_signals? */
705 SIGNAL_CONNECT(g->toplevel, "key_press_event", key_press_event, NULL);
706 SIGNAL_CONNECT(g->toplevel, "key_release_event", key_release_event,
708 gtk_widget_set_events(g->toplevel,
709 GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
711 gtk_widget_set_events (g->drawing_area,
713 | GDK_LEAVE_NOTIFY_MASK
714 | GDK_ENTER_NOTIFY_MASK
715 | GDK_BUTTON_PRESS_MASK
716 | GDK_BUTTON_RELEASE_MASK
717 | GDK_POINTER_MOTION_MASK
718 | GDK_POINTER_MOTION_HINT_MASK);
721 frame = gtk_frame_new (NULL);
722 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
723 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
725 box = gtk_hbox_new (FALSE, 0);
726 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
727 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
728 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
729 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
730 gtk_widget_show (frame);
731 gtk_widget_show (box);
734 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
735 gtk_widget_show (g->toplevel);
737 /* in case we didn't get what we asked for */
738 g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
739 g->wp.x - RMARGIN_WIDTH;
740 g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
741 g->wp.y - g->x_axis->s.height;
743 #if GTK_MAJOR_VERSION < 2
744 g->font = g->drawing_area->style->font;
745 gdk_font_ref (g->font);
747 g->font = g->drawing_area->style->font_desc;
750 colormap = gdk_window_get_colormap (g->drawing_area->window);
752 xor_gc = gdk_gc_new (g->drawing_area->window);
753 gdk_gc_set_function (xor_gc, GDK_XOR);
754 if (!gdk_color_parse ("gray15", &color)) {
756 * XXX - do more than just warn.
758 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
759 "Could not parse color gray15.");
761 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
763 * XXX - do more than just warn.
765 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
766 "Could not allocate color gray15.");
768 gdk_gc_set_foreground (xor_gc, &color);
770 g->fg_gc = gdk_gc_new (g->drawing_area->window);
771 g->bg_gc = gdk_gc_new (g->drawing_area->window);
772 if (!gdk_color_parse ("white", &color)) {
774 * XXX - do more than just warn.
776 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
777 "Could not parse color white.");
779 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
781 * XXX - do more than just warn.
783 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
784 "Could not allocate color white.");
786 gdk_gc_set_foreground (g->bg_gc, &color);
788 /* this is probably quite an ugly way to get rid of the first configure
790 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
791 * event which is handled during the next return to gtk_main which is
792 * probably the gdk_gc_new() call. configure handler calls
793 * graph_element_lists_make() which is not good because the graph struct is
794 * not fully set up yet - namely we're not sure about actual geometry
795 * and we don't have the GC's at all. so we just postpone installation
796 * of configure handler until we're ready to deal with it.
798 * !!! NEMÌLO BY TO BÝT NA KONCI graph_init_sequence()? !!!
801 SIGNAL_CONNECT(g->drawing_area,"configure_event", configure_event,
804 /* puts ("exiting create_drawing_area()"); */
807 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
809 struct graph *g = (struct graph * )data;
811 if (!(g->flags & GRAPH_DESTROYED)) {
812 g->flags |= GRAPH_DESTROYED;
813 graph_destroy ((struct graph * )data);
817 static void control_panel_create (struct graph *g)
819 GtkWidget *toplevel, *notebook;
821 GtkWidget *help_bt, *close_bt, *bbox;
822 #define WINDOW_TITLE_LENGTH 64
823 char window_title[WINDOW_TITLE_LENGTH];
825 debug(DBS_FENTRY) puts ("control_panel_create()");
827 notebook = gtk_notebook_new ();
828 control_panel_add_zoom_page (g, notebook);
829 control_panel_add_magnify_page (g, notebook);
830 control_panel_add_origin_page (g, notebook);
831 control_panel_add_cross_page (g, notebook);
832 control_panel_add_graph_type_page (g, notebook);
834 g_snprintf (window_title, WINDOW_TITLE_LENGTH,
835 "Graph %d - Control - Wireshark", refnum);
836 toplevel = dlg_window_new ("tcp-graph-control");
837 gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
839 table = gtk_table_new (2, 1, FALSE);
840 gtk_container_add (GTK_CONTAINER (toplevel), table);
842 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
843 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
846 bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
847 gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
848 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
850 help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
851 SIGNAL_CONNECT(help_bt, "clicked", callback_create_help, g);
853 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
854 window_set_cancel_button(toplevel, close_bt, NULL);
855 SIGNAL_CONNECT(close_bt, "clicked", callback_close, g);
857 SIGNAL_CONNECT(toplevel, "delete_event", callback_delete_event, g);
858 SIGNAL_CONNECT(toplevel, "destroy", callback_toplevel_destroy, g);
860 /* gtk_widget_show_all (table); */
861 /* g->gui.control_panel = table; */
862 gtk_widget_show_all (toplevel);
863 window_present(toplevel);
865 g->gui.control_panel = toplevel;
868 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
870 GtkWidget *zoom_frame;
871 GtkWidget *zoom_lock_frame;
875 zoom_frame = control_panel_create_zoom_group (g);
876 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
877 zoom_lock_frame = control_panel_create_zoomlock_group (g);
878 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
879 box = gtk_vbox_new (FALSE, 0);
880 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
881 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
882 gtk_widget_show (box);
883 label = gtk_label_new ("Zoom");
884 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
887 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
889 GtkWidget *mag_frame, *label;
891 mag_frame = control_panel_create_magnify_group (g);
892 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
893 label = gtk_label_new ("Magnify");
894 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
897 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
899 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
900 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
901 GtkWidget *box, *label;
903 /* time origin box */
905 gtk_radio_button_new_with_label (NULL, "beginning of capture");
906 time_orig_conn = gtk_radio_button_new_with_label (
907 gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)),
908 "beginning of this TCP connection");
909 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
910 time_orig_box = gtk_vbox_new (TRUE, 0);
911 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
912 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
913 time_orig_frame = gtk_frame_new ("Time origin");
914 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
915 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
917 /* sequence number origin group */
919 gtk_radio_button_new_with_label (NULL, "initial sequence number");
920 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
921 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
922 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
923 seq_orig_box = gtk_vbox_new (TRUE, 0);
924 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
925 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
926 seq_orig_frame = gtk_frame_new ("Sequence number origin");
927 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
928 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
930 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
931 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
933 SIGNAL_CONNECT(time_orig_conn, "toggled", callback_time_origin, g);
934 SIGNAL_CONNECT(seq_orig_isn, "toggled", callback_seq_origin, g);
936 box = gtk_vbox_new (FALSE, 0);
937 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
938 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
939 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
940 gtk_widget_show (box);
941 label = gtk_label_new ("Origin");
942 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
945 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
947 GtkWidget *cross_frame, *label;
949 cross_frame = control_panel_create_cross_group (g);
950 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
951 label = gtk_label_new ("Cross");
952 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
955 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
957 GtkWidget *frame, *label;
959 frame = control_panel_create_graph_type_group (g);
960 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
961 label = gtk_label_new ("Graph type");
962 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
965 /* Treat this as a cancel, by calling "callback_close()" */
967 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
970 callback_close(NULL, data);
974 static void callback_close (GtkWidget *widget _U_, gpointer data)
976 struct graph *g = (struct graph * )data;
978 if (!(g->flags & GRAPH_DESTROYED)) {
979 g->flags |= GRAPH_DESTROYED;
980 graph_destroy ((struct graph * )data);
984 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
986 GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
987 #if GTK_MAJOR_VERSION < 2
988 struct graph *g = (struct graph * )data;
993 toplevel = dlg_window_new ("Help for TCP graphing");
994 gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
996 vbox = gtk_vbox_new (FALSE, 3);
997 gtk_container_border_width(GTK_CONTAINER(vbox), 12);
998 gtk_container_add (GTK_CONTAINER (toplevel), vbox);
1000 scroll = scrolled_window_new (NULL, NULL);
1001 #if GTK_MAJOR_VERSION >= 2
1002 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1005 gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
1006 #if GTK_MAJOR_VERSION < 2
1007 text = gtk_text_new (NULL, NULL);
1008 gtk_text_set_editable (GTK_TEXT (text), FALSE);
1009 gtk_text_set_line_wrap (GTK_TEXT (text), FALSE);
1010 gtk_text_set_word_wrap (GTK_TEXT (text), FALSE);
1011 gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1);
1013 text = gtk_text_view_new();
1014 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1015 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1016 gtk_text_buffer_set_text(buf, helptext, -1);
1018 gtk_container_add (GTK_CONTAINER (scroll), text);
1021 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1022 gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
1023 gtk_widget_show(bbox);
1025 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
1026 window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
1028 SIGNAL_CONNECT(toplevel, "delete_event", window_delete_event_cb, NULL);
1030 gtk_widget_show_all (toplevel);
1031 window_present(toplevel);
1034 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1036 toggle_time_origin ((struct graph * )data);
1039 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1041 toggle_seq_origin ((struct graph * )data);
1044 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1046 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1047 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1048 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1049 GtkWidget *zoom_v_step_label, *zoom_v_step;
1050 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1051 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1052 GtkWidget *zoom_h_entry, *zoom_v_entry;
1053 GtkWidget *zoom_h_label, *zoom_v_label;
1055 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1056 zoom_out = gtk_radio_button_new_with_label (
1057 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1058 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1059 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1060 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1061 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1063 zoom_separator1 = gtk_hseparator_new ();
1065 zoom_h_entry = gtk_entry_new ();
1066 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1067 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1068 zoom_h_label = gtk_label_new ("Horizontal:");
1070 zoom_v_entry = gtk_entry_new ();
1071 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1072 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1073 zoom_v_label = gtk_label_new ("Vertical:");
1075 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1076 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1078 zoom_table = gtk_table_new (2, 2, FALSE);
1079 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1080 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1081 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1082 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1083 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1084 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1085 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1086 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1088 zoom_separator2 = gtk_hseparator_new ();
1090 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1091 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1092 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1093 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1095 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1096 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1097 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1098 zoom_v_step_label = gtk_label_new ("Vertical step:");
1100 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1101 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1103 zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1104 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1105 OBJECT_SET_DATA(zoom_same_toggle, "flag", (gpointer)ZOOM_STEPS_SAME);
1106 OBJECT_SET_DATA(zoom_ratio_toggle, "flag",
1107 (gpointer)ZOOM_STEPS_KEEP_RATIO);
1108 SIGNAL_CONNECT(zoom_same_toggle, "clicked", callback_zoom_flags, g);
1109 SIGNAL_CONNECT(zoom_ratio_toggle, "clicked", callback_zoom_flags, g);
1111 zoom_step_table = gtk_table_new (4, 2, FALSE);
1112 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1113 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1114 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1115 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1116 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1117 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1118 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1119 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1120 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1121 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1122 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1123 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1125 zoom_box = gtk_vbox_new (FALSE, 0);
1126 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1127 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1128 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1129 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1130 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1131 zoom_frame = gtk_frame_new ("Zoom");
1132 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1134 OBJECT_SET_DATA(zoom_h_step, "direction", GINT_TO_POINTER(0));
1135 OBJECT_SET_DATA(zoom_v_step, "direction", GINT_TO_POINTER(1));
1137 SIGNAL_CONNECT(zoom_in, "toggled", callback_zoom_inout, g);
1138 SIGNAL_CONNECT(zoom_h_step, "changed", callback_zoom_step, g);
1139 SIGNAL_CONNECT(zoom_v_step, "changed", callback_zoom_step, g);
1141 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1142 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1146 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1148 struct graph *g = (struct graph * )data;
1150 if (GTK_TOGGLE_BUTTON (toggle)->active)
1151 g->zoom.flags &= ~ZOOM_OUT;
1153 g->zoom.flags |= ZOOM_OUT;
1156 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1158 struct graph *g = (struct graph * )data;
1161 double *zoom_this, *zoom_other;
1162 GtkSpinButton *widget_this, *widget_other;
1165 direction = (int)OBJECT_GET_DATA(spin, "direction");
1166 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1169 zoom_this = &g->zoom.step_y;
1170 zoom_other = &g->zoom.step_x;
1171 widget_this = g->zoom.widget.v_step;
1172 widget_other = g->zoom.widget.h_step;
1174 zoom_this = &g->zoom.step_x;
1175 zoom_other = &g->zoom.step_y;
1176 widget_this = g->zoom.widget.h_step;
1177 widget_other = g->zoom.widget.v_step;
1180 old_this = *zoom_this;
1182 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1183 *zoom_other = value;
1184 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1185 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1186 double old_other = *zoom_other;
1187 *zoom_other *= value / old_this;
1188 if (*zoom_other < 1.0) {
1190 *zoom_this = old_this * 1.0 / old_other;
1191 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1192 } else if (*zoom_other > 5.0) {
1194 *zoom_this = old_this * 5.0 / old_other;
1195 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1197 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1201 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1203 struct graph *g = (struct graph * )data;
1204 int flag = (int)OBJECT_GET_DATA(toggle, "flag");
1206 if (GTK_TOGGLE_BUTTON (toggle)->active)
1207 g->zoom.flags |= flag;
1209 g->zoom.flags &= ~flag;
1212 static void update_zoom_spins (struct graph *g)
1216 g_snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
1217 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1218 g_snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
1219 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1222 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1224 GtkWidget *mag_width_label, *mag_width;
1225 GtkWidget *mag_height_label, *mag_height;
1226 GtkWidget *mag_x_label, *mag_x;
1227 GtkWidget *mag_y_label, *mag_y;
1228 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1229 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1230 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1231 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1232 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1233 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1234 GtkWidget *mag_box, *mag_frame;
1236 mag_width_label = gtk_label_new ("Width:");
1237 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1238 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1240 mag_height_label = gtk_label_new ("Height:");
1241 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1242 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1244 mag_x_label = gtk_label_new ("X:");
1245 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1246 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1248 mag_y_label = gtk_label_new ("Y:");
1249 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1250 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1252 mag_wh_table = gtk_table_new (4, 2, FALSE);
1253 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1254 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1255 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1256 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1257 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1258 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1259 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1260 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1261 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1262 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1263 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1264 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1265 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1266 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1267 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1268 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1270 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1271 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1272 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1274 mag_v_zoom_label = gtk_label_new ("Vertical:");
1275 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1276 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1278 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1279 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1281 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1282 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1283 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1284 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1285 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1286 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1287 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1288 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1289 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1290 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1291 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1292 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1293 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1295 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1296 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1297 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1299 mag_box = gtk_vbox_new (FALSE, 0);
1300 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1301 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1302 mag_frame = gtk_frame_new ("Magnify");
1303 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1305 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1306 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1307 OBJECT_SET_DATA(mag_h_zoom, "direction", GINT_TO_POINTER(0));
1308 OBJECT_SET_DATA(mag_v_zoom, "direction", GINT_TO_POINTER(1));
1309 OBJECT_SET_DATA(mag_zoom_same, "flag", (gpointer)MAGZOOMS_SAME);
1310 OBJECT_SET_DATA(mag_zoom_ratio, "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1312 SIGNAL_CONNECT(mag_width, "changed", callback_mag_width, g);
1313 SIGNAL_CONNECT(mag_height, "changed", callback_mag_height, g);
1314 SIGNAL_CONNECT(mag_x, "changed", callback_mag_x, g);
1315 SIGNAL_CONNECT(mag_y, "changed", callback_mag_y, g);
1316 SIGNAL_CONNECT(mag_h_zoom, "changed", callback_mag_zoom, g);
1317 SIGNAL_CONNECT(mag_v_zoom, "changed", callback_mag_zoom, g);
1318 SIGNAL_CONNECT(mag_zoom_same, "clicked", callback_mag_flags, g);
1319 SIGNAL_CONNECT(mag_zoom_ratio, "clicked", callback_mag_flags, g);
1324 static void callback_mag_width (GtkWidget *spin, gpointer data)
1326 struct graph *g = (struct graph * )data;
1328 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1331 static void callback_mag_height (GtkWidget *spin, gpointer data)
1333 struct graph *g = (struct graph * )data;
1335 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1338 static void callback_mag_x (GtkWidget *spin, gpointer data)
1340 struct graph *g = (struct graph * )data;
1342 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1345 static void callback_mag_y (GtkWidget *spin, gpointer data)
1347 struct graph *g = (struct graph * )data;
1349 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1352 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1354 struct graph *g = (struct graph * )data;
1357 double *zoom_this, *zoom_other;
1358 GtkSpinButton *widget_this, *widget_other;
1361 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1362 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1363 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1366 direction = (int)OBJECT_GET_DATA(spin, "direction");
1367 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1370 zoom_this = &g->magnify.zoom.y;
1371 zoom_other = &g->magnify.zoom.x;
1372 widget_this = g->magnify.widget.v_zoom;
1373 widget_other = g->magnify.widget.h_zoom;
1375 zoom_this = &g->magnify.zoom.x;
1376 zoom_other = &g->magnify.zoom.y;
1377 widget_this = g->magnify.widget.h_zoom;
1378 widget_other = g->magnify.widget.v_zoom;
1381 old_this = *zoom_this;
1383 if (g->magnify.flags & MAGZOOMS_SAME) {
1384 *zoom_other = value;
1385 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1386 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1387 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1388 double old_other = *zoom_other;
1389 *zoom_other *= value / old_this;
1390 if (*zoom_other < 1.0) {
1392 *zoom_this = old_this * 1.0 / old_other;
1393 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1394 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1395 } else if (*zoom_other > 25.0) {
1397 *zoom_this = old_this * 25.0 / old_other;
1398 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1399 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1401 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1402 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1406 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1408 struct graph *g = (struct graph * )data;
1409 int flag = (int)OBJECT_GET_DATA(toggle, "flag");
1411 if (GTK_TOGGLE_BUTTON (toggle)->active)
1412 g->magnify.flags |= flag;
1414 g->magnify.flags &= ~flag;
1417 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1419 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1420 GtkWidget *zoom_lock_frame;
1422 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1423 zoom_lock_h = gtk_radio_button_new_with_label (
1424 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1426 zoom_lock_v = gtk_radio_button_new_with_label (
1427 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1429 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1430 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1431 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1433 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1434 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1435 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1436 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1438 SIGNAL_CONNECT(zoom_lock_h, "toggled", callback_zoomlock_h, g);
1439 SIGNAL_CONNECT(zoom_lock_v, "toggled", callback_zoomlock_v, g);
1441 return zoom_lock_frame;
1444 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1446 struct graph *g = (struct graph * )data;
1448 if (GTK_TOGGLE_BUTTON (toggle)->active)
1449 g->zoom.flags |= ZOOM_HLOCK;
1451 g->zoom.flags &= ~ZOOM_HLOCK;
1454 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1456 struct graph *g = (struct graph * )data;
1458 if (GTK_TOGGLE_BUTTON (toggle)->active)
1459 g->zoom.flags |= ZOOM_VLOCK;
1461 g->zoom.flags &= ~ZOOM_VLOCK;
1464 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1466 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1468 label = gtk_label_new ("Crosshairs:");
1469 off = gtk_radio_button_new_with_label (NULL, "off");
1470 on = gtk_radio_button_new_with_label (
1471 gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on");
1472 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1473 box = gtk_hbox_new (FALSE, 0);
1474 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1475 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1476 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1477 vbox = gtk_vbox_new (FALSE, 0);
1478 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1479 /* frame = gtk_frame_new ("Cross:"); */
1480 frame = gtk_frame_new (NULL);
1481 gtk_container_add (GTK_CONTAINER (frame), vbox);
1483 SIGNAL_CONNECT(on, "toggled", callback_cross_on_off, g);
1485 g->cross.on_toggle = (GtkToggleButton * )on;
1486 g->cross.off_toggle = (GtkToggleButton * )off;
1491 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1493 struct graph *g = (struct graph * )data;
1495 if (GTK_TOGGLE_BUTTON (toggle)->active) {
1497 g->cross.draw = TRUE;
1498 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
1499 cross_draw (g, x, y);
1501 g->cross.draw = FALSE;
1506 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1508 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1509 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1510 GtkWidget *graph_frame;
1512 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1513 graph_tseqttrace = gtk_radio_button_new_with_label (
1514 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1515 "Time/Sequence (tcptrace-style)");
1516 graph_tseqstevens = gtk_radio_button_new_with_label (
1517 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1518 "Time/Sequence (Stevens'-style)");
1519 graph_rtt = gtk_radio_button_new_with_label (
1520 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1523 case GRAPH_TSEQ_STEVENS:
1524 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1526 case GRAPH_TSEQ_TCPTRACE:
1527 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1529 case GRAPH_THROUGHPUT:
1530 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1533 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1536 graph_init = gtk_check_button_new_with_label ("Init on change");
1537 graph_sep = gtk_hseparator_new ();
1538 graph_box = gtk_vbox_new (FALSE, 0);
1539 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1540 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1541 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1542 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1543 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1544 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1545 graph_frame = gtk_frame_new ("Graph type:");
1546 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1548 OBJECT_SET_DATA(graph_tseqstevens, "new-graph-type",
1549 GINT_TO_POINTER(0));
1550 OBJECT_SET_DATA(graph_tseqttrace, "new-graph-type", GINT_TO_POINTER(1));
1551 OBJECT_SET_DATA(graph_tput, "new-graph-type", GINT_TO_POINTER(2));
1552 OBJECT_SET_DATA(graph_rtt, "new-graph-type", GINT_TO_POINTER(3));
1554 SIGNAL_CONNECT(graph_tseqttrace, "toggled", callback_graph_type, g);
1555 SIGNAL_CONNECT(graph_tseqstevens, "toggled", callback_graph_type, g);
1556 SIGNAL_CONNECT(graph_tput, "toggled", callback_graph_type, g);
1557 SIGNAL_CONNECT(graph_rtt, "toggled", callback_graph_type, g);
1558 SIGNAL_CONNECT(graph_init, "toggled", callback_graph_init_on_typechg,
1564 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1566 int old_type, new_type;
1567 struct graph *g = (struct graph * )data;
1569 new_type = (int)OBJECT_GET_DATA(toggle,"new-graph-type");
1571 if (!GTK_TOGGLE_BUTTON (toggle)->active)
1577 graph_element_lists_free (g);
1578 graph_element_lists_initialize (g);
1580 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1581 /* throughput graph uses differently constructed segment list so we
1582 * need to recreate it */
1583 graph_segment_list_free (g);
1584 graph_segment_list_get (g);
1587 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1588 g->geom.width = g->wp.width;
1589 g->geom.height = g->wp.height;
1590 g->geom.x = g->wp.x;
1591 g->geom.y = g->wp.y;
1593 g->x_axis->min = g->y_axis->min = 0;
1594 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1595 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1596 graph_init_sequence (g);
1599 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1601 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1604 static struct graph *graph_new (void)
1608 g = (struct graph * )g_malloc0 (sizeof (struct graph));
1609 graph_element_lists_initialize (g);
1611 g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1612 g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1614 g->x_axis->flags = 0;
1615 g->x_axis->flags |= AXIS_ORIENTATION;
1616 g->x_axis->s.x = g->x_axis->s.y = 0;
1617 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1618 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1619 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1621 g->y_axis->flags = 0;
1622 g->y_axis->flags &= ~AXIS_ORIENTATION;
1623 g->y_axis->p.x = g->y_axis->p.y = 0;
1624 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1626 g->y_axis->s.y = TITLEBAR_HEIGHT;
1627 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1632 static void graph_initialize_values (struct graph *g)
1634 g->geom.width = g->wp.width = 750;
1635 g->geom.height = g->wp.height = 550;
1636 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1637 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1639 /* g->zoom.x = g->zoom.y = 1.0; */
1640 g->zoom.step_x = g->zoom.step_y = 1.2;
1642 g->cross.draw = g->cross.erase_needed = 0;
1643 g->grab.grabbed = 0;
1644 g->magnify.active = 0;
1645 g->magnify.offset.x = g->magnify.offset.y = 0;
1646 g->magnify.width = g->magnify.height = 250;
1647 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1648 g->magnify.flags = 0;
1651 static void graph_init_sequence (struct graph *g)
1653 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1655 graph_type_dependent_initialize (g);
1656 g->zoom.initial.x = g->zoom.x;
1657 g->zoom.initial.y = g->zoom.y;
1658 graph_element_lists_make (g);
1659 g->x_axis->s.width = g->wp.width;
1660 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1661 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1662 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1663 g->y_axis->s.height = g->wp.height;
1664 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1665 graph_pixmaps_create (g);
1666 axis_pixmaps_create (g->y_axis);
1667 axis_pixmaps_create (g->x_axis);
1668 graph_title_pixmap_create (g);
1669 graph_title_pixmap_draw (g);
1670 graph_title_pixmap_display (g);
1672 axis_display (g->y_axis);
1673 axis_display (g->x_axis);
1676 static void graph_type_dependent_initialize (struct graph *g)
1679 case GRAPH_TSEQ_STEVENS:
1680 case GRAPH_TSEQ_TCPTRACE:
1681 tseq_initialize (g);
1683 case GRAPH_THROUGHPUT:
1684 tput_initialize (g);
1694 static void graph_destroy (struct graph *g)
1696 debug(DBS_FENTRY) puts ("graph_destroy()");
1698 axis_destroy (g->x_axis);
1699 axis_destroy (g->y_axis);
1700 /* window_destroy (g->drawing_area); */
1701 window_destroy (g->gui.control_panel);
1702 window_destroy (g->toplevel);
1703 /* window_destroy (g->text); */
1704 gdk_gc_unref (g->fg_gc);
1705 gdk_gc_unref (g->bg_gc);
1706 #if GTK_MAJOR_VERSION < 2
1707 gdk_font_unref (g->font);
1709 gdk_pixmap_unref (g->pixmap[0]);
1710 gdk_pixmap_unref (g->pixmap[1]);
1713 g_free ( (gpointer) (g->title) );
1714 graph_segment_list_free (g);
1715 graph_element_lists_free (g);
1721 typedef struct _tcp_scan_t {
1722 struct segment *current;
1725 struct segment *last;
1729 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
1731 static struct segment *segment=NULL;
1732 tcp_scan_t *ts=(tcp_scan_t *)pct;
1733 struct tcpheader *tcphdr=(struct tcpheader *)vip;
1736 segment=g_malloc(sizeof (struct segment));
1738 perror ("malloc failed");
1743 if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
1744 ts->current->th_sport, ts->current->th_dport,
1745 &tcphdr->ip_src, &tcphdr->ip_dst,
1746 tcphdr->th_sport, tcphdr->th_dport,
1748 segment->next = NULL;
1749 segment->num = pinfo->fd->num;
1750 segment->rel_secs = pinfo->fd->rel_ts.secs;
1751 segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
1752 segment->abs_secs = pinfo->fd->abs_ts.secs;
1753 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
1754 segment->th_seq=tcphdr->th_seq;
1755 segment->th_ack=tcphdr->th_ack;
1756 segment->th_win=tcphdr->th_win;
1757 segment->th_flags=tcphdr->th_flags;
1758 segment->th_sport=tcphdr->th_sport;
1759 segment->th_dport=tcphdr->th_dport;
1760 segment->th_seglen=tcphdr->th_seglen;
1761 COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
1762 COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
1763 if (ts->g->segments) {
1764 ts->last->next = segment;
1766 ts->g->segments = segment;
1769 if(pinfo->fd->num==ts->current->num){
1770 ts->g->current = segment;
1781 /* here we collect all the external data we will ever need */
1782 static void graph_segment_list_get (struct graph *g)
1784 struct segment current;
1785 GString *error_string;
1789 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1790 select_tcpip_session (&cfile, ¤t);
1791 if (g->type == GRAPH_THROUGHPUT)
1792 ts.direction = COMPARE_CURR_DIR;
1794 ts.direction = COMPARE_ANY_DIR;
1796 /* rescan all the packets and pick up all interesting tcp headers.
1797 * we only filter for TCP here for speed and do the actual compare
1798 * in the tap listener
1800 ts.current=¤t;
1803 error_string=register_tap_listener("tcp", &ts, "tcp", NULL, tapall_tcpip_packet, NULL);
1805 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1807 g_string_free(error_string, TRUE);
1810 cf_retap_packets(&cfile, FALSE);
1811 remove_tap_listener(&ts);
1815 typedef struct _th_t {
1817 struct tcpheader *tcphdr;
1821 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
1826 th->tcphdr=(struct tcpheader *)vip;
1833 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
1834 * then present the user with a dialog where the user can select WHICH tcp
1837 static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
1842 epan_dissect_t *edt;
1844 GString *error_string;
1845 th_t th = {0, NULL};
1847 fdata = cf->current_frame;
1849 /* no real filter yet */
1850 if (!dfilter_compile("tcp", &sfcode)) {
1851 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
1855 /* dissect the current frame */
1856 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
1857 cf->pd, fdata->cap_len, &err, &err_info)) {
1858 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1859 cf_read_error_message(err, err_info), cf->filename);
1864 error_string=register_tap_listener("tcp", &th, NULL, NULL, tap_tcpip_packet, NULL);
1866 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1868 g_string_free(error_string, TRUE);
1872 edt = epan_dissect_new(TRUE, FALSE);
1873 epan_dissect_prime_dfilter(edt, sfcode);
1874 tap_queue_init(edt);
1875 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
1876 tap_push_tapped_queue(edt);
1877 epan_dissect_free(edt);
1878 remove_tap_listener(&th);
1881 /* This "shouldn't happen", as our menu items shouldn't
1882 * even be enabled if the selected packet isn't a TCP
1883 * segment, as tcp_graph_selected_packet_enabled() is used
1884 * to determine whether to enable any of our menu items. */
1885 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1886 "Selected packet isn't a TCP segment");
1889 /* XXX fix this later, we should show a dialog allowing the user
1890 to select which session he wants here
1893 /* can only handle a single tcp layer yet */
1894 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1895 "The selected packet has more than one TCP"
1900 hdrs->num = fdata->num;
1901 hdrs->rel_secs = fdata->rel_ts.secs;
1902 hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
1903 hdrs->abs_secs = fdata->abs_ts.secs;
1904 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
1905 hdrs->th_seq=th.tcphdr->th_seq;
1906 hdrs->th_ack=th.tcphdr->th_ack;
1907 hdrs->th_win=th.tcphdr->th_win;
1908 hdrs->th_flags=th.tcphdr->th_flags;
1909 hdrs->th_sport=th.tcphdr->th_sport;
1910 hdrs->th_dport=th.tcphdr->th_dport;
1911 hdrs->th_seglen=th.tcphdr->th_seglen;
1912 COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
1913 COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
1918 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
1922 dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
1923 (!(CMP_ADDRESS(daddr1, daddr2))) &&
1927 if(dir==COMPARE_CURR_DIR){
1930 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
1931 (!(CMP_ADDRESS(daddr1, saddr2))) &&
1934 return dir1 || dir2;
1938 static void graph_segment_list_free (struct graph *g)
1940 struct segment *segment;
1942 while (g->segments) {
1943 segment = g->segments->next;
1944 g_free (g->segments);
1945 g->segments = segment;
1950 static void graph_element_lists_initialize (struct graph *g)
1952 g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
1955 static void graph_element_lists_make (struct graph *g)
1957 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1960 case GRAPH_TSEQ_STEVENS:
1961 tseq_stevens_make_elmtlist (g);
1963 case GRAPH_TSEQ_TCPTRACE:
1964 tseq_tcptrace_make_elmtlist (g);
1966 case GRAPH_THROUGHPUT:
1967 tput_make_elmtlist (g);
1970 rtt_make_elmtlist (g);
1973 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1978 static void graph_element_lists_free (struct graph *g)
1980 struct element_list *list, *next_list;
1983 for (list=g->elists; list; list=list->next)
1984 g_free (list->elements);
1985 while (g->elists->next) {
1986 list = g->elists->next->next;
1987 g_free (g->elists->next);
1988 g->elists->next = list;
1992 for (list=g->elists; list; list=next_list) {
1993 g_free (list->elements);
1994 next_list = list->next;
1997 g->elists = NULL; /* just to make debugging easier */
2000 static void graph_title_pixmap_create (struct graph *g)
2002 if (g->title_pixmap)
2003 gdk_pixmap_unref (g->title_pixmap);
2005 g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
2006 g->x_axis->p.width, g->wp.y, -1);
2009 static void graph_title_pixmap_draw (struct graph *g)
2013 gdk_draw_rectangle(g->title_pixmap, g->bg_gc, TRUE, 0, 0,
2014 g->x_axis->p.width, g->wp.y);
2015 for (i=0; g->title[i]; i++) {
2017 #if GTK_MAJOR_VERSION < 2
2018 w = gdk_string_width(g->font, g->title[i]);
2019 h = gdk_string_height(g->font, g->title[i]);
2020 gdk_draw_string(g->title_pixmap, g->font, g->fg_gc,
2021 g->wp.width/2 - w/2, 20+h + i*(h+3),
2024 PangoLayout *layout;
2025 layout = gtk_widget_create_pango_layout(g->drawing_area,
2027 pango_layout_get_pixel_size(layout, &w, &h);
2028 gdk_draw_layout(g->title_pixmap, g->fg_gc,
2029 g->wp.width/2 - w/2, 20 + i*(h+3), layout);
2030 g_object_unref(G_OBJECT(layout));
2035 static void graph_title_pixmap_display (struct graph *g)
2037 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
2038 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
2041 static void graph_pixmaps_create (struct graph *g)
2043 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
2046 gdk_pixmap_unref (g->pixmap[0]);
2048 gdk_pixmap_unref (g->pixmap[1]);
2050 g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
2051 g->wp.width, g->wp.height, -1);
2052 g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
2053 g->wp.width, g->wp.height, -1);
2058 static void graph_display (struct graph *g)
2060 graph_pixmap_draw (g);
2061 graph_pixmaps_switch (g);
2062 graph_pixmap_display (g);
2065 static void graph_pixmap_display (struct graph *g)
2067 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
2068 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
2069 g->wp.width, g->wp.height);
2070 if (g->cross.erase_needed) {
2071 cross_xor(g, g->cross.x, g->cross.y);
2075 static void graph_pixmaps_switch (struct graph *g)
2077 g->displayed = 1 ^ g->displayed;
2080 static void graph_pixmap_draw (struct graph *g)
2082 struct element_list *list;
2086 debug(DBS_FENTRY) puts ("graph_display()");
2087 not_disp = 1 ^ g->displayed;
2089 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
2090 0, 0, g->wp.width, g->wp.height);
2092 for (list=g->elists; list; list=list->next)
2093 for (e=list->elements; e->type != ELMT_NONE; e++) {
2098 draw_element_line (g, e);
2101 draw_element_arc (g, e);
2109 static void draw_element_line (struct graph *g, struct element *e)
2113 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2114 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2115 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2116 x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2117 x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2118 y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2119 y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2130 if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
2131 (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
2132 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2136 if (x2 > g->wp.width-1)
2140 if (y2 > g->wp.height-1)
2141 y2 = g->wp.height-1;
2144 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
2145 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
2148 static void draw_element_arc (struct graph *g, struct element *e)
2152 x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2153 x2 = (int )e->p.arc.dim.width;
2154 y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2155 y2 = (int )e->p.arc.dim.height;
2156 if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
2158 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
2159 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
2160 y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
2163 static void axis_pixmaps_create (struct axis *axis)
2165 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2166 if (axis->pixmap[0])
2167 gdk_pixmap_unref (axis->pixmap[0]);
2168 if (axis->pixmap[1])
2169 gdk_pixmap_unref (axis->pixmap[1]);
2171 axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
2172 axis->p.width, axis->p.height, -1);
2173 axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
2174 axis->p.width, axis->p.height, -1);
2176 axis->displayed = 0;
2179 static void axis_destroy (struct axis *axis)
2181 gdk_pixmap_unref (axis->pixmap[0]);
2182 gdk_pixmap_unref (axis->pixmap[1]);
2183 g_free ( (gpointer) (axis->label) );
2186 static void axis_display (struct axis *axis)
2188 if (axis->flags & AXIS_ORIENTATION)
2189 h_axis_pixmap_draw (axis);
2191 v_axis_pixmap_draw (axis);
2192 axis_pixmaps_switch (axis);
2193 axis_pixmap_display (axis);
2196 static void v_axis_pixmap_draw (struct axis *axis)
2198 struct graph *g = axis->g;
2201 int not_disp, rdigits, offset, imin, imax;
2202 double bottom, top, j, fl, corr;
2203 #if GTK_MAJOR_VERSION >= 2
2204 PangoLayout *layout;
2207 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2208 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2209 (double )g->geom.height * g->bounds.height;
2210 bottom += axis->min;
2211 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2212 (double )g->geom.height * g->bounds.height;
2214 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2216 j = axis->major - floor (axis->major);
2217 for (rdigits=0; rdigits<=6; rdigits++) {
2224 not_disp = 1 ^ axis->displayed;
2225 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2226 axis->p.width, axis->p.height);
2228 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2229 (gint) ((axis->p.height-axis->s.height)/2.0), axis->s.width - 1,
2232 offset = g->wp.y + (-g->geom.y);
2233 fl = floor (axis->min / axis->major) * axis->major;
2234 corr = rint ((axis->min - fl) * g->zoom.y);
2237 major_tick = axis->major * g->zoom.y;
2238 imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2239 imax = (int) ((g->geom.height - offset + corr) / major_tick);
2240 for (i=imin; i <= imax; i++) {
2243 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2244 offset + corr + axis->s.y);
2246 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2247 i*axis->major + fl, y);
2248 if (y < 0 || y > axis->p.height)
2250 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2251 axis->s.width - 15, y, axis->s.width - 1, y);
2252 g_snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2253 #if GTK_MAJOR_VERSION < 2
2254 w = gdk_string_width(g->font, desc);
2255 h = gdk_string_height(g->font, desc);
2256 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2257 axis->s.width-15-4-w, y + h/2, desc);
2259 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2260 pango_layout_get_pixel_size(layout, &w, &h);
2261 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2262 axis->s.width-14-4-w, y - h/2, layout);
2263 g_object_unref(G_OBJECT(layout));
2268 double minor_tick = axis->minor * g->zoom.y;
2269 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2270 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2271 for (i=imin; i <= imax; i++) {
2272 int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2273 offset + corr + axis->s.y);
2275 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2276 if (y > 0 && y < axis->p.height)
2277 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2278 axis->s.width - 8, y,
2279 axis->s.width - 1, y);
2282 for (i=0; axis->label[i]; i++) {
2284 #if GTK_MAJOR_VERSION < 2
2285 w = gdk_string_width (g->font, axis->label[i]);
2286 h = gdk_string_height (g->font, axis->label[i]);
2287 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2288 (axis->p.width - w)/2 ,
2289 TITLEBAR_HEIGHT-15 - i*(h+3), axis->label[i]);
2291 layout = gtk_widget_create_pango_layout(g->drawing_area,
2293 pango_layout_get_pixel_size(layout, &w, &h);
2294 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2295 (axis->p.width - w)/2,
2296 TITLEBAR_HEIGHT-10 - i*(h+3) - h,
2298 g_object_unref(G_OBJECT(layout));
2303 static void h_axis_pixmap_draw (struct axis *axis)
2305 struct graph *g = axis->g;
2307 double major_tick, minor_tick;
2308 int not_disp, rdigits, offset, imin, imax;
2309 double left, right, j, fl, corr;
2310 #if GTK_MAJOR_VERSION >= 2
2311 PangoLayout *layout;
2314 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2315 left = (g->wp.x-g->geom.x) /
2316 (double )g->geom.width * g->bounds.width;
2318 right = (g->wp.x-g->geom.x+g->wp.width) /
2319 (double )g->geom.width * g->bounds.width;
2321 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2323 j = axis->major - floor (axis->major);
2324 for (rdigits=0; rdigits<=6; rdigits++) {
2331 not_disp = 1 ^ axis->displayed;
2332 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2333 axis->p.width, axis->p.height);
2335 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2336 (gint) (axis->s.width + (axis->p.width-axis->s.width)/2.0), 0);
2337 offset = g->wp.x - g->geom.x;
2339 fl = floor (axis->min / axis->major) * axis->major;
2340 corr = rint ((axis->min - fl) * g->zoom.x);
2343 major_tick = axis->major*g->zoom.x;
2344 imin = (int) ((offset + corr) / major_tick + 1);
2345 imax = (int) ((offset + corr + axis->s.width) / major_tick);
2346 for (i=imin; i <= imax; i++) {
2349 int x = (int ) (rint (i * major_tick) - offset - corr);
2351 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2352 if (x < 0 || x > axis->s.width)
2354 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2355 g_snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2356 #if GTK_MAJOR_VERSION < 2
2357 w = gdk_string_width (g->font, desc);
2358 h = gdk_string_height (g->font, desc);
2359 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2360 x - w/2, 15+h+4, desc);
2362 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2363 pango_layout_get_pixel_size(layout, &w, &h);
2364 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2365 x - w/2, 15+4, layout);
2366 g_object_unref(G_OBJECT(layout));
2369 if (axis->minor > 0) {
2371 minor_tick = axis->minor*g->zoom.x;
2372 imin = (int) ((offset + corr) / minor_tick + 1);
2373 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2374 for (i=imin; i <= imax; i++) {
2375 int x = (int) (rint (i * minor_tick) - offset - corr);
2376 if (x > 0 && x < axis->s.width)
2377 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2380 for (i=0; axis->label[i]; i++) {
2382 #if GTK_MAJOR_VERSION < 2
2383 w = gdk_string_width (g->font, axis->label[i]);
2384 h = gdk_string_height (g->font, axis->label[i]);
2385 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2386 axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
2389 layout = gtk_widget_create_pango_layout(g->drawing_area,
2391 pango_layout_get_pixel_size(layout, &w, &h);
2392 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2393 axis->s.width - w - 50, 15+h+15 + i*(h+3),
2395 g_object_unref(G_OBJECT(layout));
2400 static void axis_pixmaps_switch (struct axis *axis)
2402 axis->displayed = 1 ^ axis->displayed;
2405 static void axis_pixmap_display (struct axis *axis)
2407 gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
2408 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2409 axis->p.width, axis->p.height);
2412 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2414 int i, j, ii, jj, ms;
2415 double zoom, x, steps[3]={ 0.1, 0.5 };
2416 int dim, check_needed, diminished;
2417 double majthresh[2]={2.0, 3.0};
2419 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2420 debug(DBS_AXES_TICKS)
2421 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2423 zoom = axis_zoom_get (axis, dir);
2425 for (i=-9; i<=12; i++) {
2426 if (x / pow (10, i) < 1)
2430 ms = (int )(x / pow (10, i));
2440 axis->major = steps[j] * pow (10, i);
2442 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2443 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2445 /* let's compute minor ticks */
2448 axis_ticks_down (&ii, &jj);
2449 axis->minor = steps[jj] * pow (10, ii);
2450 /* we don't want minors if they would be less than 10 pixels apart */
2451 if (axis->minor*zoom < 10) {
2452 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2453 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2457 check_needed = TRUE;
2459 while (check_needed) {
2460 check_needed = FALSE;
2461 dim = get_label_dim (axis, dir, xmax);
2462 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2463 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2464 axis->major, axis->minor, axis->major*zoom/dim,
2465 axis->minor*zoom/dim);
2467 /* corrections: if majors are less than majthresh[dir] times label
2468 * dimension apart, we need to use bigger ones */
2469 if (axis->major*zoom / dim < majthresh[dir]) {
2470 axis_ticks_up (&ii, &jj);
2471 axis->minor = axis->major;
2472 axis_ticks_up (&i, &j);
2473 axis->major = steps[j] * pow (10, i);
2474 check_needed = TRUE;
2475 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2478 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2479 * we could promote them to majors as well */
2480 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2481 axis_ticks_down (&i, &j);
2482 axis->major = axis->minor;
2483 axis_ticks_down (&ii, &jj);
2484 axis->minor = steps[jj] * pow (10, ii);
2485 check_needed = TRUE;
2488 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2491 if (axis->minor*zoom < 10) {
2492 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2493 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2499 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2500 "axis->minor == %.1f\n", axis->major, axis->minor);
2503 static void axis_ticks_up (int *i, int *j)
2512 static void axis_ticks_down (int *i, int *j)
2521 static int get_label_dim (struct axis *axis, int dir, double label)
2526 #if GTK_MAJOR_VERSION >= 2
2527 PangoLayout *layout;
2530 /* First, let's compute how many digits to the right of radix
2531 * we need to print */
2532 y = axis->major - floor (axis->major);
2533 for (rdigits=0; rdigits<=6; rdigits++) {
2539 g_snprintf (str, 32, "%.*f", rdigits, label);
2541 case AXIS_HORIZONTAL:
2542 #if GTK_MAJOR_VERSION < 2
2543 dim = gdk_string_width(axis->g->font, str);
2545 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2547 pango_layout_get_pixel_size(layout, &dim, NULL);
2548 g_object_unref(G_OBJECT(layout));
2552 #if GTK_MAJOR_VERSION < 2
2553 dim = gdk_string_height(axis->g->font, str);
2555 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2557 pango_layout_get_pixel_size(layout, NULL, &dim);
2558 g_object_unref(G_OBJECT(layout));
2562 puts ("initialize axis: an axis must be either horizontal or vertical");
2569 static double axis_zoom_get (struct axis *axis, int dir)
2572 case AXIS_HORIZONTAL:
2573 return axis->g->zoom.x;
2576 return axis->g->zoom.y;
2584 static void graph_select_segment (struct graph *g, int x, int y)
2586 struct element_list *list;
2589 debug(DBS_FENTRY) puts ("graph_select_segment()");
2592 y = g->geom.height-1 - (y - g->geom.y);
2594 for (list=g->elists; list; list=list->next)
2595 for (e=list->elements; e->type != ELMT_NONE; e++) {
2600 if (line_detect_collision (e, x, y))
2601 cf_goto_frame(&cfile, e->parent->num);
2604 if (arc_detect_collision (e, x, y))
2605 cf_goto_frame(&cfile, e->parent->num);
2613 static int line_detect_collision (struct element *e, int x, int y)
2617 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2618 x1 = (int )rint (e->p.line.dim.x1);
2619 x2 = (int )rint (e->p.line.dim.x2);
2621 x1 = (int )rint (e->p.line.dim.x2);
2622 x2 = (int )rint (e->p.line.dim.x1);
2624 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2625 y1 = (int )rint (e->p.line.dim.y1);
2626 y2 = (int )rint (e->p.line.dim.y2);
2628 y1 = (int )rint (e->p.line.dim.y2);
2629 y2 = (int )rint (e->p.line.dim.y1);
2632 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2634 if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
2640 static int arc_detect_collision (struct element *e, int x, int y)
2644 x1 = (int )rint (e->p.arc.dim.x);
2645 x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2646 y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2647 y2 = (int )rint (e->p.arc.dim.y);
2649 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2651 if (x1<=x && x<=x2 && y1<=y && y<=y2)
2657 static void cross_xor (struct graph *g, int x, int y)
2659 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2660 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2661 gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
2662 y, g->wp.x + g->wp.width, y);
2663 gdk_draw_line (g->drawing_area->window, xor_gc, x,
2664 g->wp.y, x, g->wp.y + g->wp.height);
2668 static void cross_draw (struct graph *g, int x, int y)
2670 cross_xor (g, x, y);
2673 g->cross.erase_needed = 1;
2676 static void cross_erase (struct graph *g)
2678 cross_xor (g, g->cross.x, g->cross.y);
2679 g->cross.erase_needed = 0;
2682 static void magnify_create (struct graph *g, int x, int y)
2685 struct element_list *list, *new_list;
2686 struct ipoint pos, offsetpos;
2689 mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
2690 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2692 mg->toplevel = dlg_window_new("tcp graph magnify");
2693 mg->drawing_area = mg->toplevel;
2694 gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2695 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2696 /* | GDK_ENTER_NOTIFY_MASK */
2697 /* | GDK_ALL_EVENTS_MASK */
2702 mg->wp.width = g->magnify.width;
2703 mg->wp.height = g->magnify.height;
2704 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2705 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2706 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2707 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2709 /* in order to keep original element lists intact we need our own */
2710 graph_element_lists_initialize (mg);
2711 list = g->elists->next;
2712 new_list = mg->elists;
2713 for ( ; list; list=list->next) {
2715 (struct element_list * )g_malloc (sizeof (struct element_list));
2716 new_list = new_list->next;
2717 new_list->next = NULL;
2718 new_list->elements = NULL;
2720 graph_element_lists_make (mg);
2722 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2723 g->magnify.x = pos.x + x - g->magnify.width/2;
2724 g->magnify.y = pos.y + y - g->magnify.height/2;
2725 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2726 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2727 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2728 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2729 gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
2730 magnify_get_geom (g, x, y);
2732 gtk_widget_show (mg->drawing_area);
2734 /* we need to wait for the first expose event before we start drawing */
2735 while (!gdk_events_pending ());
2737 e = gdk_event_get ();
2739 if (e->any.type == GDK_EXPOSE) {
2747 mg->pixmap[0] = mg->pixmap[1] = NULL;
2748 graph_pixmaps_create (mg);
2750 g->magnify.active = 1;
2753 static void magnify_move (struct graph *g, int x, int y)
2755 struct ipoint pos, offsetpos;
2757 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2758 g->magnify.x = pos.x + x - g->magnify.width/2;
2759 g->magnify.y = pos.y + y - g->magnify.height/2;
2760 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2761 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2762 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2763 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2764 magnify_get_geom (g, x, y);
2765 gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
2770 static void magnify_destroy (struct graph *g)
2772 struct element_list *list;
2773 struct graph *mg = g->magnify.g;
2775 window_destroy (GTK_WIDGET (mg->drawing_area));
2776 gdk_pixmap_unref (mg->pixmap[0]);
2777 gdk_pixmap_unref (mg->pixmap[1]);
2778 for (list=mg->elists; list; list=list->next)
2779 g_free (list->elements);
2780 while (mg->elists->next) {
2781 list = mg->elists->next->next;
2782 g_free (mg->elists->next);
2783 mg->elists->next = list;
2785 g_free (g->magnify.g);
2786 g->magnify.active = 0;
2789 static void magnify_get_geom (struct graph *g, int x, int y)
2793 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
2795 g->magnify.g->geom.x = g->geom.x;
2796 g->magnify.g->geom.y = g->geom.y;
2798 g->magnify.g->geom.x -=
2799 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2800 ((x-g->geom.x)/(double )g->geom.width));
2801 g->magnify.g->geom.y -=
2802 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2803 ((y-g->geom.y)/(double )g->geom.height));
2805 /* we have coords of origin of graph relative to origin of g->toplevel.
2806 * now we need them to relate to origin of magnify window */
2807 g->magnify.g->geom.x -= (g->magnify.x - posx);
2808 g->magnify.g->geom.y -= (g->magnify.y - posy);
2811 static void magnify_draw (struct graph *g)
2813 int not_disp = 1 ^ g->magnify.g->displayed;
2815 graph_pixmap_draw (g->magnify.g);
2816 /* graph pixmap is almost ready, just add border */
2817 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2818 g->magnify.width - 1, 0);
2819 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2820 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2821 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2822 0, g->magnify.height - 1);
2823 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2824 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2826 graph_pixmaps_switch (g->magnify.g);
2827 graph_pixmap_display (g->magnify.g);
2831 static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
2833 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
2837 int cur_g_width, cur_g_height;
2838 int cur_wp_width, cur_wp_height;
2840 debug(DBS_FENTRY) puts ("configure_event()");
2842 cur_wp_width = g->wp.width;
2843 cur_wp_height = g->wp.height;
2844 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2845 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2846 g->x_axis->s.width = g->wp.width;
2847 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2848 g->y_axis->p.height = g->wp.height + g->wp.y;
2849 g->y_axis->s.height = g->wp.height;
2850 g->x_axis->p.y = g->y_axis->p.height;
2851 zoom.x = (double )g->wp.width / cur_wp_width;
2852 zoom.y = (double )g->wp.height / cur_wp_height;
2853 cur_g_width = g->geom.width;
2854 cur_g_height = g->geom.height;
2855 g->geom.width = (int )rint (g->geom.width * zoom.x);
2856 g->geom.height = (int )rint (g->geom.height * zoom.y);
2857 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2858 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2859 /* g->zoom.initial.x = g->zoom.x; */
2860 /* g->zoom.initial.y = g->zoom.y; */
2862 g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
2863 (g->wp.x - g->geom.x));
2864 g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
2865 (g->wp.y - g->geom.y));
2867 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2868 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2869 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2870 g->zoom.x, g->zoom.y);
2873 update_zoom_spins (g);
2874 graph_element_lists_make (g);
2875 graph_pixmaps_create (g);
2876 graph_title_pixmap_create (g);
2877 axis_pixmaps_create (g->y_axis);
2878 axis_pixmaps_create (g->x_axis);
2879 /* we don't do actual drawing here; we leave it to expose handler */
2880 graph_pixmap_draw (g);
2881 graph_pixmaps_switch (g);
2882 graph_title_pixmap_draw (g);
2883 h_axis_pixmap_draw (g->x_axis);
2884 axis_pixmaps_switch (g->x_axis);
2885 v_axis_pixmap_draw (g->y_axis);
2886 axis_pixmaps_switch (g->y_axis);
2890 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
2892 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
2894 debug(DBS_FENTRY) puts ("expose_event()");
2899 /* lower left corner */
2900 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
2901 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2903 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
2904 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2906 graph_pixmap_display (g);
2907 graph_title_pixmap_display (g);
2908 axis_pixmap_display (g->x_axis);
2909 axis_pixmap_display (g->y_axis);
2914 static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
2916 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
2918 debug(DBS_FENTRY) puts ("button_press_event()");
2920 if (event->button == 3) {
2921 if (event->state & GDK_CONTROL_MASK)
2922 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
2924 g->grab.x = (int )rint (event->x) - g->geom.x;
2925 g->grab.y = (int )rint (event->y) - g->geom.y;
2926 g->grab.grabbed = TRUE;
2929 /* Windows mouse control: */
2930 /* [<ctrl>-left] - select packet */
2931 /* [left] - zoom in */
2932 /* [<shift>-left] - zoom out */
2933 } else if (event->button == 1) {
2934 if (event->state & GDK_CONTROL_MASK) {
2935 graph_select_segment (g, (int)event->x, (int)event->y);
2938 } else if (event->button == 2) {
2940 int cur_width = g->geom.width, cur_height = g->geom.height;
2941 struct { double x, y; } factor;
2943 if (g->zoom.flags & ZOOM_OUT) {
2944 if (g->zoom.flags & ZOOM_HLOCK)
2947 factor.x = 1 / g->zoom.step_x;
2948 if (g->zoom.flags & ZOOM_VLOCK)
2951 factor.y = 1 / g->zoom.step_y;
2953 if (g->zoom.flags & ZOOM_HLOCK)
2956 factor.x = g->zoom.step_x;
2957 if (g->zoom.flags & ZOOM_VLOCK)
2960 factor.y = g->zoom.step_y;
2963 g->geom.width = (int )rint (g->geom.width * factor.x);
2964 g->geom.height = (int )rint (g->geom.height * factor.y);
2965 if (g->geom.width < g->wp.width)
2966 g->geom.width = g->wp.width;
2967 if (g->geom.height < g->wp.height)
2968 g->geom.height = g->wp.height;
2969 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2970 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2972 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2973 ((event->x-g->geom.x)/(double )cur_width));
2974 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2975 ((event->y-g->geom.y)/(double )cur_height));
2977 if (g->geom.x > g->wp.x)
2978 g->geom.x = g->wp.x;
2979 if (g->geom.y > g->wp.y)
2980 g->geom.y = g->wp.y;
2981 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2982 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2983 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2984 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2986 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2987 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2988 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2989 g->wp.height, g->zoom.x, g->zoom.y);
2991 graph_element_lists_make (g);
2992 g->cross.erase_needed = 0;
2994 axis_display (g->y_axis);
2995 axis_display (g->x_axis);
2996 update_zoom_spins (g);
2998 cross_draw (g, (int) event->x, (int) event->y);
3000 } else if (event->button == 1) {
3001 graph_select_segment (g, (int )event->x, (int )event->y);
3009 static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
3011 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3013 GdkModifierType state;
3015 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
3018 gdk_window_get_pointer (event->window, &x, &y, &state);
3022 state = event->state;
3025 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
3026 * is pressed while pointer is in motion, we will receive one more motion
3027 * notify *before* we get the button press. This last motion notify works
3028 * with stale grab coordinates */
3029 if (state & GDK_BUTTON3_MASK) {
3030 if (g->grab.grabbed) {
3031 g->geom.x = x-g->grab.x;
3032 g->geom.y = y-g->grab.y;
3034 if (g->geom.x > g->wp.x)
3035 g->geom.x = g->wp.x;
3036 if (g->geom.y > g->wp.y)
3037 g->geom.y = g->wp.y;
3038 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3039 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3040 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3041 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3042 g->cross.erase_needed = 0;
3044 axis_display (g->y_axis);
3045 axis_display (g->x_axis);
3047 cross_draw (g, x, y);
3048 } else if (g->magnify.active)
3049 magnify_move (g, x, y);
3050 } else if (state & GDK_BUTTON1_MASK) {
3051 graph_select_segment (g, x, y);
3052 if (g->cross.erase_needed)
3055 cross_draw (g, x, y);
3057 if (g->cross.erase_needed)
3060 cross_draw (g, x, y);
3066 static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
3068 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3070 debug(DBS_FENTRY) puts ("button_release_event()");
3072 if (event->button == 3)
3073 g->grab.grabbed = FALSE;
3075 if (g->magnify.active)
3076 magnify_destroy (g);
3080 static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
3082 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3084 debug(DBS_FENTRY) puts ("key_press_event()");
3086 if (event->keyval == 32 /*space*/) {
3089 if (g->cross.draw) {
3091 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3093 } else if (g->cross.erase_needed) {
3097 /* toggle buttons emit their "toggled" signals so don't bother doing
3098 * any real work here, it will be done in signal handlers */
3100 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
3102 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
3103 } else if (event->keyval == 't')
3104 toggle_time_origin (g);
3105 else if (event->keyval == 's')
3106 toggle_seq_origin (g);
3107 else if (event->keyval == GDK_Shift_L) {
3108 /* g->zoom.flags |= ZOOM_OUT; */
3109 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3114 static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
3116 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3118 debug(DBS_FENTRY) puts ("key_release_event()");
3120 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3121 /* g->zoom.flags &= ~ZOOM_OUT; */
3122 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3127 static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3129 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3131 if (g->cross.erase_needed)
3137 static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3139 struct graph *g = (struct graph *) OBJECT_GET_DATA(widget, "graph");
3141 /* graph_pixmap_display (g); */
3142 if (g->cross.draw) {
3144 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3145 cross_draw (g, x, y);
3150 static void toggle_time_origin (struct graph *g)
3153 case GRAPH_TSEQ_STEVENS:
3154 tseq_stevens_toggle_time_origin (g);
3156 case GRAPH_TSEQ_TCPTRACE:
3157 tseq_tcptrace_toggle_time_origin (g);
3159 case GRAPH_THROUGHPUT:
3160 tput_toggle_time_origin (g);
3165 axis_display (g->x_axis);
3168 static void toggle_seq_origin (struct graph *g)
3171 case GRAPH_TSEQ_STEVENS:
3172 tseq_stevens_toggle_seq_origin (g);
3173 axis_display (g->y_axis);
3175 case GRAPH_TSEQ_TCPTRACE:
3176 tseq_tcptrace_toggle_seq_origin (g);
3177 axis_display (g->y_axis);
3180 rtt_toggle_seq_origin (g);
3181 axis_display (g->x_axis);
3188 static int get_num_dsegs (struct graph *g)
3191 struct segment *tmp;
3193 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3194 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3195 g->current->th_sport, g->current->th_dport,
3196 &tmp->ip_src, &tmp->ip_dst,
3197 tmp->th_sport, tmp->th_dport,
3198 COMPARE_CURR_DIR)) {
3205 static int get_num_acks (struct graph *g)
3208 struct segment *tmp;
3210 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3211 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3212 g->current->th_sport, g->current->th_dport,
3213 &tmp->ip_src, &tmp->ip_dst,
3214 tmp->th_sport, tmp->th_dport,
3215 COMPARE_CURR_DIR)) {
3223 * Stevens-style time-sequence grapH
3226 static void tseq_stevens_read_config (struct graph *g)
3228 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3230 g->s.tseq_stevens.seq_width = 4;
3231 g->s.tseq_stevens.seq_height = 4;
3232 g->s.tseq_stevens.flags = 0;
3234 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3235 g->title[0] = "Time/Sequence Graph";
3237 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3238 g->y_axis->label[0] = "number[B]";
3239 g->y_axis->label[1] = "Sequence";
3240 g->y_axis->label[2] = NULL;
3241 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3242 g->x_axis->label[0] = "Time[s]";
3243 g->x_axis->label[1] = NULL;
3246 /* Used by both 'stevens' and 'tcptrace' */
3247 static void tseq_initialize (struct graph *g)
3249 debug(DBS_FENTRY) puts ("tseq_initialize()");
3250 tseq_get_bounds (g);
3256 case GRAPH_TSEQ_STEVENS:
3257 tseq_stevens_read_config(g);
3259 case GRAPH_TSEQ_TCPTRACE:
3260 tseq_tcptrace_read_config(g);
3266 /* Determine "bounds"
3267 * Essentially: look for lowest/highest time and seq in the list of segments
3268 * Note that for tcptrace the "(ack + window) sequence number" would normally be expected
3269 * to be the upper bound; However, just to be safe, include the data seg sequence numbers
3270 * in the comparison for tcptrace
3271 * (e.g. to handle the case of only data segments).
3274 /* ToDo: worry about handling cases such as trying to plot seq of just 1 frame */
3276 static void tseq_get_bounds (struct graph *g)
3278 struct segment *tmp;
3280 gboolean data_frame_seen=FALSE;
3281 double data_tim_low=0;
3282 double data_tim_high=0;
3283 guint32 data_seq_cur;
3284 guint32 data_seq_nxt;
3285 guint32 data_seq_low=0;
3286 guint32 data_seq_high=0;
3287 gboolean ack_frame_seen=FALSE;
3288 double ack_tim_low=0;
3289 double ack_tim_high=0;
3290 guint32 ack_seq_cur;
3291 guint32 ack_seq_low=0;
3292 guint32 win_seq_cur;
3293 guint32 win_seq_high=0;
3295 /* go thru all segments to determine "bounds" */
3296 for (tmp=g->segments; tmp; tmp=tmp->next) {
3297 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3298 g->current->th_sport, g->current->th_dport,
3299 &tmp->ip_src, &tmp->ip_dst,
3300 tmp->th_sport, tmp->th_dport,
3301 COMPARE_CURR_DIR)) {
3304 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3305 data_seq_cur = tmp->th_seq;
3306 data_seq_nxt = data_seq_cur + tmp->th_seglen;
3307 if (! data_frame_seen) {
3308 data_tim_low = data_tim_high = tim;
3309 data_seq_low = data_seq_cur;
3310 data_seq_high = data_seq_nxt;
3311 data_frame_seen = TRUE;
3313 if (tim < data_tim_low) data_tim_low = tim;
3314 if (tim > data_tim_high) data_tim_high = tim;
3315 if (data_seq_cur < data_seq_low) data_seq_low = data_seq_cur;
3316 if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
3318 else { /* ack seg */
3319 /* skip ack processing if no ACK (e.g. in RST) */
3320 if (TCP_ACK (tmp->th_flags)) {
3321 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3322 ack_seq_cur = tmp->th_ack;
3323 win_seq_cur = ack_seq_cur + tmp->th_win;
3324 if (! ack_frame_seen) {
3325 ack_tim_low = ack_tim_high = tim;
3326 ack_seq_low = ack_seq_cur;
3327 win_seq_high = win_seq_cur;
3328 ack_frame_seen = TRUE;
3330 if (tim < ack_tim_low) ack_tim_low = tim;
3331 if (tim > ack_tim_high) ack_tim_high = tim;
3332 if (ack_seq_cur < ack_seq_low) ack_seq_low = ack_seq_cur;
3333 if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
3338 /* if 'stevens': use only data segments to determine bounds */
3339 /* if 'tcptrace': use both data and ack segments to determine bounds */
3341 case GRAPH_TSEQ_STEVENS:
3342 g->bounds.x0 = data_tim_low;
3343 g->bounds.width = data_tim_high - data_tim_low;
3344 g->bounds.y0 = data_seq_low;
3345 g->bounds.height = data_seq_high - data_seq_low;
3347 case GRAPH_TSEQ_TCPTRACE:
3348 /* If (ack_frame_seen == false) -> use 'data' segments.
3349 * Else If (data_frame_seen == false) -> use 'ack' segments.
3350 * Else -> use both data and ack segments.
3352 g->bounds.x0 = ((data_tim_low <= ack_tim_low && data_frame_seen) || (! ack_frame_seen)) ? data_tim_low : ack_tim_low;
3353 g->bounds.width = (((data_tim_high >= ack_tim_high && data_frame_seen) || (! ack_frame_seen)) ? data_tim_high : ack_tim_high) - g->bounds.x0;
3354 g->bounds.y0 = ((data_seq_low <= ack_seq_low && data_frame_seen) || (! ack_frame_seen)) ? data_seq_low : ack_seq_low;
3355 g->bounds.height = (((data_seq_high >= win_seq_high && data_frame_seen) || (! ack_frame_seen)) ? data_seq_high : win_seq_high) - g->bounds.y0;
3359 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3360 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3364 static void tseq_stevens_make_elmtlist (struct graph *g)
3366 struct segment *tmp;
3367 struct element *elements, *e;
3368 double x0 = g->bounds.x0, y0 = g->bounds.y0;
3369 guint32 seq_base = (guint32) y0;
3372 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3373 if (g->elists->elements == NULL) {
3374 int n = 1 + get_num_dsegs (g);
3375 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3377 e = elements = g->elists->elements;
3379 for (tmp=g->segments; tmp; tmp=tmp->next) {
3382 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3383 g->current->th_sport, g->current->th_dport,
3384 &tmp->ip_src, &tmp->ip_dst,
3385 tmp->th_sport, tmp->th_dport,
3386 COMPARE_CURR_DIR)) {
3390 seq_cur = tmp->th_seq - seq_base;
3391 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
3392 seqno = g->zoom.y * seq_cur;
3397 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3398 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3399 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3400 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3401 e->p.arc.filled = TRUE;
3402 e->p.arc.angle1 = 0;
3403 e->p.arc.angle2 = 23040;
3406 e->type = ELMT_NONE;
3407 g->elists->elements = elements;
3410 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3412 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3414 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3415 g->y_axis->min = g->bounds.y0;
3416 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3420 static void tseq_stevens_toggle_time_origin (struct graph *g)
3422 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3424 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3425 g->x_axis->min = g->bounds.x0;
3426 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3431 * tcptrace-style time-sequence graph
3434 static void tseq_tcptrace_read_config (struct graph *g)
3436 GdkColormap *colormap;
3439 g->s.tseq_tcptrace.flags = 0;
3440 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
3441 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
3442 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
3443 colormap = gdk_window_get_colormap (g->drawing_area->window);
3444 if (!gdk_color_parse ("black", &color)) {
3446 * XXX - do more than just warn.
3448 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3449 "Could not parse color black.");
3451 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3453 * XXX - do more than just warn.
3455 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3456 "Could not allocate color black.");
3458 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3459 if (!gdk_color_parse ("LightSlateGray", &color)) {
3461 * XXX - do more than just warn.
3463 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3464 "Could not parse color LightSlateGray.");
3466 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3468 * XXX - do more than just warn.
3470 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3471 "Could not allocate color LightSlateGray.");
3473 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3474 if (!gdk_color_parse ("LightGray", &color)) {
3476 * XXX - do more than just warn.
3478 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3479 "Could not parse color LightGray.");
3481 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3483 * XXX - do more than just warn.
3485 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3486 "Could not allocate color LightGray.");
3488 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3490 g->elists->next = (struct element_list * )
3491 g_malloc (sizeof (struct element_list));
3492 g->elists->next->next = NULL;
3493 g->elists->next->elements = NULL;
3495 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3496 g->title[0] = "Time/Sequence Graph";
3498 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3499 g->y_axis->label[0] = "number[B]";
3500 g->y_axis->label[1] = "Sequence";
3501 g->y_axis->label[2] = NULL;
3502 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3503 g->x_axis->label[0] = "Time[s]";
3504 g->x_axis->label[1] = NULL;
3507 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3509 struct segment *tmp;
3510 struct element *elements0, *e0; /* list of elmts with prio 0 */
3511 struct element *elements1, *e1; /* list of elmts with prio 1 */
3513 double p_t = 0; /* ackno, window and time of previous segment */
3514 double p_ackno = 0, p_win = 0;
3515 gboolean ack_seen=FALSE;
3520 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3522 if (g->elists->elements == NULL) {
3523 int n = 1 + 4*get_num_acks(g);
3524 e0 = elements0 = (struct element * )g_malloc (n*sizeof (struct element));
3526 e0 = elements0 = g->elists->elements;
3528 if (g->elists->next->elements == NULL ) {
3529 int n = 1 + 3*get_num_dsegs(g);
3530 e1 = elements1 = (struct element * )g_malloc (n*sizeof (struct element));
3532 e1 = elements1 = g->elists->next->elements;
3536 seq_base = (guint32) y0;
3538 for (tmp=g->segments; tmp; tmp=tmp->next) {
3542 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3545 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3546 g->current->th_sport, g->current->th_dport,
3547 &tmp->ip_src, &tmp->ip_dst,
3548 tmp->th_sport, tmp->th_dport,
3549 COMPARE_CURR_DIR)) {
3550 /* forward direction -> we need seqno and amount of data */
3553 seq_cur = tmp->th_seq - seq_base;
3554 if (TCP_SYN (tmp->th_flags) || TCP_FIN (tmp->th_flags))
3557 data = tmp->th_seglen;
3559 y1 = g->zoom.y * (seq_cur);
3560 y2 = g->zoom.y * (seq_cur + data);
3561 e1->type = ELMT_LINE;
3563 e1->gc = g->s.tseq_tcptrace.gc_seq;
3564 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3565 e1->p.line.dim.y1 = y1;
3566 e1->p.line.dim.y2 = y2;
3568 e1->type = ELMT_LINE;
3570 e1->gc = g->s.tseq_tcptrace.gc_seq;
3571 e1->p.line.dim.x1 = x - 1;
3572 e1->p.line.dim.x2 = x + 1;
3573 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
3575 e1->type = ELMT_LINE;
3577 e1->gc = g->s.tseq_tcptrace.gc_seq;
3578 e1->p.line.dim.x1 = x + 1;
3579 e1->p.line.dim.x2 = x - 1;
3580 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
3584 if (! TCP_ACK (tmp->th_flags))
3585 /* SYN's and RST's do not necessarily have ACK's*/
3587 /* backward direction -> we need ackno and window */
3588 seq_cur = tmp->th_ack - seq_base;
3589 ackno = seq_cur * g->zoom.y;
3590 win = tmp->th_win * g->zoom.y;
3593 if (ack_seen == TRUE) { /* don't plot the first ack */
3594 e0->type = ELMT_LINE;
3596 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3597 e0->p.line.dim.x1 = p_t;
3598 e0->p.line.dim.y1 = p_ackno;
3599 e0->p.line.dim.x2 = x;
3600 e0->p.line.dim.y2 = p_ackno;
3602 e0->type = ELMT_LINE;
3604 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3605 e0->p.line.dim.x1 = x;
3606 e0->p.line.dim.y1 = p_ackno;
3607 e0->p.line.dim.x2 = x;
3608 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3611 e0->type = ELMT_LINE;
3613 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3614 e0->p.line.dim.x1 = p_t;
3615 e0->p.line.dim.y1 = p_win + p_ackno;
3616 e0->p.line.dim.x2 = x;
3617 e0->p.line.dim.y2 = p_win + p_ackno;
3619 e0->type = ELMT_LINE;
3621 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3622 e0->p.line.dim.x1 = x;
3623 e0->p.line.dim.y1 = p_win + p_ackno;
3624 e0->p.line.dim.x2 = x;
3625 e0->p.line.dim.y2 = win + ackno;
3635 e0->type = ELMT_NONE;
3636 e1->type = ELMT_NONE;
3637 g->elists->elements = elements0;
3638 g->elists->next->elements = elements1;
3641 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3643 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3645 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3646 g->y_axis->min = g->bounds.y0;
3647 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3651 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3653 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3655 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3656 g->x_axis->min = g->bounds.x0;
3657 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3665 static void tput_make_elmtlist (struct graph *g)
3667 struct segment *tmp, *oldest;
3668 struct element *elements, *e;
3672 if (g->elists->elements == NULL) {
3673 int n = 1 + get_num_dsegs (g);
3674 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3676 e = elements = g->elists->elements;
3678 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3679 double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3680 dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3681 if (i>g->s.tput.nsegs) {
3682 sum -= oldest->th_seglen;
3683 oldest=oldest->next;
3685 sum += tmp->th_seglen;
3687 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3692 e->p.arc.dim.width = g->s.tput.width;
3693 e->p.arc.dim.height = g->s.tput.height;
3694 e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
3695 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3696 e->p.arc.filled = TRUE;
3697 e->p.arc.angle1 = 0;
3698 e->p.arc.angle2 = 23040;
3701 e->type = ELMT_NONE;
3702 g->elists->elements = elements;
3705 /* Purpose of <graph_type>_initialize functions:
3706 * - find maximum and minimum for both axes
3707 * - call setup routine for style struct */
3708 static void tput_initialize (struct graph *g)
3710 struct segment *tmp, *oldest, *last;
3712 double dtime, tput, tputmax=0;
3713 double t, t0, tmax = 0, y0, ymax;
3715 debug(DBS_FENTRY) puts ("tput_initialize()");
3717 tput_read_config(g);
3719 for (last=g->segments; last->next; last=last->next);
3720 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3721 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3722 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3723 if (i>g->s.tput.nsegs) {
3724 sum -= oldest->th_seglen;
3725 oldest=oldest->next;
3727 sum += tmp->th_seglen;
3729 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3732 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3737 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3743 g->bounds.width = tmax - t0;
3744 g->bounds.height = ymax - y0;
3745 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3746 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3749 static void tput_read_config (struct graph *g)
3751 debug(DBS_FENTRY) puts ("tput_read_config()");
3753 g->s.tput.width = 4;
3754 g->s.tput.height = 4;
3755 g->s.tput.nsegs = 20;
3757 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3758 g->title[0] = "Throughput Graph";
3760 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3761 g->y_axis->label[0] = "[B/s]";
3762 g->y_axis->label[1] = "Throughput";
3763 g->y_axis->label[2] = NULL;
3764 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3765 g->x_axis->label[0] = "Time[s]";
3766 g->x_axis->label[1] = NULL;
3767 g->s.tput.flags = 0;
3770 static void tput_toggle_time_origin (struct graph *g)
3772 g->s.tput.flags ^= TIME_ORIGIN;
3774 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3775 g->x_axis->min = g->bounds.x0;
3776 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3782 static void rtt_read_config (struct graph *g)
3784 debug(DBS_FENTRY) puts ("rtt_read_config()");
3787 g->s.rtt.height = 4;
3790 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3791 g->title[0] = "Round Trip Time Graph";
3793 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3794 g->y_axis->label[0] = "RTT [s]";
3795 g->y_axis->label[1] = NULL;
3796 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3797 g->x_axis->label[0] = "Sequence Number[B]";
3798 g->x_axis->label[1] = NULL;
3801 static void rtt_initialize (struct graph *g)
3803 struct segment *tmp, *first=NULL;
3804 struct unack *unack = NULL, *u;
3806 double x0, y0, ymax;
3808 guint32 seq_base = 0;
3810 debug(DBS_FENTRY) puts ("rtt_initialize()");
3812 rtt_read_config (g);
3814 for (tmp=g->segments; tmp; tmp=tmp->next) {
3815 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3816 g->current->th_sport, g->current->th_dport,
3817 &tmp->ip_src, &tmp->ip_dst,
3818 tmp->th_sport, tmp->th_dport,
3819 COMPARE_CURR_DIR)) {
3820 guint32 seqno = tmp->th_seq;
3827 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
3828 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3829 u = rtt_get_new_unack (time, seqno);
3831 rtt_put_unack_on_list (&unack, u);
3834 if (seqno + tmp->th_seglen > xmax)
3835 xmax = seqno + tmp->th_seglen;
3837 guint32 ackno = tmp->th_ack -seq_base;
3838 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3841 for (u=unack; u; u=v)
3842 if (ackno > u->seqno) {
3843 double rtt = time - u->time;
3847 rtt_delete_unack_from_list (&unack, u);
3859 g->bounds.width = xmax;
3860 g->bounds.height = ymax - y0;
3861 g->zoom.x = g->geom.width / g->bounds.width;
3862 g->zoom.y = g->geom.height / g->bounds.height;
3865 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
3869 for (u=list; u; u=u->next)
3870 if (u->seqno== seqno)
3876 static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
3880 u = (struct unack * )g_malloc (sizeof (struct unack));
3889 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
3891 struct unack *u, *list = *l;
3893 for (u=list; u; u=u->next)
3903 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
3905 struct unack *u, *list = *l;
3914 for (u=list; u; u=u->next)
3915 if (u->next == dead) {
3916 u->next = u->next->next;
3922 static void rtt_make_elmtlist (struct graph *g)
3924 struct segment *tmp;
3925 struct unack *unack = NULL, *u;
3926 struct element *elements, *e;
3927 guint32 seq_base = (guint32) g->bounds.x0;
3929 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
3931 if (g->elists->elements == NULL) {
3932 int n = 1 + get_num_dsegs (g);
3933 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3935 e = elements = g->elists->elements;
3938 for (tmp=g->segments; tmp; tmp=tmp->next) {
3939 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3940 g->current->th_sport, g->current->th_dport,
3941 &tmp->ip_src, &tmp->ip_dst,
3942 tmp->th_sport, tmp->th_dport,
3943 COMPARE_CURR_DIR)) {
3944 guint32 seqno = tmp->th_seq -seq_base;
3946 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
3947 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3948 u = rtt_get_new_unack (time, seqno);
3950 rtt_put_unack_on_list (&unack, u);
3953 guint32 ackno = tmp->th_ack -seq_base;
3954 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3957 for (u=unack; u; u=v)
3958 if (ackno > u->seqno) {
3959 double rtt = time - u->time;
3964 e->p.arc.dim.width = g->s.rtt.width;
3965 e->p.arc.dim.height = g->s.rtt.height;
3966 e->p.arc.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
3967 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
3968 e->p.arc.filled = TRUE;
3969 e->p.arc.angle1 = 0;
3970 e->p.arc.angle2 = 23040;
3974 rtt_delete_unack_from_list (&unack, u);
3979 e->type = ELMT_NONE;
3980 g->elists->elements = elements;
3983 static void rtt_toggle_seq_origin (struct graph *g)
3985 g->s.rtt.flags ^= SEQ_ORIGIN;
3987 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3988 g->x_axis->min = g->bounds.x0;
3993 #if defined(_WIN32) && !defined(__MINGW32__)
3994 /* replacement of Unix rint() for Windows */
3995 static int rint (double x)
4000 buf = _fcvt(x, 0, &dec, &sig);
4010 static gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt)
4012 return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
4017 register_tap_listener_tcp_graph(void)
4019 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (Stevens)", REGISTER_STAT_GROUP_NONE,
4020 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(0));
4021 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (tcptrace)", REGISTER_STAT_GROUP_NONE,
4022 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(1));
4023 register_stat_menu_item("TCP Stream Graph/Throughput Graph", REGISTER_STAT_GROUP_NONE,
4024 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(2));
4025 register_stat_menu_item("TCP Stream Graph/Round Trip Time Graph", REGISTER_STAT_GROUP_NONE,
4026 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(3));