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.
36 #include <gdk/gdkkeysyms.h>
37 #if GTK_CHECK_VERSION(3,0,0)
38 # include <gdk/gdkkeysyms-compat.h>
41 #include <epan/packet.h>
42 #include <epan/ipproto.h>
43 #include <epan/etypes.h>
44 #include <epan/ppptypes.h>
45 #include <epan/epan_dissect.h>
46 #include <epan/dissectors/packet-tcp.h>
47 #include <epan/address.h>
50 #include "../globals.h"
51 #include "../simple_dialog.h"
52 #include "../stat_menu.h"
54 #include "gtk/gui_utils.h"
55 #include "gtk/dlg_utils.h"
56 #include "gtk/gui_stat_menu.h"
58 #include "gtk/old-gtk-compat.h"
67 #define TCP_SYN(flags) ( flags & TH_SYN )
68 #define TCP_ACK(flags) ( flags & TH_ACK )
69 #define TCP_FIN(flags) ( flags & TH_FIN )
72 #define TXT_HEIGHT 550
74 /* for compare_headers() */
75 /* segment went the same direction as the currently selected one */
76 #define COMPARE_CURR_DIR 0
77 #define COMPARE_ANY_DIR 1
79 /* initalize_axis() */
80 #define AXIS_HORIZONTAL 0
81 #define AXIS_VERTICAL 1
83 #define WINDOW_TITLE_LENGTH 256
85 #define MOUSE_BUTTON_LEFT 1
86 #define MOUSE_BUTTON_MIDDLE 2
87 #define MOUSE_BUTTON_RIGHT 3
100 guint32 th_win; /* make it 32 bits so we can handle some scaling */
109 double x, y, width, height;
113 double x1, y1, x2, y2;
117 int x, y, width, height;
149 struct segment *parent;
151 struct arc_params arc;
152 struct rect_params rect;
153 struct line_params line;
157 struct element_list {
158 struct element_list *next;
159 struct element *elements;
163 struct graph *g; /* which graph we belong to */
164 GtkWidget *drawing_area;
165 GdkPixmap *pixmap[2];
167 #define AXIS_ORIENTATION 1 << 0
169 /* dim and orig (relative to origin of window) of axis' pixmap */
171 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
174 gdouble major, minor; /* major and minor ticks */
178 #define HAXIS_INIT_HEIGHT 70
179 #define VAXIS_INIT_WIDTH 100
180 #define TITLEBAR_HEIGHT 50
181 #define RMARGIN_WIDTH 30
183 struct style_tseq_tcptrace {
189 struct style_tseq_stevens {
206 struct style_wscale {
213 #define SEQ_ORIGIN 0x1
214 /* show absolute sequence numbers (not differences from isn) */
215 #define SEQ_ORIGIN_ZERO 0x1
216 #define SEQ_ORIGIN_ISN 0x0
217 #define TIME_ORIGIN 0x10
218 /* show time from beginning of capture as opposed to time from beginning
219 * of the connection */
220 #define TIME_ORIGIN_CAP 0x10
221 #define TIME_ORIGIN_CONN 0x0
223 /* this is used by rtt module only */
232 int draw; /* indicates whether we should draw cross at all */
234 GtkToggleButton *on_toggle;
235 GtkToggleButton *off_toggle;
239 double x0, y0, width, height;
248 double step_x, step_y;
250 #define ZOOM_OUT (1 << 0)
251 #define ZOOM_HLOCK (1 << 1)
252 #define ZOOM_VLOCK (1 << 2)
253 #define ZOOM_STEPS_SAME (1 << 3)
254 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
256 /* unfortunately, we need them both because gtk_toggle_button_set_active ()
257 * with second argument FALSE doesn't do anything, somehow */
259 GtkToggleButton *in_toggle;
260 GtkToggleButton *out_toggle;
263 GtkSpinButton *h_step;
264 GtkSpinButton *v_step;
276 struct ipoint offset;
280 #define MAGZOOMS_SAME (1U << 0)
281 #define MAGZOOMS_SAME_RATIO (1U << 1)
282 #define MAGZOOMS_IGNORE (1U << 31)
285 GtkSpinButton *h_zoom, *v_zoom;
291 #define GRAPH_TSEQ_STEVENS 0
292 #define GRAPH_TSEQ_TCPTRACE 1
293 #define GRAPH_THROUGHPUT 2
295 #define GRAPH_WSCALE 4
297 #define GRAPH_DESTROYED (1 << 0)
298 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
300 GtkWidget *toplevel; /* keypress handler needs this */
301 GtkWidget *drawing_area;
302 GtkWidget *text; /* text widget for seg list - probably
304 PangoFontDescription *font; /* font used for annotations etc. */
307 GdkPixmap *title_pixmap;
308 GdkPixmap *pixmap[2];
309 int displayed; /* which of both pixmaps is on screen right now */
311 GtkWidget *control_panel;
312 /* this belongs to style structs of graph types that make use of it */
313 GtkToggleButton *time_orig_conn, *seq_orig_isn;
316 /* Next 4 attribs describe the graph in natural units, before any scaling.
317 * For example, if we want to display graph of TCP conversation that
318 * started 112.309845 s after beginning of the capture and ran until
319 * 479.093582 s, 237019 B went through the connection (in one direction)
320 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
321 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
322 struct bounds bounds;
323 /* dimensions and position of the graph, both expressed already in pixels.
324 * x and y give the position of upper left corner of the graph relative
325 * to origin of the graph window, size is basically bounds*zoom */
327 /* viewport (=graph window area which is reserved for graph itself), its
328 * size and position relative to origin of the graph window */
331 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
332 * pixels, we have to scale the graph down by factor of 0.002109. This
333 * number would be zoom.y. Obviously, both directions have separate zooms.*/
336 struct magnify magnify;
337 struct axis *x_axis, *y_axis;
338 struct segment *segments;
339 struct segment *current;
340 struct element_list *elists; /* element lists */
342 struct style_tseq_stevens tseq_stevens;
343 struct style_tseq_tcptrace tseq_tcptrace;
344 struct style_tput tput;
345 struct style_rtt rtt;
346 struct style_wscale wscale;
348 /* This allows keyboard to set the radio button */
350 GtkToggleButton *graph_rtt, *graph_tput, *graph_tseqstevens, *graph_tseqttrace;
351 GtkToggleButton *graph_wscale;
355 static GdkGC *xor_gc = NULL;
358 #define debug(section) if (debugging & section)
359 /* print function entry points */
360 #define DBS_FENTRY (1 << 0)
361 #define DBS_AXES_TICKS (1 << 1)
362 #define DBS_AXES_DRAWING (1 << 2)
363 #define DBS_GRAPH_DRAWING (1 << 3)
364 #define DBS_TPUT_ELMTS (1 << 4)
365 /*int debugging = DBS_FENTRY;*/
366 static int debugging = 0;
367 /*int debugging = DBS_AXES_TICKS;*/
368 /*int debugging = DBS_AXES_DRAWING;*/
369 /*int debugging = DBS_GRAPH_DRAWING;*/
370 /*int debugging = DBS_TPUT_ELMTS;*/
372 static void create_gui (struct graph * );
374 static void create_text_widget (struct graph * );
375 static void display_text (struct graph * );
377 static void create_drawing_area (struct graph * );
378 static void control_panel_create (struct graph * );
379 static GtkWidget *control_panel_create_zoom_group (struct graph * );
380 static GtkWidget *control_panel_create_magnify_group (struct graph * );
381 static GtkWidget *control_panel_create_cross_group (struct graph * );
382 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
383 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
384 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
385 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
386 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
387 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
388 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
389 static void callback_toplevel_destroy (GtkWidget * , gpointer );
390 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
391 static void callback_close (GtkWidget * , gpointer );
392 static void callback_time_origin (GtkWidget * , gpointer );
393 static void callback_seq_origin (GtkWidget * , gpointer );
394 static void callback_zoomlock_h (GtkWidget * , gpointer );
395 static void callback_zoomlock_v (GtkWidget * , gpointer );
396 static void callback_zoom_inout (GtkWidget * , gpointer );
397 static void callback_zoom_step (GtkWidget * , gpointer );
398 static void callback_zoom_flags (GtkWidget * , gpointer );
399 static void callback_cross_on_off (GtkWidget * , gpointer );
400 static void callback_mag_width (GtkWidget * , gpointer );
401 static void callback_mag_height (GtkWidget * , gpointer );
402 static void callback_mag_x (GtkWidget * , gpointer );
403 static void callback_mag_y (GtkWidget * , gpointer );
404 static void callback_mag_zoom (GtkWidget * , gpointer );
405 static void callback_mag_flags (GtkWidget * , gpointer );
406 static void callback_graph_type (GtkWidget * , gpointer );
407 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
408 static void callback_create_help (GtkWidget * , gpointer );
409 static void update_zoom_spins (struct graph * );
410 static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
411 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
412 static int get_num_dsegs (struct graph * );
413 static int get_num_acks (struct graph * );
414 static void graph_type_dependent_initialize (struct graph * );
415 static struct graph *graph_new (void);
416 static void graph_destroy (struct graph * );
417 static void graph_initialize_values (struct graph * );
418 static void graph_init_sequence (struct graph * );
419 static void draw_element_line (struct graph * , struct element * );
420 static void draw_element_arc (struct graph * , struct element * );
421 static void graph_display (struct graph * );
422 static void graph_pixmaps_create (struct graph * );
423 static void graph_pixmaps_switch (struct graph * );
424 static void graph_pixmap_draw (struct graph * );
425 static void graph_pixmap_display (struct graph * );
426 static void graph_element_lists_make (struct graph * );
427 static void graph_element_lists_free (struct graph * );
428 static void graph_element_lists_initialize (struct graph * );
429 static void graph_title_pixmap_create (struct graph * );
430 static void graph_title_pixmap_draw (struct graph * );
431 static void graph_title_pixmap_display (struct graph * );
432 static void graph_segment_list_get (struct graph * );
433 static void graph_segment_list_free (struct graph * );
434 static void graph_select_segment (struct graph * , int , int );
435 static int line_detect_collision (struct element * , int , int );
436 static int arc_detect_collision (struct element * , int , int );
437 static void axis_pixmaps_create (struct axis * );
438 static void axis_pixmaps_switch (struct axis * );
439 static void axis_display (struct axis * );
440 static void v_axis_pixmap_draw (struct axis * );
441 static void h_axis_pixmap_draw (struct axis * );
442 static void axis_pixmap_display (struct axis * );
443 static void axis_compute_ticks (struct axis * , double , double , int );
444 static double axis_zoom_get (struct axis * , int );
445 static void axis_ticks_up (int * , int * );
446 static void axis_ticks_down (int * , int * );
447 static void axis_destroy (struct axis * );
448 static int get_label_dim (struct axis * , int , double );
449 static void toggle_crosshairs (struct graph *g);
450 static void toggle_time_origin (struct graph * );
451 static void toggle_seq_origin (struct graph * );
452 static void restore_initial_graph_view (struct graph *g);
453 static void cross_xor (struct graph * , int , int );
454 static void cross_draw (struct graph * , int , int );
455 static void cross_erase (struct graph * );
456 static void magnify_create (struct graph * , int , int );
457 static void magnify_move (struct graph * , int , int );
458 static void magnify_destroy (struct graph * );
459 static void magnify_draw (struct graph * );
460 static void magnify_get_geom (struct graph * , int , int );
461 static gboolean configure_event (GtkWidget * , GdkEventConfigure * , gpointer );
462 static gboolean expose_event (GtkWidget * , GdkEventExpose * , gpointer );
463 static gboolean button_press_event (GtkWidget * , GdkEventButton * , gpointer );
464 static gboolean button_release_event (GtkWidget * , GdkEventButton * , gpointer );
465 static gboolean motion_notify_event (GtkWidget * , GdkEventMotion * , gpointer );
466 static gboolean key_press_event (GtkWidget * , GdkEventKey * , gpointer );
467 static gboolean key_release_event (GtkWidget * , GdkEventKey * , gpointer );
468 static gboolean leave_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
469 static gboolean enter_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
470 static void tseq_initialize (struct graph * );
471 static void tseq_get_bounds (struct graph * );
472 static void tseq_stevens_read_config (struct graph * );
473 static void tseq_stevens_make_elmtlist (struct graph * );
474 static void tseq_stevens_toggle_seq_origin (struct graph * );
475 static void tseq_stevens_toggle_time_origin (struct graph * );
476 static void tseq_tcptrace_read_config (struct graph * );
477 static void tseq_tcptrace_make_elmtlist (struct graph * );
478 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
479 static void tseq_tcptrace_toggle_time_origin (struct graph * );
480 static void tput_initialize (struct graph * );
481 static void tput_read_config (struct graph * );
482 static void tput_make_elmtlist (struct graph * );
483 static void tput_toggle_time_origin (struct graph * );
484 static void rtt_read_config (struct graph * );
485 static void rtt_initialize (struct graph * );
486 static int rtt_is_retrans (struct unack * , unsigned int );
487 static struct unack *rtt_get_new_unack (double , unsigned int );
488 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
489 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
490 static void rtt_make_elmtlist (struct graph * );
491 static void rtt_toggle_seq_origin (struct graph * );
492 static void wscale_initialize(struct graph *);
493 static void wscale_read_config(struct graph *);
494 static void wscale_make_elmtlist(struct graph *);
495 #if defined(_WIN32) && !defined(__MINGW32__)
496 static int rint (double ); /* compiler template for Windows */
500 * Uncomment the following define to revert WIN32 to
501 * use original mouse button controls
504 /* #define ORIGINAL_WIN32_BUTTONS 1 */
506 /* XXX - what about OS X? */
507 static char helptext[] =
508 "Here's what you can do:\n"
510 #ifdef ORIGINAL_WIN32_BUTTONS
511 " <Ctrl>-Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
513 " Left Mouse Button zooms in (towards area under mouse pointer)\n"
514 " <Shift>-Left Mouse Button zooms out\n"
516 " Right Mouse Button moves the graph (if zoomed in)\n"
517 " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
518 #else /* !ORIGINAL_WIN32_BUTTONS */
519 " Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
521 " Middle Mouse Button zooms in (towards area under cursor)\n"
522 " <Shift>-Middle Mouse Button zooms out\n"
524 " Right Mouse Button moves the graph (if zoomed in)\n"
525 " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
529 " '1' display Round Trip Time Graph\n"
530 " '2' display Throughput Graph\n"
531 " '3' display Time/Sequence Graph (Stevens)\n"
532 " '4' display Time/Sequence Graph (tcptrace)\n"
533 " '5' display Window Scaling Graph\n"
535 " <Space bar> toggles crosshairs on/off\n"
537 " 'i' or '+' zoom in (towards area under mouse pointer)\n"
538 " 'o' or '-' zoom out\n"
539 " 'r' or <Home> restore graph to initial state (zoom out max)\n"
540 " 's' toggles relative/absolute sequence numbers\n"
541 " 't' toggles time origin\n"
542 " 'g' go to frame under cursor in Wireshark's packet list (if possible)\n"
544 " <Left> move view left by 100 pixels (if zoomed in)\n"
545 " <Right> move view right 100 pixels (if zoomed in)\n"
546 " <Up> move view up by 100 pixels (if zoomed in)\n"
547 " <Down> move view down by 100 pixels (if zoomed in)\n"
549 " <Shift><Left> move view left by 10 pixels (if zoomed in)\n"
550 " <Shift><Right> move view right 10 pixels (if zoomed in)\n"
551 " <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
552 " <Shift><Down> move view down by 10 pixels (if zoomed in)\n"
554 " <Ctrl><Left> move view left by 1 pixel (if zoomed in)\n"
555 " <Ctrl><Right> move view right 1 pixel (if zoomed in)\n"
556 " <Ctrl><Up> move view up by 1 pixel (if zoomed in)\n"
557 " <Ctrl><Down> move view down by 1 pixel (if zoomed in)\n"
561 static void debug_coord (struct graph *g, const char *c)
563 static unsigned count = 0;
566 printf("%u: %s\n", count, c);
567 printf("%u: g->geom.width %d\n", count, g->geom.width);
568 printf("%u: g->geom.height %d\n", count, g->geom.height);
569 printf("%u: g->geom.x %d\n", count, g->geom.x);
570 printf("%u: g->geom.y %d\n", count, g->geom.y);
572 printf("%u: g->wp.width %d\n", count, g->wp.width);
573 printf("%u: g->wp.height %d\n", count, g->wp.height);
574 printf("%u: g->wp.x %d\n", count, g->wp.x);
575 printf("%u: g->wp.y %d\n", count, g->wp.y);
576 printf("---------------\n");
580 static void set_busy_cursor(GdkWindow *w)
584 cursor = gdk_cursor_new(GDK_WATCH);
585 gdk_window_set_cursor(w, cursor);
587 gdk_cursor_unref(cursor);
590 static void unset_busy_cursor(GdkWindow *w)
592 gdk_window_set_cursor(w, NULL);
595 #ifdef MAIN_MENU_USE_UIMANAGER
596 void tcp_graph_cb (GtkAction *action, gpointer user_data _U_)
598 struct segment current;
603 name = gtk_action_get_name (action);
604 if(strcmp(name, "Analyze/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens") == 0){
605 graph_type = GRAPH_TSEQ_STEVENS;
606 }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace") == 0){
607 graph_type = GRAPH_TSEQ_TCPTRACE;
608 }else if(strcmp(name, "StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph") == 0){
609 graph_type = GRAPH_THROUGHPUT;
610 }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph") == 0){
611 graph_type = GRAPH_RTT;
612 }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph") == 0){
613 graph_type = GRAPH_WSCALE;
618 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
620 if (! (g = graph_new()))
624 graph_initialize_values (g);
626 g->type = graph_type;
627 if (!select_tcpip_session (&cfile, ¤t)) {
631 graph_segment_list_get(g);
633 /* display_text(g); */
634 graph_init_sequence(g);
638 static void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
640 struct segment current;
643 guint graph_type = GPOINTER_TO_INT(data);
645 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
647 if (! (g = graph_new()))
651 graph_initialize_values (g);
653 g->type = graph_type;
654 if (!select_tcpip_session (&cfile, ¤t)) {
658 graph_segment_list_get(g);
660 /* display_text(g); */
661 graph_init_sequence(g);
664 static void create_gui (struct graph *g)
666 debug(DBS_FENTRY) puts ("create_gui()");
667 /* create_text_widget(g); */
668 control_panel_create (g);
669 create_drawing_area(g);
674 static void create_drawing_area (struct graph *g)
676 GdkColormap *colormap;
678 char window_title[WINDOW_TITLE_LENGTH];
679 struct segment current;
680 struct tcpheader *thdr;
681 GtkAllocation widget_alloc;
683 debug(DBS_FENTRY) puts ("create_drawing_area()");
685 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
687 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
688 "-*-*-m-*-iso8859-2");
690 thdr=select_tcpip_session (&cfile, ¤t);
691 g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
693 cf_get_display_name(&cfile),
694 ep_address_to_str(&(thdr->ip_src)),
696 ep_address_to_str(&(thdr->ip_dst)),
699 g->toplevel = dlg_window_new ("Tcp Graph");
700 gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
701 gtk_widget_set_name (g->toplevel, "Test Graph");
702 g_object_set_data(G_OBJECT(g->toplevel), "graph", g);
704 /* Create the drawing area */
705 g->drawing_area = gtk_drawing_area_new ();
706 g_object_set_data(G_OBJECT(g->drawing_area), "graph", g);
707 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
708 gtk_widget_set_size_request (g->drawing_area,
709 g->wp.width + g->wp.x + RMARGIN_WIDTH,
710 g->wp.height + g->wp.y + g->x_axis->s.height);
711 gtk_widget_show (g->drawing_area);
713 g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), NULL);
714 /* this has to be done later, after the widget has been shown */
716 g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
719 g_signal_connect(g->drawing_area, "motion_notify_event",
720 G_CALLBACK(motion_notify_event), NULL);
721 g_signal_connect(g->drawing_area, "button_press_event",
722 G_CALLBACK(button_press_event), NULL);
723 g_signal_connect(g->drawing_area, "button_release_event",
724 G_CALLBACK(button_release_event), NULL);
725 g_signal_connect(g->drawing_area, "leave_notify_event",
726 G_CALLBACK(leave_notify_event), NULL);
727 g_signal_connect(g->drawing_area, "enter_notify_event",
728 G_CALLBACK(enter_notify_event), NULL);
729 g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
730 /* why doesn't drawing area send key_press_signals? */
731 g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), NULL);
732 g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
734 gtk_widget_set_events(g->toplevel,
735 GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
737 gtk_widget_set_events (g->drawing_area,
739 | GDK_LEAVE_NOTIFY_MASK
740 | GDK_ENTER_NOTIFY_MASK
741 | GDK_BUTTON_PRESS_MASK
742 | GDK_BUTTON_RELEASE_MASK
743 | GDK_POINTER_MOTION_MASK
744 | GDK_POINTER_MOTION_HINT_MASK);
747 frame = gtk_frame_new (NULL);
748 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
749 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
751 box = gtk_hbox_new (FALSE, 0);
752 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
753 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
754 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
755 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
756 gtk_widget_show (frame);
757 gtk_widget_show (box);
760 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
761 gtk_widget_show (g->toplevel);
763 /* in case we didn't get what we asked for */
764 gtk_widget_get_allocation(GTK_WIDGET (g->drawing_area), &widget_alloc);
765 g->wp.width = widget_alloc.width - g->wp.x - RMARGIN_WIDTH;
766 g->wp.height = widget_alloc.height - g->wp.y - g->x_axis->s.height;
768 g->font = gtk_widget_get_style(g->drawing_area)->font_desc;
770 colormap = gtk_widget_get_colormap(GTK_WIDGET(g->drawing_area));
772 xor_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
773 gdk_gc_set_function (xor_gc, GDK_XOR);
774 if (!gdk_color_parse ("gray15", &color)) {
776 * XXX - do more than just warn.
778 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
779 "Could not parse color gray15.");
781 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
783 * XXX - do more than just warn.
785 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
786 "Could not allocate color gray15.");
788 gdk_gc_set_foreground (xor_gc, &color);
790 g->fg_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
791 g->bg_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
792 if (!gdk_color_parse ("white", &color)) {
794 * XXX - do more than just warn.
796 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
797 "Could not parse color white.");
799 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
801 * XXX - do more than just warn.
803 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
804 "Could not allocate color white.");
806 gdk_gc_set_foreground (g->bg_gc, &color);
808 /* this is probably quite an ugly way to get rid of the first configure
810 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
811 * event which is handled during the next return to gtk_main which is
812 * probably the gdk_gc_new() call. configure handler calls
813 * graph_element_lists_make() which is not good because the graph struct is
814 * not fully set up yet - namely we're not sure about actual geometry
815 * and we don't have the GC's at all. so we just postpone installation
816 * of configure handler until we're ready to deal with it.
818 * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
821 g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event),
824 /* puts ("exiting create_drawing_area()"); */
827 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
829 struct graph *g = (struct graph * )data;
831 if (!(g->flags & GRAPH_DESTROYED)) {
832 g->flags |= GRAPH_DESTROYED;
833 graph_destroy ((struct graph * )data);
837 static void control_panel_create (struct graph *g)
839 GtkWidget *toplevel, *notebook;
841 GtkWidget *help_bt, *close_bt, *bbox;
842 char window_title[WINDOW_TITLE_LENGTH];
844 debug(DBS_FENTRY) puts ("control_panel_create()");
846 notebook = gtk_notebook_new ();
847 control_panel_add_zoom_page (g, notebook);
848 control_panel_add_magnify_page (g, notebook);
849 control_panel_add_origin_page (g, notebook);
850 control_panel_add_cross_page (g, notebook);
851 control_panel_add_graph_type_page (g, notebook);
853 g_snprintf (window_title, WINDOW_TITLE_LENGTH,
854 "Graph %d - Control - Wireshark", refnum);
855 toplevel = dlg_window_new ("tcp-graph-control");
856 gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
858 table = gtk_table_new (2, 1, FALSE);
859 gtk_container_add (GTK_CONTAINER (toplevel), table);
861 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
862 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
865 bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
866 gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
867 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
869 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
870 g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
872 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
873 window_set_cancel_button(toplevel, close_bt, NULL);
874 g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
876 g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
877 g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
879 /* gtk_widget_show_all (table); */
880 /* g->gui.control_panel = table; */
881 gtk_widget_show_all (toplevel);
882 window_present(toplevel);
884 g->gui.control_panel = toplevel;
887 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
889 GtkWidget *zoom_frame;
890 GtkWidget *zoom_lock_frame;
894 zoom_frame = control_panel_create_zoom_group (g);
895 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
896 zoom_lock_frame = control_panel_create_zoomlock_group (g);
897 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
898 box = gtk_vbox_new (FALSE, 0);
899 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
900 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
901 gtk_widget_show (box);
902 label = gtk_label_new ("Zoom");
903 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
906 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
908 GtkWidget *mag_frame, *label;
910 mag_frame = control_panel_create_magnify_group (g);
911 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
912 label = gtk_label_new ("Magnify");
913 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
916 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
918 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
919 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
920 GtkWidget *box, *label;
922 /* time origin box */
924 gtk_radio_button_new_with_label (NULL, "beginning of capture");
925 time_orig_conn = gtk_radio_button_new_with_label (
926 gtk_radio_button_get_group (GTK_RADIO_BUTTON (time_orig_cap)),
927 "beginning of this TCP connection");
928 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
929 time_orig_box = gtk_vbox_new (TRUE, 0);
930 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
931 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
932 time_orig_frame = gtk_frame_new ("Time origin");
933 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
934 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
936 /* sequence number origin group */
938 gtk_radio_button_new_with_label (NULL, "initial sequence number");
939 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_get_group (
940 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
941 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
942 seq_orig_box = gtk_vbox_new (TRUE, 0);
943 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
944 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
945 seq_orig_frame = gtk_frame_new ("Sequence number origin");
946 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
947 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
949 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
950 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
952 g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
953 g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
955 box = gtk_vbox_new (FALSE, 0);
956 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
957 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
958 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
959 gtk_widget_show (box);
960 label = gtk_label_new ("Origin");
961 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
964 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
966 GtkWidget *cross_frame, *label;
968 cross_frame = control_panel_create_cross_group (g);
969 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
970 label = gtk_label_new ("Cross");
971 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
974 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
976 GtkWidget *frame, *label;
978 frame = control_panel_create_graph_type_group (g);
979 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
980 label = gtk_label_new ("Graph type");
981 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
984 /* Treat this as a cancel, by calling "callback_close()" */
986 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
989 callback_close(NULL, data);
993 static void callback_close (GtkWidget *widget _U_, gpointer data)
995 struct graph *g = (struct graph * )data;
997 if (!(g->flags & GRAPH_DESTROYED)) {
998 g->flags |= GRAPH_DESTROYED;
999 graph_destroy ((struct graph * )data);
1003 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
1005 GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
1008 toplevel = dlg_window_new ("Help for TCP graphing");
1009 gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
1011 vbox = gtk_vbox_new (FALSE, 3);
1012 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1013 gtk_container_add (GTK_CONTAINER (toplevel), vbox);
1015 scroll = scrolled_window_new (NULL, NULL);
1016 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1018 gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
1019 text = gtk_text_view_new();
1020 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1021 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1022 gtk_text_buffer_set_text(buf, helptext, -1);
1023 gtk_container_add (GTK_CONTAINER (scroll), text);
1026 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1027 gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
1028 gtk_widget_show(bbox);
1030 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1031 window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
1033 g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1035 gtk_widget_show_all (toplevel);
1036 window_present(toplevel);
1039 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1041 toggle_time_origin ((struct graph * )data);
1044 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1046 toggle_seq_origin ((struct graph * )data);
1049 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1051 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1052 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1053 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1054 GtkWidget *zoom_v_step_label, *zoom_v_step;
1055 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1056 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1057 GtkWidget *zoom_h_entry, *zoom_v_entry;
1058 GtkWidget *zoom_h_label, *zoom_v_label;
1060 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1061 zoom_out = gtk_radio_button_new_with_label (
1062 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1063 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1064 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1065 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1066 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1068 zoom_separator1 = gtk_hseparator_new ();
1070 zoom_h_entry = gtk_entry_new ();
1071 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1072 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1073 zoom_h_label = gtk_label_new ("Horizontal:");
1075 zoom_v_entry = gtk_entry_new ();
1076 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1077 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1078 zoom_v_label = gtk_label_new ("Vertical:");
1080 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1081 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1083 zoom_table = gtk_table_new (2, 2, FALSE);
1084 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1085 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1086 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1087 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1088 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1089 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1090 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1091 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1093 zoom_separator2 = gtk_hseparator_new ();
1095 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1096 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1097 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1098 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1100 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1101 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1102 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1103 zoom_v_step_label = gtk_label_new ("Vertical step:");
1105 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1106 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1108 zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1109 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1110 g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
1111 g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
1112 (gpointer)ZOOM_STEPS_KEEP_RATIO);
1113 g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1114 g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1116 zoom_step_table = gtk_table_new (4, 2, FALSE);
1117 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1118 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1119 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1120 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1121 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1122 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1123 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1124 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1125 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1126 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1127 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1128 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1130 zoom_box = gtk_vbox_new (FALSE, 0);
1131 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1132 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1133 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1134 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1135 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1136 zoom_frame = gtk_frame_new ("Zoom");
1137 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1139 g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
1140 g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
1142 g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
1143 g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
1144 g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
1146 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1147 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1151 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1153 struct graph *g = (struct graph * )data;
1155 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1156 g->zoom.flags &= ~ZOOM_OUT;
1158 g->zoom.flags |= ZOOM_OUT;
1161 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1163 struct graph *g = (struct graph * )data;
1166 double *zoom_this, *zoom_other;
1167 GtkSpinButton *widget_this, *widget_other;
1170 direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1171 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1174 zoom_this = &g->zoom.step_y;
1175 zoom_other = &g->zoom.step_x;
1176 widget_this = g->zoom.widget.v_step;
1177 widget_other = g->zoom.widget.h_step;
1179 zoom_this = &g->zoom.step_x;
1180 zoom_other = &g->zoom.step_y;
1181 widget_this = g->zoom.widget.h_step;
1182 widget_other = g->zoom.widget.v_step;
1185 old_this = *zoom_this;
1187 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1188 *zoom_other = value;
1189 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1190 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1191 double old_other = *zoom_other;
1192 *zoom_other *= value / old_this;
1193 if (*zoom_other < 1.0) {
1195 *zoom_this = old_this * 1.0 / old_other;
1196 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1197 } else if (*zoom_other > 5.0) {
1199 *zoom_this = old_this * 5.0 / old_other;
1200 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1202 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1206 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1208 struct graph *g = (struct graph * )data;
1209 int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1211 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1212 g->zoom.flags |= flag;
1214 g->zoom.flags &= ~flag;
1217 static void update_zoom_spins (struct graph *g)
1221 g_snprintf (s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
1222 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1223 g_snprintf (s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
1224 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1227 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1229 GtkWidget *mag_width_label, *mag_width;
1230 GtkWidget *mag_height_label, *mag_height;
1231 GtkWidget *mag_x_label, *mag_x;
1232 GtkWidget *mag_y_label, *mag_y;
1233 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1234 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1235 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1236 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1237 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1238 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1239 GtkWidget *mag_box, *mag_frame;
1241 mag_width_label = gtk_label_new ("Width:");
1242 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1243 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1245 mag_height_label = gtk_label_new ("Height:");
1246 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1247 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1249 mag_x_label = gtk_label_new ("X:");
1250 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1251 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1253 mag_y_label = gtk_label_new ("Y:");
1254 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1255 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1257 mag_wh_table = gtk_table_new (4, 2, FALSE);
1258 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1259 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1260 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1261 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1262 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1263 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1264 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1265 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1266 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1267 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1268 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1269 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1270 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1271 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1272 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1273 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1275 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1276 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1277 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1279 mag_v_zoom_label = gtk_label_new ("Vertical:");
1280 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1281 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1283 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1284 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1286 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1287 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1288 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1289 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1290 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1291 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1292 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1293 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1294 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1295 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1296 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1297 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1298 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1300 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1301 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1302 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1304 mag_box = gtk_vbox_new (FALSE, 0);
1305 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1306 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1307 mag_frame = gtk_frame_new ("Magnify");
1308 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1310 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1311 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1312 g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
1313 g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
1314 g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
1315 g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1317 g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
1318 g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
1319 g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
1320 g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
1321 g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1322 g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1323 g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
1324 g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
1329 static void callback_mag_width (GtkWidget *spin, gpointer data)
1331 struct graph *g = (struct graph * )data;
1333 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1336 static void callback_mag_height (GtkWidget *spin, gpointer data)
1338 struct graph *g = (struct graph * )data;
1340 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1343 static void callback_mag_x (GtkWidget *spin, gpointer data)
1345 struct graph *g = (struct graph * )data;
1347 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1350 static void callback_mag_y (GtkWidget *spin, gpointer data)
1352 struct graph *g = (struct graph * )data;
1354 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1357 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1359 struct graph *g = (struct graph * )data;
1362 double *zoom_this, *zoom_other;
1363 GtkSpinButton *widget_this, *widget_other;
1366 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1367 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1368 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1371 direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1372 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1375 zoom_this = &g->magnify.zoom.y;
1376 zoom_other = &g->magnify.zoom.x;
1377 widget_this = g->magnify.widget.v_zoom;
1378 widget_other = g->magnify.widget.h_zoom;
1380 zoom_this = &g->magnify.zoom.x;
1381 zoom_other = &g->magnify.zoom.y;
1382 widget_this = g->magnify.widget.h_zoom;
1383 widget_other = g->magnify.widget.v_zoom;
1386 old_this = *zoom_this;
1388 if (g->magnify.flags & MAGZOOMS_SAME) {
1389 *zoom_other = value;
1390 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1391 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1392 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1393 double old_other = *zoom_other;
1394 *zoom_other *= value / old_this;
1395 if (*zoom_other < 1.0) {
1397 *zoom_this = old_this * 1.0 / old_other;
1398 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1399 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1400 } else if (*zoom_other > 25.0) {
1402 *zoom_this = old_this * 25.0 / old_other;
1403 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1404 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1406 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1407 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1411 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1413 struct graph *g = (struct graph * )data;
1414 int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1416 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1417 g->magnify.flags |= flag;
1419 g->magnify.flags &= ~flag;
1422 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1424 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1425 GtkWidget *zoom_lock_frame;
1427 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1428 zoom_lock_h = gtk_radio_button_new_with_label (
1429 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1431 zoom_lock_v = gtk_radio_button_new_with_label (
1432 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1434 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1435 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1436 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1438 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1439 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1440 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1441 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1443 g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
1444 g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
1446 return zoom_lock_frame;
1449 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1451 struct graph *g = (struct graph * )data;
1453 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1454 g->zoom.flags |= ZOOM_HLOCK;
1456 g->zoom.flags &= ~ZOOM_HLOCK;
1459 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1461 struct graph *g = (struct graph * )data;
1463 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1464 g->zoom.flags |= ZOOM_VLOCK;
1466 g->zoom.flags &= ~ZOOM_VLOCK;
1469 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1471 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1473 label = gtk_label_new ("Crosshairs:");
1474 off = gtk_radio_button_new_with_label (NULL, "off");
1475 on = gtk_radio_button_new_with_label (
1476 gtk_radio_button_get_group (GTK_RADIO_BUTTON (off)), "on");
1477 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1478 box = gtk_hbox_new (FALSE, 0);
1479 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1480 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1481 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1482 vbox = gtk_vbox_new (FALSE, 0);
1483 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1484 /* frame = gtk_frame_new ("Cross:"); */
1485 frame = gtk_frame_new (NULL);
1486 gtk_container_add (GTK_CONTAINER (frame), vbox);
1488 g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
1490 g->cross.on_toggle = (GtkToggleButton * )on;
1491 g->cross.off_toggle = (GtkToggleButton * )off;
1496 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1498 struct graph *g = (struct graph * )data;
1500 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle))) {
1502 g->cross.draw = TRUE;
1503 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
1504 cross_draw (g, x, y);
1506 g->cross.draw = FALSE;
1511 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1513 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1514 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1515 GtkWidget *graph_frame;
1516 GtkWidget *graph_wscale;
1518 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1519 graph_tseqttrace = gtk_radio_button_new_with_label (
1520 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1521 "Time/Sequence (tcptrace-style)");
1522 graph_tseqstevens = gtk_radio_button_new_with_label (
1523 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1524 "Time/Sequence (Stevens'-style)");
1525 graph_rtt = gtk_radio_button_new_with_label (
1526 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1528 graph_wscale = gtk_radio_button_new_with_label (
1529 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1533 case GRAPH_TSEQ_STEVENS:
1534 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1536 case GRAPH_TSEQ_TCPTRACE:
1537 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1539 case GRAPH_THROUGHPUT:
1540 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1543 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1546 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_wscale), TRUE);
1549 graph_init = gtk_check_button_new_with_label ("Init on change");
1550 graph_sep = gtk_hseparator_new ();
1551 graph_box = gtk_vbox_new (FALSE, 0);
1552 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1553 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1554 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1555 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1556 gtk_box_pack_start (GTK_BOX (graph_box), graph_wscale, TRUE, TRUE, 0);
1557 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1558 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1559 graph_frame = gtk_frame_new ("Graph type:");
1560 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1562 g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
1563 GINT_TO_POINTER(0));
1564 g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type", GINT_TO_POINTER(1));
1565 g_object_set_data(G_OBJECT(graph_tput), "new-graph-type", GINT_TO_POINTER(2));
1566 g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type", GINT_TO_POINTER(3));
1567 g_object_set_data(G_OBJECT(graph_wscale), "new-graph-type", GINT_TO_POINTER(GRAPH_WSCALE));
1569 g->gt.graph_wscale = (GtkToggleButton *)graph_wscale;
1570 g->gt.graph_rtt = (GtkToggleButton * )graph_rtt;
1571 g->gt.graph_tput = (GtkToggleButton * )graph_tput;
1572 g->gt.graph_tseqstevens = (GtkToggleButton * )graph_tseqstevens;
1573 g->gt.graph_tseqttrace = (GtkToggleButton * )graph_tseqttrace;
1575 g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
1576 g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
1577 g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
1578 g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
1579 g_signal_connect(graph_wscale, "toggled", G_CALLBACK(callback_graph_type), g);
1580 g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg), g);
1585 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1587 int old_type, new_type;
1588 struct graph *g = (struct graph * )data;
1590 new_type = (long)g_object_get_data(G_OBJECT(toggle),"new-graph-type");
1592 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1598 graph_element_lists_free (g);
1599 graph_element_lists_initialize (g);
1601 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1602 /* throughput graph uses differently constructed segment list so we
1603 * need to recreate it */
1604 graph_segment_list_free (g);
1605 graph_segment_list_get (g);
1608 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1609 g->geom.width = g->wp.width;
1610 g->geom.height = g->wp.height;
1611 g->geom.x = g->wp.x;
1612 g->geom.y = g->wp.y;
1614 g->x_axis->min = g->y_axis->min = 0;
1615 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1616 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1617 graph_init_sequence (g);
1620 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1622 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1625 static struct graph *graph_new (void)
1629 g = (struct graph * )g_malloc0 (sizeof (struct graph));
1630 graph_element_lists_initialize (g);
1632 g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1633 g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1635 g->x_axis->flags = 0;
1636 g->x_axis->flags |= AXIS_ORIENTATION;
1637 g->x_axis->s.x = g->x_axis->s.y = 0;
1638 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1639 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1640 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1642 g->y_axis->flags = 0;
1643 g->y_axis->flags &= ~AXIS_ORIENTATION;
1644 g->y_axis->p.x = g->y_axis->p.y = 0;
1645 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1647 g->y_axis->s.y = TITLEBAR_HEIGHT;
1648 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1653 static void graph_initialize_values (struct graph *g)
1655 g->geom.width = g->wp.width = 750;
1656 g->geom.height = g->wp.height = 550;
1657 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1658 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1660 /* g->zoom.x = g->zoom.y = 1.0; */
1661 g->zoom.step_x = g->zoom.step_y = 1.2;
1663 g->cross.draw = g->cross.erase_needed = 0;
1664 g->grab.grabbed = 0;
1665 g->magnify.active = 0;
1666 g->magnify.offset.x = g->magnify.offset.y = 0;
1667 g->magnify.width = g->magnify.height = 250;
1668 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1669 g->magnify.flags = 0;
1672 static void graph_init_sequence (struct graph *g)
1674 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1676 graph_type_dependent_initialize (g);
1677 g->zoom.initial.x = g->zoom.x;
1678 g->zoom.initial.y = g->zoom.y;
1679 graph_element_lists_make (g);
1680 g->x_axis->s.width = g->wp.width;
1681 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1682 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1683 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1684 g->y_axis->s.height = g->wp.height;
1685 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1686 graph_pixmaps_create (g);
1687 axis_pixmaps_create (g->y_axis);
1688 axis_pixmaps_create (g->x_axis);
1689 graph_title_pixmap_create (g);
1690 graph_title_pixmap_draw (g);
1691 graph_title_pixmap_display (g);
1693 axis_display (g->y_axis);
1694 axis_display (g->x_axis);
1697 static void graph_type_dependent_initialize (struct graph *g)
1700 case GRAPH_TSEQ_STEVENS:
1701 case GRAPH_TSEQ_TCPTRACE:
1702 tseq_initialize (g);
1704 case GRAPH_THROUGHPUT:
1705 tput_initialize (g);
1711 wscale_initialize (g);
1718 static void graph_destroy (struct graph *g)
1720 debug(DBS_FENTRY) puts ("graph_destroy()");
1722 axis_destroy (g->x_axis);
1723 axis_destroy (g->y_axis);
1724 /* window_destroy (g->drawing_area); */
1725 window_destroy (g->gui.control_panel);
1726 window_destroy (g->toplevel);
1727 /* window_destroy (g->text); */
1728 g_object_unref (g->fg_gc);
1729 g_object_unref (g->bg_gc);
1730 g_object_unref (g->pixmap[0]);
1731 g_object_unref (g->pixmap[1]);
1734 g_free ( (gpointer) (g->title) );
1735 graph_segment_list_free (g);
1736 graph_element_lists_free (g);
1742 typedef struct _tcp_scan_t {
1743 struct segment *current;
1746 struct segment *last;
1750 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
1752 static struct segment *segment=NULL;
1753 tcp_scan_t *ts=(tcp_scan_t *)pct;
1754 struct tcpheader *tcphdr=(struct tcpheader *)vip;
1757 segment=g_malloc(sizeof (struct segment));
1761 if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
1762 ts->current->th_sport, ts->current->th_dport,
1763 &tcphdr->ip_src, &tcphdr->ip_dst,
1764 tcphdr->th_sport, tcphdr->th_dport,
1766 segment->next = NULL;
1767 segment->num = pinfo->fd->num;
1768 segment->rel_secs = (guint32) pinfo->fd->rel_ts.secs;
1769 segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
1770 segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
1771 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
1772 segment->th_seq=tcphdr->th_seq;
1773 segment->th_ack=tcphdr->th_ack;
1774 segment->th_win=tcphdr->th_win;
1775 segment->th_flags=tcphdr->th_flags;
1776 segment->th_sport=tcphdr->th_sport;
1777 segment->th_dport=tcphdr->th_dport;
1778 segment->th_seglen=tcphdr->th_seglen;
1779 COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
1780 COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
1781 if (ts->g->segments) {
1782 ts->last->next = segment;
1784 ts->g->segments = segment;
1787 if(pinfo->fd->num==ts->current->num){
1788 ts->g->current = segment;
1799 /* here we collect all the external data we will ever need */
1800 static void graph_segment_list_get (struct graph *g)
1802 struct segment current;
1803 GString *error_string;
1807 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1808 select_tcpip_session (&cfile, ¤t);
1809 if (g->type == GRAPH_THROUGHPUT)
1810 ts.direction = COMPARE_CURR_DIR;
1812 ts.direction = COMPARE_ANY_DIR;
1814 /* rescan all the packets and pick up all interesting tcp headers.
1815 * we only filter for TCP here for speed and do the actual compare
1816 * in the tap listener
1818 ts.current=¤t;
1821 error_string=register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
1823 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1825 g_string_free(error_string, TRUE);
1828 cf_retap_packets(&cfile);
1829 remove_tap_listener(&ts);
1833 typedef struct _th_t {
1835 struct tcpheader *tcphdr;
1839 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
1844 th->tcphdr=(struct tcpheader *)vip;
1851 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
1852 * then present the user with a dialog where the user can select WHICH tcp
1855 static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
1860 GString *error_string;
1861 th_t th = {0, NULL};
1863 fdata = cf->current_frame;
1865 /* no real filter yet */
1866 if (!dfilter_compile("tcp", &sfcode)) {
1867 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
1871 /* dissect the current frame */
1872 if (!cf_read_frame(cf, fdata))
1873 return NULL; /* error reading the frame */
1876 error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
1878 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1880 g_string_free(error_string, TRUE);
1884 epan_dissect_init(&edt, TRUE, FALSE);
1885 epan_dissect_prime_dfilter(&edt, sfcode);
1886 tap_queue_init(&edt);
1887 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
1888 tap_push_tapped_queue(&edt);
1889 epan_dissect_cleanup(&edt);
1890 remove_tap_listener(&th);
1893 /* This "shouldn't happen", as our menu items shouldn't
1894 * even be enabled if the selected packet isn't a TCP
1895 * segment, as tcp_graph_selected_packet_enabled() is used
1896 * to determine whether to enable any of our menu items. */
1897 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1898 "Selected packet isn't a TCP segment");
1901 /* XXX fix this later, we should show a dialog allowing the user
1902 to select which session he wants here
1905 /* can only handle a single tcp layer yet */
1906 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1907 "The selected packet has more than one TCP"
1912 hdrs->num = fdata->num;
1913 hdrs->rel_secs = (guint32) fdata->rel_ts.secs;
1914 hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
1915 hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
1916 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
1917 hdrs->th_seq=th.tcphdr->th_seq;
1918 hdrs->th_ack=th.tcphdr->th_ack;
1919 hdrs->th_win=th.tcphdr->th_win;
1920 hdrs->th_flags=th.tcphdr->th_flags;
1921 hdrs->th_sport=th.tcphdr->th_sport;
1922 hdrs->th_dport=th.tcphdr->th_dport;
1923 hdrs->th_seglen=th.tcphdr->th_seglen;
1924 COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
1925 COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
1930 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
1934 dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
1935 (!(CMP_ADDRESS(daddr1, daddr2))) &&
1939 if(dir==COMPARE_CURR_DIR){
1942 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
1943 (!(CMP_ADDRESS(daddr1, saddr2))) &&
1946 return dir1 || dir2;
1950 static void graph_segment_list_free (struct graph *g)
1952 struct segment *segment;
1954 while (g->segments) {
1955 segment = g->segments->next;
1956 g_free (g->segments);
1957 g->segments = segment;
1962 static void graph_element_lists_initialize (struct graph *g)
1964 g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
1967 static void graph_element_lists_make (struct graph *g)
1969 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1972 case GRAPH_TSEQ_STEVENS:
1973 tseq_stevens_make_elmtlist (g);
1975 case GRAPH_TSEQ_TCPTRACE:
1976 tseq_tcptrace_make_elmtlist (g);
1978 case GRAPH_THROUGHPUT:
1979 tput_make_elmtlist (g);
1982 rtt_make_elmtlist (g);
1985 wscale_make_elmtlist (g);
1988 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1993 static void graph_element_lists_free (struct graph *g)
1995 struct element_list *list, *next_list;
1998 for (list=g->elists; list; list=list->next)
1999 g_free (list->elements);
2000 while (g->elists->next) {
2001 list = g->elists->next->next;
2002 g_free (g->elists->next);
2003 g->elists->next = list;
2007 for (list=g->elists; list; list=next_list) {
2008 g_free (list->elements);
2009 next_list = list->next;
2012 g->elists = NULL; /* just to make debugging easier */
2015 static void graph_title_pixmap_create (struct graph *g)
2017 if (g->title_pixmap)
2018 g_object_unref (g->title_pixmap);
2020 g->title_pixmap = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2021 g->x_axis->p.width, g->wp.y, -1);
2024 static void graph_title_pixmap_draw (struct graph *g)
2028 gdk_draw_rectangle(g->title_pixmap, g->bg_gc, TRUE, 0, 0,
2029 g->x_axis->p.width, g->wp.y);
2030 for (i=0; g->title[i]; i++) {
2032 PangoLayout *layout;
2033 layout = gtk_widget_create_pango_layout(g->drawing_area,
2035 pango_layout_get_pixel_size(layout, &w, &h);
2036 gdk_draw_layout(g->title_pixmap, g->fg_gc,
2037 g->wp.width/2 - w/2, 20 + i*(h+3), layout);
2038 g_object_unref(G_OBJECT(layout));
2042 static void graph_title_pixmap_display (struct graph *g)
2044 gdk_draw_pixmap (gtk_widget_get_window(g->drawing_area), g->fg_gc, g->title_pixmap,
2045 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
2048 static void graph_pixmaps_create (struct graph *g)
2050 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
2053 g_object_unref (g->pixmap[0]);
2055 g_object_unref (g->pixmap[1]);
2057 g->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2058 g->wp.width, g->wp.height, -1);
2059 g->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2060 g->wp.width, g->wp.height, -1);
2065 static void graph_display (struct graph *g)
2067 set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2068 graph_pixmap_draw (g);
2069 unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2070 graph_pixmaps_switch (g);
2071 graph_pixmap_display (g);
2074 static void graph_pixmap_display (struct graph *g)
2076 gdk_draw_pixmap (gtk_widget_get_window(g->drawing_area), g->fg_gc,
2077 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
2078 g->wp.width, g->wp.height);
2079 if (g->cross.erase_needed) {
2080 cross_xor(g, g->cross.x, g->cross.y);
2084 static void graph_pixmaps_switch (struct graph *g)
2086 g->displayed = 1 ^ g->displayed;
2089 static void graph_pixmap_draw (struct graph *g)
2091 struct element_list *list;
2095 debug(DBS_FENTRY) puts ("graph_display()");
2096 not_disp = 1 ^ g->displayed;
2098 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
2099 0, 0, g->wp.width, g->wp.height);
2100 for (list=g->elists; list; list=list->next)
2101 for (e=list->elements; e->type != ELMT_NONE; e++) {
2106 draw_element_line (g, e);
2109 draw_element_arc (g, e);
2117 static void draw_element_line (struct graph *g, struct element *e)
2119 int xx1, xx2, yy1, yy2;
2121 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2122 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2123 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2124 xx1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2125 xx2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2126 yy1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2127 yy2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2138 if ((xx1<0 && xx2<0) || (xx1>=g->wp.width && xx2>=g->wp.width) ||
2139 (yy1<0 && yy2<0) || (yy1>=g->wp.height && yy2>=g->wp.height)) {
2140 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2141 xx1, yy1, xx2, yy2);
2144 if (xx2 > g->wp.width-1)
2145 xx2 = g->wp.width-1;
2148 if (yy2 > g->wp.height-1)
2149 yy2 = g->wp.height-1;
2152 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2,yy2);
2153 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, xx1, yy1, xx2, yy2);
2156 static void draw_element_arc (struct graph *g, struct element *e)
2158 int xx1, xx2, yy1, yy2;
2160 xx1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2161 xx2 = (int )e->p.arc.dim.width;
2162 yy1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2163 yy2 = (int )e->p.arc.dim.height;
2164 if (xx1<-xx2 || xx1>=g->wp.width || yy1<-yy2 || yy1>=g->wp.height)
2166 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2, yy2);
2167 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, xx1,
2168 yy1, xx2, yy2, e->p.arc.angle1, e->p.arc.angle2);
2171 static void axis_pixmaps_create (struct axis *axis)
2173 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2174 if (axis->pixmap[0])
2175 g_object_unref (axis->pixmap[0]);
2176 if (axis->pixmap[1])
2177 g_object_unref (axis->pixmap[1]);
2179 axis->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2180 axis->p.width, axis->p.height, -1);
2181 axis->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2182 axis->p.width, axis->p.height, -1);
2184 axis->displayed = 0;
2187 static void axis_destroy (struct axis *axis)
2189 g_object_unref (axis->pixmap[0]);
2190 g_object_unref (axis->pixmap[1]);
2191 g_free ( (gpointer) (axis->label) );
2194 static void axis_display (struct axis *axis)
2196 if (axis->flags & AXIS_ORIENTATION)
2197 h_axis_pixmap_draw (axis);
2199 v_axis_pixmap_draw (axis);
2200 axis_pixmaps_switch (axis);
2201 axis_pixmap_display (axis);
2204 static void v_axis_pixmap_draw (struct axis *axis)
2206 struct graph *g = axis->g;
2209 int not_disp, rdigits, offset, imin, imax;
2210 double bottom, top, j, fl, corr;
2211 PangoLayout *layout;
2213 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2214 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2215 (double )g->geom.height * g->bounds.height;
2216 bottom += axis->min;
2217 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2218 (double )g->geom.height * g->bounds.height;
2220 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2222 j = axis->major - floor (axis->major);
2223 for (rdigits=0; rdigits<=6; rdigits++) {
2230 not_disp = 1 ^ axis->displayed;
2231 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2232 axis->p.width, axis->p.height);
2234 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2235 (gint) ((axis->p.height-axis->s.height)/2.0), axis->s.width - 1,
2238 offset = g->wp.y + (-g->geom.y);
2239 fl = floor (axis->min / axis->major) * axis->major;
2240 corr = rint ((axis->min - fl) * g->zoom.y);
2243 major_tick = axis->major * g->zoom.y;
2244 imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2245 imax = (int) ((g->geom.height - offset + corr) / major_tick);
2246 for (i=imin; i <= imax; i++) {
2249 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2250 offset + corr + axis->s.y);
2252 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2253 i*axis->major + fl, y);
2254 if (y < 0 || y > axis->p.height)
2256 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2257 axis->s.width - 15, y, axis->s.width - 1, y);
2258 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
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));
2267 double minor_tick = axis->minor * g->zoom.y;
2268 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2269 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2270 for (i=imin; i <= imax; i++) {
2271 int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2272 offset + corr + axis->s.y);
2274 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2275 if (y > 0 && y < axis->p.height)
2276 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2277 axis->s.width - 8, y,
2278 axis->s.width - 1, y);
2281 for (i=0; axis->label[i]; i++) {
2283 layout = gtk_widget_create_pango_layout(g->drawing_area,
2285 pango_layout_get_pixel_size(layout, &w, &h);
2286 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2287 (axis->p.width - w)/2,
2288 TITLEBAR_HEIGHT-10 - i*(h+3) - h,
2290 g_object_unref(G_OBJECT(layout));
2294 static void h_axis_pixmap_draw (struct axis *axis)
2296 struct graph *g = axis->g;
2298 double major_tick, minor_tick;
2299 int not_disp, rdigits, offset, imin, imax;
2300 double left, right, j, fl, corr;
2301 PangoLayout *layout;
2303 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2304 left = (g->wp.x-g->geom.x) /
2305 (double )g->geom.width * g->bounds.width;
2307 right = (g->wp.x-g->geom.x+g->wp.width) /
2308 (double )g->geom.width * g->bounds.width;
2310 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2312 j = axis->major - floor (axis->major);
2313 for (rdigits=0; rdigits<=6; rdigits++) {
2320 not_disp = 1 ^ axis->displayed;
2321 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2322 axis->p.width, axis->p.height);
2324 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2325 (gint) (axis->s.width + (axis->p.width-axis->s.width)/2.0), 0);
2326 offset = g->wp.x - g->geom.x;
2328 fl = floor (axis->min / axis->major) * axis->major;
2329 corr = rint ((axis->min - fl) * g->zoom.x);
2332 major_tick = axis->major*g->zoom.x;
2333 imin = (int) ((offset + corr) / major_tick + 1);
2334 imax = (int) ((offset + corr + axis->s.width) / major_tick);
2335 for (i=imin; i <= imax; i++) {
2338 int x = (int ) (rint (i * major_tick) - offset - corr);
2340 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2341 if (x < 0 || x > axis->s.width)
2343 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2344 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2345 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2346 pango_layout_get_pixel_size(layout, &w, &h);
2347 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2348 x - w/2, 15+4, layout);
2349 g_object_unref(G_OBJECT(layout));
2351 if (axis->minor > 0) {
2353 minor_tick = axis->minor*g->zoom.x;
2354 imin = (int) ((offset + corr) / minor_tick + 1);
2355 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2356 for (i=imin; i <= imax; i++) {
2357 int x = (int) (rint (i * minor_tick) - offset - corr);
2358 if (x > 0 && x < axis->s.width)
2359 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2362 for (i=0; axis->label[i]; i++) {
2364 layout = gtk_widget_create_pango_layout(g->drawing_area,
2366 pango_layout_get_pixel_size(layout, &w, &h);
2367 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2368 axis->s.width - w - 50, 15+h+15 + i*(h+3),
2370 g_object_unref(G_OBJECT(layout));
2374 static void axis_pixmaps_switch (struct axis *axis)
2376 axis->displayed = 1 ^ axis->displayed;
2379 static void axis_pixmap_display (struct axis *axis)
2381 gdk_draw_pixmap (gtk_widget_get_window(axis->drawing_area), axis->g->fg_gc,
2382 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2383 axis->p.width, axis->p.height);
2386 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2388 int i, j, ii, jj, ms;
2389 double zoom, x, steps[3]={ 0.1, 0.5 };
2390 int dim, check_needed, diminished;
2391 double majthresh[2]={2.0, 3.0};
2393 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2394 debug(DBS_AXES_TICKS)
2395 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2397 zoom = axis_zoom_get (axis, dir);
2399 for (i=-9; i<=12; i++) {
2400 if (x / pow (10, i) < 1)
2404 ms = (int )(x / pow (10, i));
2414 axis->major = steps[j] * pow (10, i);
2416 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2417 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2419 /* let's compute minor ticks */
2422 axis_ticks_down (&ii, &jj);
2423 axis->minor = steps[jj] * pow (10, ii);
2424 /* we don't want minors if they would be less than 10 pixels apart */
2425 if (axis->minor*zoom < 10) {
2426 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2427 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2431 check_needed = TRUE;
2433 while (check_needed) {
2434 check_needed = FALSE;
2435 dim = get_label_dim (axis, dir, xmax);
2436 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2437 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2438 axis->major, axis->minor, axis->major*zoom/dim,
2439 axis->minor*zoom/dim);
2441 /* corrections: if majors are less than majthresh[dir] times label
2442 * dimension apart, we need to use bigger ones */
2443 if (axis->major*zoom / dim < majthresh[dir]) {
2444 axis_ticks_up (&ii, &jj);
2445 axis->minor = axis->major;
2446 axis_ticks_up (&i, &j);
2447 axis->major = steps[j] * pow (10, i);
2448 check_needed = TRUE;
2449 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2452 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2453 * we could promote them to majors as well */
2454 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2455 axis_ticks_down (&i, &j);
2456 axis->major = axis->minor;
2457 axis_ticks_down (&ii, &jj);
2458 axis->minor = steps[jj] * pow (10, ii);
2459 check_needed = TRUE;
2462 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2465 if (axis->minor*zoom < 10) {
2466 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2467 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2473 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2474 "axis->minor == %.1f\n", axis->major, axis->minor);
2477 static void axis_ticks_up (int *i, int *j)
2486 static void axis_ticks_down (int *i, int *j)
2495 static int get_label_dim (struct axis *axis, int dir, double label)
2500 PangoLayout *layout;
2502 /* First, let's compute how many digits to the right of radix
2503 * we need to print */
2504 y = axis->major - floor (axis->major);
2505 for (rdigits=0; rdigits<=6; rdigits++) {
2511 g_snprintf (str, sizeof(str), "%.*f", rdigits, label);
2513 case AXIS_HORIZONTAL:
2514 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2516 pango_layout_get_pixel_size(layout, &dim, NULL);
2517 g_object_unref(G_OBJECT(layout));
2520 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2522 pango_layout_get_pixel_size(layout, NULL, &dim);
2523 g_object_unref(G_OBJECT(layout));
2526 puts ("initialize axis: an axis must be either horizontal or vertical");
2532 static double axis_zoom_get (struct axis *axis, int dir)
2535 case AXIS_HORIZONTAL:
2536 return axis->g->zoom.x;
2538 return axis->g->zoom.y;
2544 static void graph_select_segment (struct graph *g, int x, int y)
2546 struct element_list *list;
2550 debug(DBS_FENTRY) puts ("graph_select_segment()");
2553 y = g->geom.height-1 - (y - g->geom.y);
2555 set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2557 for (list=g->elists; list; list=list->next)
2558 for (e=list->elements; e->type != ELMT_NONE; e++) {
2563 if (line_detect_collision (e, x, y)) {
2564 num = e->parent->num;
2568 if (arc_detect_collision (e, x, y)) {
2569 num = e->parent->num;
2579 cf_goto_frame(&cfile, num);
2581 unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2584 static int line_detect_collision (struct element *e, int x, int y)
2586 int xx1, yy1, xx2, yy2;
2588 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2589 xx1 = (int )rint (e->p.line.dim.x1);
2590 xx2 = (int )rint (e->p.line.dim.x2);
2592 xx1 = (int )rint (e->p.line.dim.x2);
2593 xx2 = (int )rint (e->p.line.dim.x1);
2595 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2596 yy1 = (int )rint (e->p.line.dim.y1);
2597 yy2 = (int )rint (e->p.line.dim.y2);
2599 yy1 = (int )rint (e->p.line.dim.y2);
2600 yy2 = (int )rint (e->p.line.dim.y1);
2603 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2605 if ((xx1==x && xx2==x && yy1<=y && y<=yy2)||(yy1==y && yy2==y && xx1<=x && x<=xx2))
2611 static int arc_detect_collision (struct element *e, int x, int y)
2613 int xx1, yy1, xx2, yy2;
2615 xx1 = (int )rint (e->p.arc.dim.x);
2616 xx2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2617 yy1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2618 yy2 = (int )rint (e->p.arc.dim.y);
2620 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2622 if (xx1<=x && x<=xx2 && yy1<=y && y<=yy2)
2628 static void cross_xor (struct graph *g, int x, int y)
2630 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2631 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2632 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, g->wp.x,
2633 y, g->wp.x + g->wp.width, y);
2634 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, x,
2635 g->wp.y, x, g->wp.y + g->wp.height);
2639 static void cross_draw (struct graph *g, int x, int y)
2641 cross_xor (g, x, y);
2644 g->cross.erase_needed = 1;
2647 static void cross_erase (struct graph *g)
2649 cross_xor (g, g->cross.x, g->cross.y);
2650 g->cross.erase_needed = 0;
2653 static void magnify_create (struct graph *g, int x, int y)
2656 struct element_list *list, *new_list;
2657 struct ipoint pos, offsetpos;
2660 mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
2661 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2663 mg->toplevel = dlg_window_new("tcp graph magnify");
2664 mg->drawing_area = mg->toplevel;
2665 gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2666 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2667 /* | GDK_ENTER_NOTIFY_MASK */
2668 /* | GDK_ALL_EVENTS_MASK */
2673 mg->wp.width = g->magnify.width;
2674 mg->wp.height = g->magnify.height;
2675 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2676 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2677 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2678 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2680 /* in order to keep original element lists intact we need our own */
2681 graph_element_lists_initialize (mg);
2682 list = g->elists->next;
2683 new_list = mg->elists;
2684 for ( ; list; list=list->next) {
2686 (struct element_list * )g_malloc (sizeof (struct element_list));
2687 new_list = new_list->next;
2688 new_list->next = NULL;
2689 new_list->elements = NULL;
2691 graph_element_lists_make (mg);
2693 gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
2694 g->magnify.x = pos.x + x - g->magnify.width/2;
2695 g->magnify.y = pos.y + y - g->magnify.height/2;
2696 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2697 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2698 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2699 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2700 gtk_window_set_position (GTK_WINDOW(mg->drawing_area), GTK_WIN_POS_NONE);
2701 magnify_get_geom (g, x, y);
2703 gtk_widget_show (mg->drawing_area);
2705 /* we need to wait for the first expose event before we start drawing */
2706 while (!gdk_events_pending ());
2708 e = gdk_event_get ();
2710 if (e->any.type == GDK_EXPOSE) {
2718 mg->pixmap[0] = mg->pixmap[1] = NULL;
2719 graph_pixmaps_create (mg);
2721 g->magnify.active = 1;
2724 static void magnify_move (struct graph *g, int x, int y)
2726 struct ipoint pos, offsetpos;
2728 gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
2729 g->magnify.x = pos.x + x - g->magnify.width/2;
2730 g->magnify.y = pos.y + y - g->magnify.height/2;
2731 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2732 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2733 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2734 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2735 magnify_get_geom (g, x, y);
2739 static void magnify_destroy (struct graph *g)
2741 struct element_list *list;
2742 struct graph *mg = g->magnify.g;
2744 window_destroy (GTK_WIDGET (mg->drawing_area));
2745 g_object_unref (mg->pixmap[0]);
2746 g_object_unref (mg->pixmap[1]);
2747 for (list=mg->elists; list; list=list->next)
2748 g_free (list->elements);
2751 while (mg->elists->next) {
2752 list = mg->elists->next->next;
2753 g_free (mg->elists->next);
2754 mg->elists->next = list;
2757 g_free (g->magnify.g);
2758 g->magnify.active = 0;
2761 static void magnify_get_geom (struct graph *g, int x, int y)
2765 gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &posx, &posy);
2767 g->magnify.g->geom.x = g->geom.x;
2768 g->magnify.g->geom.y = g->geom.y;
2770 g->magnify.g->geom.x -=
2771 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2772 ((x-g->geom.x)/(double )g->geom.width));
2773 g->magnify.g->geom.y -=
2774 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2775 ((y-g->geom.y)/(double )g->geom.height));
2777 /* we have coords of origin of graph relative to origin of g->toplevel.
2778 * now we need them to relate to origin of magnify window */
2779 g->magnify.g->geom.x -= (g->magnify.x - posx);
2780 g->magnify.g->geom.y -= (g->magnify.y - posy);
2783 static void magnify_draw (struct graph *g)
2785 int not_disp = 1 ^ g->magnify.g->displayed;
2787 graph_pixmap_draw (g->magnify.g);
2788 /* graph pixmap is almost ready, just add border */
2789 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2790 g->magnify.width - 1, 0);
2791 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2792 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2793 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2794 0, g->magnify.height - 1);
2795 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2796 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2798 graph_pixmaps_switch (g->magnify.g);
2799 graph_pixmap_display (g->magnify.g);
2803 static gboolean configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data _U_)
2805 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2809 int cur_g_width, cur_g_height;
2810 int cur_wp_width, cur_wp_height;
2812 debug(DBS_FENTRY) puts ("configure_event()");
2814 cur_wp_width = g->wp.width;
2815 cur_wp_height = g->wp.height;
2816 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2817 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2818 g->x_axis->s.width = g->wp.width;
2819 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2820 g->y_axis->p.height = g->wp.height + g->wp.y;
2821 g->y_axis->s.height = g->wp.height;
2822 g->x_axis->p.y = g->y_axis->p.height;
2823 zoom.x = (double )g->wp.width / cur_wp_width;
2824 zoom.y = (double )g->wp.height / cur_wp_height;
2825 cur_g_width = g->geom.width;
2826 cur_g_height = g->geom.height;
2827 g->geom.width = (int )rint (g->geom.width * zoom.x);
2828 g->geom.height = (int )rint (g->geom.height * zoom.y);
2829 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2830 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2831 /* g->zoom.initial.x = g->zoom.x; */
2832 /* g->zoom.initial.y = g->zoom.y; */
2834 g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
2835 (g->wp.x - g->geom.x));
2836 g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
2837 (g->wp.y - g->geom.y));
2839 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2840 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2841 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2842 g->zoom.x, g->zoom.y);
2845 update_zoom_spins (g);
2846 graph_element_lists_make (g);
2847 graph_pixmaps_create (g);
2848 graph_title_pixmap_create (g);
2849 axis_pixmaps_create (g->y_axis);
2850 axis_pixmaps_create (g->x_axis);
2851 /* we don't do actual drawing here; we leave it to expose handler */
2852 graph_pixmap_draw (g);
2853 graph_pixmaps_switch (g);
2854 graph_title_pixmap_draw (g);
2855 h_axis_pixmap_draw (g->x_axis);
2856 axis_pixmaps_switch (g->x_axis);
2857 v_axis_pixmap_draw (g->y_axis);
2858 axis_pixmaps_switch (g->y_axis);
2862 static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data _U_)
2864 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2866 debug(DBS_FENTRY) puts ("expose_event()");
2871 /* lower left corner */
2872 gdk_draw_rectangle (gtk_widget_get_window(g->drawing_area), g->bg_gc, TRUE, 0,
2873 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2875 gdk_draw_rectangle (gtk_widget_get_window(g->drawing_area), g->bg_gc, TRUE,
2876 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2878 graph_pixmap_display (g);
2879 graph_title_pixmap_display (g);
2880 axis_pixmap_display (g->x_axis);
2881 axis_pixmap_display (g->y_axis);
2886 static void do_zoom_mouse (struct graph *g, GdkEventButton *event)
2888 int cur_width = g->geom.width, cur_height = g->geom.height;
2889 struct { double x, y; } factor;
2891 if (g->zoom.flags & ZOOM_OUT) {
2892 if (g->zoom.flags & ZOOM_HLOCK)
2895 factor.x = 1 / g->zoom.step_x;
2896 if (g->zoom.flags & ZOOM_VLOCK)
2899 factor.y = 1 / g->zoom.step_y;
2901 if (g->zoom.flags & ZOOM_HLOCK)
2904 factor.x = g->zoom.step_x;
2905 if (g->zoom.flags & ZOOM_VLOCK)
2908 factor.y = g->zoom.step_y;
2911 g->geom.width = (int )rint (g->geom.width * factor.x);
2912 g->geom.height = (int )rint (g->geom.height * factor.y);
2913 if (g->geom.width < g->wp.width)
2914 g->geom.width = g->wp.width;
2915 if (g->geom.height < g->wp.height)
2916 g->geom.height = g->wp.height;
2917 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2918 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2920 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2921 ((event->x-g->geom.x)/(double )cur_width));
2922 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2923 ((event->y-g->geom.y)/(double )cur_height));
2925 if (g->geom.x > g->wp.x)
2926 g->geom.x = g->wp.x;
2927 if (g->geom.y > g->wp.y)
2928 g->geom.y = g->wp.y;
2929 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2930 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2931 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2932 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2934 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2935 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2936 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2937 g->wp.height, g->zoom.x, g->zoom.y);
2939 graph_element_lists_make (g);
2940 g->cross.erase_needed = 0;
2942 axis_display (g->y_axis);
2943 axis_display (g->x_axis);
2944 update_zoom_spins (g);
2946 cross_draw (g, (int) event->x, (int) event->y);
2949 static void do_zoom_keyboard (struct graph *g)
2951 int cur_width = g->geom.width, cur_height = g->geom.height;
2952 struct { double x, y; } factor;
2953 int pointer_x, pointer_y;
2955 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
2957 if (g->zoom.flags & ZOOM_OUT) {
2958 if (g->zoom.flags & ZOOM_HLOCK)
2961 factor.x = 1 / g->zoom.step_x;
2962 if (g->zoom.flags & ZOOM_VLOCK)
2965 factor.y = 1 / g->zoom.step_y;
2967 if (g->zoom.flags & ZOOM_HLOCK)
2970 factor.x = g->zoom.step_x;
2971 if (g->zoom.flags & ZOOM_VLOCK)
2974 factor.y = g->zoom.step_y;
2977 g->geom.width = (int )rint (g->geom.width * factor.x);
2978 g->geom.height = (int )rint (g->geom.height * factor.y);
2979 if (g->geom.width < g->wp.width)
2980 g->geom.width = g->wp.width;
2981 if (g->geom.height < g->wp.height)
2982 g->geom.height = g->wp.height;
2983 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2984 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2986 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2987 ((pointer_x - g->geom.x)/(double )cur_width));
2988 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2989 ((pointer_y - g->geom.y)/(double )cur_height));
2991 if (g->geom.x > g->wp.x)
2992 g->geom.x = g->wp.x;
2993 if (g->geom.y > g->wp.y)
2994 g->geom.y = g->wp.y;
2995 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2996 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2997 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2998 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3000 printf ("key press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
3001 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
3002 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
3003 g->wp.height, g->zoom.x, g->zoom.y);
3006 graph_element_lists_make (g);
3007 g->cross.erase_needed = 0;
3009 axis_display (g->y_axis);
3010 axis_display (g->x_axis);
3011 update_zoom_spins (g);
3013 cross_draw (g, pointer_x, pointer_y);
3016 static void do_zoom_in_keyboard (struct graph *g)
3018 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3019 do_zoom_keyboard (g);
3022 static void do_zoom_out_keyboard (struct graph *g)
3024 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3025 do_zoom_keyboard (g);
3026 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3029 static void do_select_segment (struct graph *g)
3031 int pointer_x, pointer_y;
3033 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3034 graph_select_segment (g, pointer_x, pointer_y);
3037 static void do_wscale_graph (struct graph *g)
3039 gtk_toggle_button_set_active (g->gt.graph_wscale, TRUE);
3042 static void do_rtt_graph (struct graph *g)
3044 gtk_toggle_button_set_active (g->gt.graph_rtt, TRUE);
3047 static void do_throughput_graph (struct graph *g)
3049 gtk_toggle_button_set_active (g->gt.graph_tput, TRUE);
3052 static void do_ts_graph_stevens (struct graph *g)
3054 gtk_toggle_button_set_active (g->gt.graph_tseqstevens, TRUE);
3057 static void do_ts_graph_tcptrace (struct graph *g)
3059 gtk_toggle_button_set_active (g->gt.graph_tseqttrace, TRUE);
3062 static void do_magnify_create (struct graph *g)
3064 int pointer_x, pointer_y;
3066 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3068 magnify_create (g, (int )rint (pointer_x), (int )rint (pointer_y));
3071 static void do_key_motion (struct graph *g)
3073 if (g->geom.x > g->wp.x)
3074 g->geom.x = g->wp.x;
3075 if (g->geom.y > g->wp.y)
3076 g->geom.y = g->wp.y;
3077 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3078 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3079 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3080 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3081 g->cross.erase_needed = 0;
3083 axis_display (g->y_axis);
3084 axis_display (g->x_axis);
3085 if (g->cross.draw) {
3086 int pointer_x, pointer_y;
3087 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3088 cross_draw (g, pointer_x, pointer_y);
3092 static void do_key_motion_up (struct graph *g, int step)
3098 static void do_key_motion_down (struct graph *g, int step)
3104 static void do_key_motion_left (struct graph *g, int step)
3110 static void do_key_motion_right (struct graph *g, int step)
3116 static gboolean button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data _U_)
3118 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3120 debug(DBS_FENTRY) puts ("button_press_event()");
3122 if (event->button == MOUSE_BUTTON_RIGHT) {
3123 if (event->state & GDK_CONTROL_MASK) {
3124 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
3126 g->grab.x = (int )rint (event->x) - g->geom.x;
3127 g->grab.y = (int )rint (event->y) - g->geom.y;
3128 g->grab.grabbed = TRUE;
3130 #ifdef ORIGINAL_WIN32_BUTTONS
3131 /* Windows mouse control: */
3132 /* [<ctrl>-left] - select packet */
3133 /* [left] - zoom in */
3134 /* [<shift>-left] - zoom out */
3135 } else if (event->button == MOUSE_BUTTON_LEFT) {
3136 if (event->state & GDK_CONTROL_MASK) {
3137 graph_select_segment (g, (int)event->x, (int)event->y);
3139 #else /* !ORIGINAL_WIN32_BUTTONS */
3140 } else if (event->button == MOUSE_BUTTON_MIDDLE) {
3142 do_zoom_mouse(g, event);
3143 #ifndef ORIGINAL_WIN32_BUTTONS
3144 } else if (event->button == MOUSE_BUTTON_LEFT) {
3145 graph_select_segment (g, (int )event->x, (int )event->y);
3146 #else /* ORIGINAL_WIN32_BUTTONS*/
3153 static gboolean motion_notify_event (GtkWidget *widget, GdkEventMotion *event, gpointer user_data _U_)
3155 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3157 GdkModifierType state;
3159 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
3162 gdk_window_get_pointer (event->window, &x, &y, &state);
3166 state = event->state;
3169 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
3170 * is pressed while pointer is in motion, we will receive one more motion
3171 * notify *before* we get the button press. This last motion notify works
3172 * with stale grab coordinates */
3173 if (state & GDK_BUTTON3_MASK) {
3174 if (g->grab.grabbed) {
3175 g->geom.x = x-g->grab.x;
3176 g->geom.y = y-g->grab.y;
3178 if (g->geom.x > g->wp.x)
3179 g->geom.x = g->wp.x;
3180 if (g->geom.y > g->wp.y)
3181 g->geom.y = g->wp.y;
3182 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3183 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3184 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3185 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3186 g->cross.erase_needed = 0;
3188 axis_display (g->y_axis);
3189 axis_display (g->x_axis);
3191 cross_draw (g, x, y);
3192 } else if (g->magnify.active)
3193 magnify_move (g, x, y);
3194 } else if (state & GDK_BUTTON1_MASK) {
3195 graph_select_segment (g, x, y);
3196 if (g->cross.erase_needed)
3199 cross_draw (g, x, y);
3201 if (g->cross.erase_needed)
3204 cross_draw (g, x, y);
3210 static gboolean button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data _U_)
3212 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3214 debug(DBS_FENTRY) puts ("button_release_event()");
3216 if (event->button == MOUSE_BUTTON_RIGHT)
3217 g->grab.grabbed = FALSE;
3219 if (g->magnify.active)
3220 magnify_destroy (g);
3224 static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data _U_)
3226 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3229 debug(DBS_FENTRY) puts ("key_press_event()");
3231 if((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK))
3233 else if (event->state & GDK_CONTROL_MASK)
3235 else if (event->state & GDK_SHIFT_MASK)
3240 switch (event->keyval) {
3242 toggle_crosshairs (g);
3245 toggle_time_origin (g);
3248 toggle_seq_origin (g);
3252 restore_initial_graph_view (g);
3256 do_zoom_in_keyboard (g);
3260 do_zoom_out_keyboard (g);
3263 do_magnify_create (g);
3266 do_select_segment (g);
3272 do_throughput_graph (g);
3275 do_ts_graph_stevens (g);
3278 do_ts_graph_tcptrace (g);
3284 do_key_motion_left (g, step);
3287 do_key_motion_up (g, step);
3290 do_key_motion_right (g, step);
3293 do_key_motion_down (g, step);
3296 callback_create_help (NULL, NULL);
3304 static gboolean key_release_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data _U_)
3306 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3308 debug(DBS_FENTRY) puts ("key_release_event()");
3310 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3311 /* g->zoom.flags &= ~ZOOM_OUT; */
3312 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3317 static gboolean leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data _U_)
3319 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3321 if (g->cross.erase_needed)
3327 static gboolean enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data _U_)
3329 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3331 /* graph_pixmap_display (g); */
3332 if (g->cross.draw) {
3334 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
3335 cross_draw (g, x, y);
3340 static void toggle_crosshairs (struct graph *g)
3344 if (g->cross.draw) {
3346 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
3348 } else if (g->cross.erase_needed) {
3352 /* toggle buttons emit their "toggled" signals so don't bother doing
3353 * any real work here, it will be done in signal handlers */
3355 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
3357 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
3360 static void toggle_time_origin (struct graph *g)
3363 case GRAPH_TSEQ_STEVENS:
3364 tseq_stevens_toggle_time_origin (g);
3366 case GRAPH_TSEQ_TCPTRACE:
3367 tseq_tcptrace_toggle_time_origin (g);
3369 case GRAPH_THROUGHPUT:
3370 tput_toggle_time_origin (g);
3375 axis_display (g->x_axis);
3378 static void toggle_seq_origin (struct graph *g)
3381 case GRAPH_TSEQ_STEVENS:
3382 tseq_stevens_toggle_seq_origin (g);
3383 axis_display (g->y_axis);
3385 case GRAPH_TSEQ_TCPTRACE:
3386 tseq_tcptrace_toggle_seq_origin (g);
3387 axis_display (g->y_axis);
3390 rtt_toggle_seq_origin (g);
3391 axis_display (g->x_axis);
3398 static void restore_initial_graph_view (struct graph *g)
3400 g->geom.width = g->wp.width;
3401 g->geom.height = g->wp.height;
3402 g->geom.x = g->wp.x;
3403 g->geom.y = g->wp.y;
3404 graph_init_sequence (g);
3407 static int get_num_dsegs (struct graph *g)
3410 struct segment *tmp;
3412 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3413 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3414 g->current->th_sport, g->current->th_dport,
3415 &tmp->ip_src, &tmp->ip_dst,
3416 tmp->th_sport, tmp->th_dport,
3417 COMPARE_CURR_DIR)) {
3424 static int get_num_acks (struct graph *g)
3427 struct segment *tmp;
3429 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3430 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3431 g->current->th_sport, g->current->th_dport,
3432 &tmp->ip_src, &tmp->ip_dst,
3433 tmp->th_sport, tmp->th_dport,
3434 COMPARE_CURR_DIR)) {
3442 * Stevens-style time-sequence grapH
3445 static void tseq_stevens_read_config (struct graph *g)
3447 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3449 g->s.tseq_stevens.seq_width = 4;
3450 g->s.tseq_stevens.seq_height = 4;
3451 g->s.tseq_stevens.flags = 0;
3453 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3454 g->title[0] = "Time/Sequence Graph (Stevens)";
3456 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3457 g->y_axis->label[0] = "number[B]";
3458 g->y_axis->label[1] = "Sequence";
3459 g->y_axis->label[2] = NULL;
3460 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3461 g->x_axis->label[0] = "Time[s]";
3462 g->x_axis->label[1] = NULL;
3465 /* Used by both 'stevens' and 'tcptrace' */
3466 static void tseq_initialize (struct graph *g)
3468 debug(DBS_FENTRY) puts ("tseq_initialize()");
3469 tseq_get_bounds (g);
3475 case GRAPH_TSEQ_STEVENS:
3476 tseq_stevens_read_config(g);
3478 case GRAPH_TSEQ_TCPTRACE:
3479 tseq_tcptrace_read_config(g);
3485 /* Determine "bounds"
3486 * Essentially: look for lowest/highest time and seq in the list of segments
3487 * Note that for tcptrace the "(ack + window) sequence number" would normally be expected
3488 * to be the upper bound; However, just to be safe, include the data seg sequence numbers
3489 * in the comparison for tcptrace
3490 * (e.g. to handle the case of only data segments).
3493 /* ToDo: worry about handling cases such as trying to plot seq of just 1 frame */
3495 static void tseq_get_bounds (struct graph *g)
3497 struct segment *tmp;
3499 gboolean data_frame_seen=FALSE;
3500 double data_tim_low=0;
3501 double data_tim_high=0;
3502 guint32 data_seq_cur;
3503 guint32 data_seq_nxt;
3504 guint32 data_seq_low=0;
3505 guint32 data_seq_high=0;
3506 gboolean ack_frame_seen=FALSE;
3507 double ack_tim_low=0;
3508 double ack_tim_high=0;
3509 guint32 ack_seq_cur;
3510 guint32 ack_seq_low=0;
3511 guint32 win_seq_cur;
3512 guint32 win_seq_high=0;
3514 /* go thru all segments to determine "bounds" */
3515 for (tmp=g->segments; tmp; tmp=tmp->next) {
3516 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3517 g->current->th_sport, g->current->th_dport,
3518 &tmp->ip_src, &tmp->ip_dst,
3519 tmp->th_sport, tmp->th_dport,
3520 COMPARE_CURR_DIR)) {
3523 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3524 data_seq_cur = tmp->th_seq;
3525 data_seq_nxt = data_seq_cur + tmp->th_seglen;
3526 if (! data_frame_seen) {
3527 data_tim_low = data_tim_high = tim;
3528 data_seq_low = data_seq_cur;
3529 data_seq_high = data_seq_nxt;
3530 data_frame_seen = TRUE;
3532 if (tim < data_tim_low) data_tim_low = tim;
3533 if (tim > data_tim_high) data_tim_high = tim;
3534 if (data_seq_cur < data_seq_low) data_seq_low = data_seq_cur;
3535 if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
3537 else { /* ack seg */
3538 /* skip ack processing if no ACK (e.g. in RST) */
3539 if (TCP_ACK (tmp->th_flags)) {
3540 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3541 ack_seq_cur = tmp->th_ack;
3542 win_seq_cur = ack_seq_cur + tmp->th_win;
3543 if (! ack_frame_seen) {
3544 ack_tim_low = ack_tim_high = tim;
3545 ack_seq_low = ack_seq_cur;
3546 win_seq_high = win_seq_cur;
3547 ack_frame_seen = TRUE;
3549 if (tim < ack_tim_low) ack_tim_low = tim;
3550 if (tim > ack_tim_high) ack_tim_high = tim;
3551 if (ack_seq_cur < ack_seq_low) ack_seq_low = ack_seq_cur;
3552 if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
3557 /* if 'stevens': use only data segments to determine bounds */
3558 /* if 'tcptrace': use both data and ack segments to determine bounds */
3560 case GRAPH_TSEQ_STEVENS:
3561 g->bounds.x0 = data_tim_low;
3562 g->bounds.width = data_tim_high - data_tim_low;
3563 g->bounds.y0 = data_seq_low;
3564 g->bounds.height = data_seq_high - data_seq_low;
3566 case GRAPH_TSEQ_TCPTRACE:
3567 /* If (ack_frame_seen == false) -> use 'data' segments.
3568 * Else If (data_frame_seen == false) -> use 'ack' segments.
3569 * Else -> use both data and ack segments.
3571 g->bounds.x0 = ((data_tim_low <= ack_tim_low && data_frame_seen) || (! ack_frame_seen)) ? data_tim_low : ack_tim_low;
3572 g->bounds.width = (((data_tim_high >= ack_tim_high && data_frame_seen) || (! ack_frame_seen)) ? data_tim_high : ack_tim_high) - g->bounds.x0;
3573 g->bounds.y0 = ((data_seq_low <= ack_seq_low && data_frame_seen) || (! ack_frame_seen)) ? data_seq_low : ack_seq_low;
3574 g->bounds.height = (((data_seq_high >= win_seq_high && data_frame_seen) || (! ack_frame_seen)) ? data_seq_high : win_seq_high) - g->bounds.y0;
3578 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3579 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3583 static void tseq_stevens_make_elmtlist (struct graph *g)
3585 struct segment *tmp;
3586 struct element *elements, *e;
3587 double xx0 = g->bounds.x0, yy0 = g->bounds.y0;
3588 guint32 seq_base = (guint32) yy0;
3591 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3592 if (g->elists->elements == NULL) {
3593 int n = 1 + get_num_dsegs (g);
3594 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3596 e = elements = g->elists->elements;
3598 for (tmp=g->segments; tmp; tmp=tmp->next) {
3601 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3602 g->current->th_sport, g->current->th_dport,
3603 &tmp->ip_src, &tmp->ip_dst,
3604 tmp->th_sport, tmp->th_dport,
3605 COMPARE_CURR_DIR)) {
3609 seq_cur = tmp->th_seq - seq_base;
3610 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - xx0);
3611 seqno = g->zoom.y * seq_cur;
3616 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3617 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3618 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3619 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3620 e->p.arc.filled = TRUE;
3621 e->p.arc.angle1 = 0;
3622 e->p.arc.angle2 = 23040;
3625 e->type = ELMT_NONE;
3626 g->elists->elements = elements;
3629 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3631 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3633 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3634 g->y_axis->min = g->bounds.y0;
3635 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3639 static void tseq_stevens_toggle_time_origin (struct graph *g)
3641 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3643 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3644 g->x_axis->min = g->bounds.x0;
3645 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3650 * tcptrace-style time-sequence graph
3653 static void tseq_tcptrace_read_config (struct graph *g)
3655 GdkColormap *colormap;
3658 g->s.tseq_tcptrace.flags = 0;
3659 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
3660 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
3661 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
3662 colormap = gtk_widget_get_colormap (GTK_WIDGET(g->drawing_area));
3663 if (!gdk_color_parse ("black", &color)) {
3665 * XXX - do more than just warn.
3667 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3668 "Could not parse color black.");
3670 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3672 * XXX - do more than just warn.
3674 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3675 "Could not allocate color black.");
3677 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3678 if (!gdk_color_parse ("LightSlateGray", &color)) {
3680 * XXX - do more than just warn.
3682 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3683 "Could not parse color LightSlateGray.");
3685 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3687 * XXX - do more than just warn.
3689 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3690 "Could not allocate color LightSlateGray.");
3692 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3693 if (!gdk_color_parse ("LightGray", &color)) {
3695 * XXX - do more than just warn.
3697 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3698 "Could not parse color LightGray.");
3700 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3702 * XXX - do more than just warn.
3704 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3705 "Could not allocate color LightGray.");
3707 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3709 g->elists->next = (struct element_list * )
3710 g_malloc (sizeof (struct element_list));
3711 g->elists->next->next = NULL;
3712 g->elists->next->elements = NULL;
3714 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3715 g->title[0] = "Time/Sequence Graph (tcptrace)";
3717 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3718 g->y_axis->label[0] = "number[B]";
3719 g->y_axis->label[1] = "Sequence";
3720 g->y_axis->label[2] = NULL;
3721 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3722 g->x_axis->label[0] = "Time[s]";
3723 g->x_axis->label[1] = NULL;
3726 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3728 struct segment *tmp;
3729 struct element *elements0, *e0; /* list of elmts with prio 0 */
3730 struct element *elements1, *e1; /* list of elmts with prio 1 */
3732 double p_t = 0; /* ackno, window and time of previous segment */
3733 double p_ackno = 0, p_win = 0;
3734 gboolean ack_seen=FALSE;
3739 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3741 if (g->elists->elements == NULL) {
3742 int n = 1 + 4*get_num_acks(g);
3743 e0 = elements0 = (struct element * )g_malloc (n*sizeof (struct element));
3745 e0 = elements0 = g->elists->elements;
3747 if (g->elists->next->elements == NULL ) {
3748 int n = 1 + 3*get_num_dsegs(g);
3749 e1 = elements1 = (struct element * )g_malloc (n*sizeof (struct element));
3751 e1 = elements1 = g->elists->next->elements;
3755 seq_base = (guint32) yy0;
3757 for (tmp=g->segments; tmp; tmp=tmp->next) {
3761 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3764 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3765 g->current->th_sport, g->current->th_dport,
3766 &tmp->ip_src, &tmp->ip_dst,
3767 tmp->th_sport, tmp->th_dport,
3768 COMPARE_CURR_DIR)) {
3769 /* forward direction -> we need seqno and amount of data */
3772 seq_cur = tmp->th_seq - seq_base;
3773 if (TCP_SYN (tmp->th_flags) || TCP_FIN (tmp->th_flags))
3776 data = tmp->th_seglen;
3778 yy1 = g->zoom.y * (seq_cur);
3779 yy2 = g->zoom.y * (seq_cur + data);
3780 e1->type = ELMT_LINE;
3782 e1->gc = g->s.tseq_tcptrace.gc_seq;
3783 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3784 e1->p.line.dim.y1 = yy1;
3785 e1->p.line.dim.y2 = yy2;
3787 e1->type = ELMT_LINE;
3789 e1->gc = g->s.tseq_tcptrace.gc_seq;
3790 e1->p.line.dim.x1 = x - 1;
3791 e1->p.line.dim.x2 = x + 1;
3792 e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy1;
3794 e1->type = ELMT_LINE;
3796 e1->gc = g->s.tseq_tcptrace.gc_seq;
3797 e1->p.line.dim.x1 = x + 1;
3798 e1->p.line.dim.x2 = x - 1;
3799 e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy2;
3803 if (! TCP_ACK (tmp->th_flags))
3804 /* SYN's and RST's do not necessarily have ACK's*/
3806 /* backward direction -> we need ackno and window */
3807 seq_cur = tmp->th_ack - seq_base;
3808 ackno = seq_cur * g->zoom.y;
3809 win = tmp->th_win * g->zoom.y;
3812 if (ack_seen == TRUE) { /* don't plot the first ack */
3813 e0->type = ELMT_LINE;
3815 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3816 e0->p.line.dim.x1 = p_t;
3817 e0->p.line.dim.y1 = p_ackno;
3818 e0->p.line.dim.x2 = x;
3819 e0->p.line.dim.y2 = p_ackno;
3821 e0->type = ELMT_LINE;
3823 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3824 e0->p.line.dim.x1 = x;
3825 e0->p.line.dim.y1 = p_ackno;
3826 e0->p.line.dim.x2 = x;
3827 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3830 e0->type = ELMT_LINE;
3832 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3833 e0->p.line.dim.x1 = p_t;
3834 e0->p.line.dim.y1 = p_win + p_ackno;
3835 e0->p.line.dim.x2 = x;
3836 e0->p.line.dim.y2 = p_win + p_ackno;
3838 e0->type = ELMT_LINE;
3840 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3841 e0->p.line.dim.x1 = x;
3842 e0->p.line.dim.y1 = p_win + p_ackno;
3843 e0->p.line.dim.x2 = x;
3844 e0->p.line.dim.y2 = win + ackno;
3854 e0->type = ELMT_NONE;
3855 e1->type = ELMT_NONE;
3856 g->elists->elements = elements0;
3857 g->elists->next->elements = elements1;
3860 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3862 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3864 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3865 g->y_axis->min = g->bounds.y0;
3866 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3870 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3872 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3874 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3875 g->x_axis->min = g->bounds.x0;
3876 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3884 static void tput_make_elmtlist (struct graph *g)
3886 struct segment *tmp, *oldest;
3887 struct element *elements, *e;
3891 if (g->elists->elements == NULL) {
3892 int n = 1 + get_num_dsegs (g);
3893 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3895 e = elements = g->elists->elements;
3897 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3898 double time_val = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3899 dtime = time_val - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3900 if (i>g->s.tput.nsegs) {
3901 sum -= oldest->th_seglen;
3902 oldest=oldest->next;
3904 sum += tmp->th_seglen;
3906 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3911 e->p.arc.dim.width = g->s.tput.width;
3912 e->p.arc.dim.height = g->s.tput.height;
3913 e->p.arc.dim.x = g->zoom.x*(time_val - g->bounds.x0) - g->s.tput.width/2.0;
3914 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3915 e->p.arc.filled = TRUE;
3916 e->p.arc.angle1 = 0;
3917 e->p.arc.angle2 = 23040;
3920 e->type = ELMT_NONE;
3921 g->elists->elements = elements;
3924 /* Purpose of <graph_type>_initialize functions:
3925 * - find maximum and minimum for both axes
3926 * - call setup routine for style struct */
3927 static void tput_initialize (struct graph *g)
3929 struct segment *tmp, *oldest, *last;
3931 double dtime, tput, tputmax=0;
3932 double t, t0, tmax = 0, yy0, ymax;
3934 debug(DBS_FENTRY) puts ("tput_initialize()");
3936 tput_read_config(g);
3938 for (last=g->segments; last->next; last=last->next);
3939 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3940 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3941 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3942 if (i>g->s.tput.nsegs) {
3943 sum -= oldest->th_seglen;
3944 oldest=oldest->next;
3946 sum += tmp->th_seglen;
3948 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3951 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3956 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3962 g->bounds.width = tmax - t0;
3963 g->bounds.height = ymax - yy0;
3964 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3965 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3968 static void tput_read_config (struct graph *g)
3970 debug(DBS_FENTRY) puts ("tput_read_config()");
3972 g->s.tput.width = 4;
3973 g->s.tput.height = 4;
3974 g->s.tput.nsegs = 20;
3976 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3977 g->title[0] = "Throughput Graph";
3979 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3980 g->y_axis->label[0] = "[B/s]";
3981 g->y_axis->label[1] = "Throughput";
3982 g->y_axis->label[2] = NULL;
3983 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3984 g->x_axis->label[0] = "Time[s]";
3985 g->x_axis->label[1] = NULL;
3986 g->s.tput.flags = 0;
3989 static void tput_toggle_time_origin (struct graph *g)
3991 g->s.tput.flags ^= TIME_ORIGIN;
3993 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3994 g->x_axis->min = g->bounds.x0;
3995 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4001 static void rtt_read_config (struct graph *g)
4003 debug(DBS_FENTRY) puts ("rtt_read_config()");
4006 g->s.rtt.height = 4;
4009 g->title = (const char ** )g_malloc (2 * sizeof (char *));
4010 g->title[0] = "Round Trip Time Graph";
4012 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4013 g->y_axis->label[0] = "RTT [s]";
4014 g->y_axis->label[1] = NULL;
4015 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4016 g->x_axis->label[0] = "Sequence Number[B]";
4017 g->x_axis->label[1] = NULL;
4020 static void rtt_initialize (struct graph *g)
4022 struct segment *tmp, *first=NULL;
4023 struct unack *unack = NULL, *u;
4025 double xx0, yy0, ymax;
4027 guint32 seq_base = 0;
4029 debug(DBS_FENTRY) puts ("rtt_initialize()");
4031 rtt_read_config (g);
4033 for (tmp=g->segments; tmp; tmp=tmp->next) {
4034 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
4035 g->current->th_sport, g->current->th_dport,
4036 &tmp->ip_src, &tmp->ip_dst,
4037 tmp->th_sport, tmp->th_dport,
4038 COMPARE_CURR_DIR)) {
4039 guint32 seqno = tmp->th_seq;
4046 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
4047 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4048 u = rtt_get_new_unack (time_val, seqno);
4050 rtt_put_unack_on_list (&unack, u);
4053 if (seqno + tmp->th_seglen > xmax)
4054 xmax = seqno + tmp->th_seglen;
4056 guint32 ackno = tmp->th_ack -seq_base;
4057 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4060 for (u=unack; u; u=v)
4061 if (ackno > u->seqno) {
4062 double rtt = time_val - u->time;
4066 rtt_delete_unack_from_list (&unack, u);
4078 g->bounds.width = xmax;
4079 g->bounds.height = ymax - yy0;
4080 g->zoom.x = g->geom.width / g->bounds.width;
4081 g->zoom.y = g->geom.height / g->bounds.height;
4084 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
4088 for (u=list; u; u=u->next)
4089 if (u->seqno== seqno)
4095 static struct unack *rtt_get_new_unack (double time_val, unsigned int seqno)
4099 u = (struct unack * )g_malloc (sizeof (struct unack));
4108 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
4110 struct unack *u, *list = *l;
4112 for (u=list; u; u=u->next)
4122 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
4124 struct unack *u, *list = *l;
4133 for (u=list; u; u=u->next)
4134 if (u->next == dead) {
4135 u->next = u->next->next;
4141 static void rtt_make_elmtlist (struct graph *g)
4143 struct segment *tmp;
4144 struct unack *unack = NULL, *u;
4145 struct element *elements, *e;
4146 guint32 seq_base = (guint32) g->bounds.x0;
4148 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
4150 if (g->elists->elements == NULL) {
4151 int n = 1 + get_num_dsegs (g);
4152 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
4154 e = elements = g->elists->elements;
4157 for (tmp=g->segments; tmp; tmp=tmp->next) {
4158 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
4159 g->current->th_sport, g->current->th_dport,
4160 &tmp->ip_src, &tmp->ip_dst,
4161 tmp->th_sport, tmp->th_dport,
4162 COMPARE_CURR_DIR)) {
4163 guint32 seqno = tmp->th_seq -seq_base;
4165 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
4166 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4167 u = rtt_get_new_unack (time_val, seqno);
4169 rtt_put_unack_on_list (&unack, u);
4172 guint32 ackno = tmp->th_ack -seq_base;
4173 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4176 for (u=unack; u; u=v)
4177 if (ackno > u->seqno) {
4178 double rtt = time_val - u->time;
4183 e->p.arc.dim.width = g->s.rtt.width;
4184 e->p.arc.dim.height = g->s.rtt.height;
4185 e->p.arc.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
4186 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
4187 e->p.arc.filled = TRUE;
4188 e->p.arc.angle1 = 0;
4189 e->p.arc.angle2 = 23040;
4193 rtt_delete_unack_from_list (&unack, u);
4198 e->type = ELMT_NONE;
4199 g->elists->elements = elements;
4202 static void rtt_toggle_seq_origin (struct graph *g)
4204 g->s.rtt.flags ^= SEQ_ORIGIN;
4206 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
4207 g->x_axis->min = g->bounds.x0;
4214 static void wscale_read_config(struct graph* g)
4216 debug(DBS_FENTRY) puts ("wscale_read_config()");
4218 g->s.wscale.win_width = 4;
4219 g->s.wscale.win_height = 4;
4220 g->s.wscale.flags = 0;
4222 g->title = (const char ** )g_malloc (2 * sizeof (char *));
4223 g->title[0] = "Window Scaling Graph";
4225 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4226 g->y_axis->label[0] = "[bytes]";
4227 g->y_axis->label[1] = "Windowsize";
4228 g->y_axis->label[2] = NULL;
4229 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4230 g->x_axis->label[0] = "Time [s]";
4231 g->x_axis->label[1] = NULL;
4235 (1) Find maximum and minimum values for Window-Size(scaled) and seconds
4236 (2) call function to define window related values
4238 static void wscale_initialize(struct graph* g)
4241 struct segment* segm = NULL;
4242 guint32 wsize_max = 0;
4243 guint32 wsize_min = 0;
4244 gdouble sec_max = 0.0;
4245 gdouble sec_base = -1.0;
4247 wscale_read_config (g);
4249 debug(DBS_FENTRY) puts ("wscale_initialize()");
4251 for (segm = g->segments; segm; segm = segm->next)
4253 if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
4254 g->current->th_sport, g->current->th_dport,
4255 &segm->ip_src, &segm->ip_dst,
4256 segm->th_sport, segm->th_dport,
4259 gdouble sec = segm->rel_secs + ( segm->rel_usecs / 1000000.0 );
4260 guint16 flags = segm->th_flags;
4261 guint32 wsize = segm->th_win;
4263 /* only data segments */
4264 if ( (flags & (TH_SYN|TH_RST)) == 0 )
4265 if ( wsize > wsize_max )
4268 /* remind time of first probe */
4269 if ( sec_base < 0 && sec > 0 )
4272 if ( sec_max < sec )
4280 g->bounds.y0 = wsize_min;
4281 g->bounds.width = sec_max - sec_base + 5;
4282 g->bounds.height = wsize_max + 5;
4283 g->zoom.x = g->geom.width / g->bounds.width;
4284 g->zoom.y = g->geom.height / g->bounds.height;
4289 (1) Fill & allocate memory for segments times elements,
4291 static void wscale_make_elmtlist(struct graph* g)
4293 struct segment* segm = NULL;
4294 struct element* elements = NULL;
4295 struct element* e = NULL;
4296 gdouble sec_base = -1.0;
4298 debug(DBS_FENTRY) puts ("wscale_make_elmtlist()");
4300 /* Allocate memory for elements if not already done */
4301 if (g->elists->elements == NULL)
4303 int n = 1 + get_num_dsegs(g);
4304 e = elements = (struct element*)g_malloc(n*sizeof(struct element));
4307 e = elements = g->elists->elements;
4310 for ( segm = g->segments; segm; segm = segm->next )
4312 if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
4313 g->current->th_sport, g->current->th_dport,
4314 &segm->ip_src, &segm->ip_dst,
4315 segm->th_sport, segm->th_dport,
4318 gdouble sec = segm->rel_secs + (segm->rel_usecs / 1000000.0);
4319 guint16 flags = segm->th_flags;
4320 guint32 wsize = segm->th_win;
4322 /* remind time of first probe */
4323 if ( sec_base < 0 && sec > 0 )
4326 /* only data or ack segments */
4327 if ( (flags & (TH_SYN|TH_RST)) == 0 )
4332 e->p.arc.dim.width = g->s.wscale.win_width;
4333 e->p.arc.dim.height = g->s.wscale.win_height;
4334 e->p.arc.dim.x = g->zoom.x * (sec - sec_base) - g->s.wscale.win_width / 2.0;
4335 e->p.arc.dim.y = g->zoom.y * wsize - g->s.wscale.win_height / 2.0;
4336 e->p.arc.filled = TRUE;
4337 e->p.arc.angle1 = 0;
4338 e->p.arc.angle2 = 0x5A00;
4343 /* finished populating element list */
4344 e->type = ELMT_NONE;
4345 g->elists->elements = elements;
4349 #if defined(_WIN32) && !defined(__MINGW32__)
4350 /* replacement of Unix rint() for Windows */
4351 static int rint (double x)
4356 buf = _fcvt(x, 0, &dec, &sig);
4365 #ifdef MAIN_MENU_USE_UIMANAGER
4366 gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
4369 gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
4372 return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
4377 register_tap_listener_tcp_graph(void)
4379 #ifdef MAIN_MENU_USE_UIMANAGER
4381 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (Stevens)", REGISTER_STAT_GROUP_UNSORTED,
4382 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(0));
4383 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (tcptrace)", REGISTER_STAT_GROUP_UNSORTED,
4384 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(1));
4385 register_stat_menu_item("TCP Stream Graph/Throughput Graph", REGISTER_STAT_GROUP_UNSORTED,
4386 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(2));
4387 register_stat_menu_item("TCP Stream Graph/Round Trip Time Graph", REGISTER_STAT_GROUP_UNSORTED,
4388 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(3));
4389 register_stat_menu_item("TCP Stream Graph/Window Scaling Graph", REGISTER_STAT_GROUP_UNSORTED,
4390 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(GRAPH_WSCALE));