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.
34 #include <gdk/gdkkeysyms.h>
36 #include <epan/ipproto.h>
38 #include <epan/packet.h>
39 #include <epan/etypes.h>
40 #include <epan/ppptypes.h>
41 #include <epan/epan_dissect.h>
42 #include <epan/dissectors/packet-tcp.h>
43 #include <epan/address.h>
46 #include "../globals.h"
47 #include "../simple_dialog.h"
49 #include "../stat_menu.h"
51 #include "gtk/gui_utils.h"
52 #include "gtk/dlg_utils.h"
53 #include "gtk/gui_stat_menu.h"
63 #define TCP_SYN(flags) ( flags & TH_SYN )
64 #define TCP_ACK(flags) ( flags & TH_ACK )
65 #define TCP_FIN(flags) ( flags & TH_FIN )
68 #define TXT_HEIGHT 550
70 /* for compare_headers() */
71 /* segment went the same direction as the currently selected one */
72 #define COMPARE_CURR_DIR 0
73 #define COMPARE_ANY_DIR 1
75 /* initalize_axis() */
76 #define AXIS_HORIZONTAL 0
77 #define AXIS_VERTICAL 1
91 guint32 th_win; /* make it 32 bits so we can handle some scaling */
100 double x, y, width, height;
104 double x1, y1, x2, y2;
108 int x, y, width, height;
140 struct segment *parent;
142 struct arc_params arc;
143 struct rect_params rect;
144 struct line_params line;
148 struct element_list {
149 struct element_list *next;
150 struct element *elements;
154 struct graph *g; /* which graph we belong to */
155 GtkWidget *drawing_area;
156 GdkPixmap *pixmap[2];
158 #define AXIS_ORIENTATION 1 << 0
160 /* dim and orig (relative to origin of window) of axis' pixmap */
162 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
165 gdouble major, minor; /* major and minor ticks */
169 #define HAXIS_INIT_HEIGHT 70
170 #define VAXIS_INIT_WIDTH 100
171 #define TITLEBAR_HEIGHT 50
172 #define RMARGIN_WIDTH 30
174 struct style_tseq_tcptrace {
180 struct style_tseq_stevens {
198 #define SEQ_ORIGIN 0x1
199 /* show absolute sequence numbers (not differences from isn) */
200 #define SEQ_ORIGIN_ZERO 0x1
201 #define SEQ_ORIGIN_ISN 0x0
202 #define TIME_ORIGIN 0x10
203 /* show time from beginning of capture as opposed to time from beginning
204 * of the connection */
205 #define TIME_ORIGIN_CAP 0x10
206 #define TIME_ORIGIN_CONN 0x0
208 /* this is used by rtt module only */
217 int draw; /* indicates whether we should draw cross at all */
219 GtkToggleButton *on_toggle;
220 GtkToggleButton *off_toggle;
224 double x0, y0, width, height;
233 double step_x, step_y;
235 #define ZOOM_OUT (1 << 0)
236 #define ZOOM_HLOCK (1 << 1)
237 #define ZOOM_VLOCK (1 << 2)
238 #define ZOOM_STEPS_SAME (1 << 3)
239 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
241 /* unfortunately, we need them both because gtk_toggle_button_set_active ()
242 * with second argument FALSE doesn't do anything, somehow */
244 GtkToggleButton *in_toggle;
245 GtkToggleButton *out_toggle;
248 GtkSpinButton *h_step;
249 GtkSpinButton *v_step;
261 struct ipoint offset;
265 #define MAGZOOMS_SAME (1U << 0)
266 #define MAGZOOMS_SAME_RATIO (1U << 1)
267 #define MAGZOOMS_IGNORE (1U << 31)
270 GtkSpinButton *h_zoom, *v_zoom;
276 #define GRAPH_TSEQ_STEVENS 0
277 #define GRAPH_TSEQ_TCPTRACE 1
278 #define GRAPH_THROUGHPUT 2
281 #define GRAPH_DESTROYED (1 << 0)
282 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
284 GtkWidget *toplevel; /* keypress handler needs this */
285 GtkWidget *drawing_area;
286 GtkWidget *text; /* text widget for seg list - probably
288 PangoFontDescription *font; /* font used for annotations etc. */
291 GdkPixmap *title_pixmap;
292 GdkPixmap *pixmap[2];
293 int displayed; /* which of both pixmaps is on screen right now */
295 GtkWidget *control_panel;
296 /* this belongs to style structs of graph types that make use of it */
297 GtkToggleButton *time_orig_conn, *seq_orig_isn;
300 /* Next 4 attribs describe the graph in natural units, before any scaling.
301 * For example, if we want to display graph of TCP conversation that
302 * started 112.309845 s after beginning of the capture and ran until
303 * 479.093582 s, 237019 B went through the connection (in one direction)
304 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
305 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
306 struct bounds bounds;
307 /* dimensions and position of the graph, both expressed already in pixels.
308 * x and y give the position of upper left corner of the graph relative
309 * to origin of the graph window, size is basically bounds*zoom */
311 /* viewport (=graph window area which is reserved for graph itself), its
312 * size and position relative to origin of the graph window */
315 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
316 * pixels, we have to scale the graph down by factor of 0.002109. This
317 * number would be zoom.y. Obviously, both directions have separate zooms.*/
320 struct magnify magnify;
321 struct axis *x_axis, *y_axis;
322 struct segment *segments;
323 struct segment *current;
324 struct element_list *elists; /* element lists */
326 struct style_tseq_stevens tseq_stevens;
327 struct style_tseq_tcptrace tseq_tcptrace;
328 struct style_tput tput;
329 struct style_rtt rtt;
333 static GdkGC *xor_gc = NULL;
336 #define debug(section) if (debugging & section)
337 /* print function entry points */
338 #define DBS_FENTRY (1 << 0)
339 #define DBS_AXES_TICKS (1 << 1)
340 #define DBS_AXES_DRAWING (1 << 2)
341 #define DBS_GRAPH_DRAWING (1 << 3)
342 #define DBS_TPUT_ELMTS (1 << 4)
343 /*int debugging = DBS_FENTRY;*/
344 static int debugging = 0;
345 /*int debugging = DBS_AXES_TICKS;*/
346 /*int debugging = DBS_AXES_DRAWING;*/
347 /*int debugging = DBS_GRAPH_DRAWING;*/
348 /*int debugging = DBS_TPUT_ELMTS;*/
350 static void create_gui (struct graph * );
352 static void create_text_widget (struct graph * );
353 static void display_text (struct graph * );
355 static void create_drawing_area (struct graph * );
356 static void control_panel_create (struct graph * );
357 static GtkWidget *control_panel_create_zoom_group (struct graph * );
358 static GtkWidget *control_panel_create_magnify_group (struct graph * );
359 static GtkWidget *control_panel_create_cross_group (struct graph * );
360 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
361 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
362 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
363 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
364 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
365 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
366 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
367 static void callback_toplevel_destroy (GtkWidget * , gpointer );
368 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
369 static void callback_close (GtkWidget * , gpointer );
370 static void callback_time_origin (GtkWidget * , gpointer );
371 static void callback_seq_origin (GtkWidget * , gpointer );
372 static void callback_zoomlock_h (GtkWidget * , gpointer );
373 static void callback_zoomlock_v (GtkWidget * , gpointer );
374 static void callback_zoom_inout (GtkWidget * , gpointer );
375 static void callback_zoom_step (GtkWidget * , gpointer );
376 static void callback_zoom_flags (GtkWidget * , gpointer );
377 static void callback_cross_on_off (GtkWidget * , gpointer );
378 static void callback_mag_width (GtkWidget * , gpointer );
379 static void callback_mag_height (GtkWidget * , gpointer );
380 static void callback_mag_x (GtkWidget * , gpointer );
381 static void callback_mag_y (GtkWidget * , gpointer );
382 static void callback_mag_zoom (GtkWidget * , gpointer );
383 static void callback_mag_flags (GtkWidget * , gpointer );
384 static void callback_graph_type (GtkWidget * , gpointer );
385 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
386 static void callback_create_help (GtkWidget * , gpointer );
387 static void update_zoom_spins (struct graph * );
388 static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
389 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
390 static int get_num_dsegs (struct graph * );
391 static int get_num_acks (struct graph * );
392 static void graph_type_dependent_initialize (struct graph * );
393 static struct graph *graph_new (void);
394 static void graph_destroy (struct graph * );
395 static void graph_initialize_values (struct graph * );
396 static void graph_init_sequence (struct graph * );
397 static void draw_element_line (struct graph * , struct element * );
398 static void draw_element_arc (struct graph * , struct element * );
399 static void graph_display (struct graph * );
400 static void graph_pixmaps_create (struct graph * );
401 static void graph_pixmaps_switch (struct graph * );
402 static void graph_pixmap_draw (struct graph * );
403 static void graph_pixmap_display (struct graph * );
404 static void graph_element_lists_make (struct graph * );
405 static void graph_element_lists_free (struct graph * );
406 static void graph_element_lists_initialize (struct graph * );
407 static void graph_title_pixmap_create (struct graph * );
408 static void graph_title_pixmap_draw (struct graph * );
409 static void graph_title_pixmap_display (struct graph * );
410 static void graph_segment_list_get (struct graph * );
411 static void graph_segment_list_free (struct graph * );
412 static void graph_select_segment (struct graph * , int , int );
413 static int line_detect_collision (struct element * , int , int );
414 static int arc_detect_collision (struct element * , int , int );
415 static void axis_pixmaps_create (struct axis * );
416 static void axis_pixmaps_switch (struct axis * );
417 static void axis_display (struct axis * );
418 static void v_axis_pixmap_draw (struct axis * );
419 static void h_axis_pixmap_draw (struct axis * );
420 static void axis_pixmap_display (struct axis * );
421 static void axis_compute_ticks (struct axis * , double , double , int );
422 static double axis_zoom_get (struct axis * , int );
423 static void axis_ticks_up (int * , int * );
424 static void axis_ticks_down (int * , int * );
425 static void axis_destroy (struct axis * );
426 static int get_label_dim (struct axis * , int , double );
427 static void toggle_time_origin (struct graph * );
428 static void toggle_seq_origin (struct graph * );
429 static void cross_xor (struct graph * , int , int );
430 static void cross_draw (struct graph * , int , int );
431 static void cross_erase (struct graph * );
432 static void magnify_create (struct graph * , int , int );
433 static void magnify_move (struct graph * , int , int );
434 static void magnify_destroy (struct graph * );
435 static void magnify_draw (struct graph * );
436 static void magnify_get_geom (struct graph * , int , int );
437 static gint configure_event (GtkWidget * , GdkEventConfigure * );
438 static gint expose_event (GtkWidget * , GdkEventExpose * );
439 static gint button_press_event (GtkWidget * , GdkEventButton * );
440 static gint button_release_event (GtkWidget * , GdkEventButton * );
441 static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
442 static gint key_press_event (GtkWidget * , GdkEventKey * );
443 static gint key_release_event (GtkWidget * , GdkEventKey * );
444 static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
445 static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
446 static void tseq_initialize (struct graph * );
447 static void tseq_get_bounds (struct graph * );
448 static void tseq_stevens_read_config (struct graph * );
449 static void tseq_stevens_make_elmtlist (struct graph * );
450 static void tseq_stevens_toggle_seq_origin (struct graph * );
451 static void tseq_stevens_toggle_time_origin (struct graph * );
452 static void tseq_tcptrace_read_config (struct graph * );
453 static void tseq_tcptrace_make_elmtlist (struct graph * );
454 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
455 static void tseq_tcptrace_toggle_time_origin (struct graph * );
456 static void tput_initialize (struct graph * );
457 static void tput_read_config (struct graph * );
458 static void tput_make_elmtlist (struct graph * );
459 static void tput_toggle_time_origin (struct graph * );
460 static void rtt_read_config (struct graph * );
461 static void rtt_initialize (struct graph * );
462 static int rtt_is_retrans (struct unack * , unsigned int );
463 static struct unack *rtt_get_new_unack (double , unsigned int );
464 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
465 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
466 static void rtt_make_elmtlist (struct graph * );
467 static void rtt_toggle_seq_origin (struct graph * );
468 #if defined(_WIN32) && !defined(__MINGW32__)
469 static int rint (double ); /* compiler template for Windows */
472 /* XXX - what about OS X? */
473 static char helptext[] =
475 "Here's what you can do:\n\
476 - Left Mouse Button selects segment in Wireshark's packet list\n\
477 - Middle Mouse Button zooms in\n\
478 - <shift>-Middle Button zooms out\n\
479 - Right Mouse Button moves the graph (if zoomed in)\n\
480 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
481 - Space toggles crosshairs\n\
482 - 's' toggles relative/absolute sequence numbers\n\
483 - 't' toggles time origin\n\
486 "Here's what you can do:\n\
487 - <ctrl>-Left Mouse Button selects segment in Wireshark's packet list\n\
488 - Left Mouse Button zooms in\n\
489 - <shift>-Left Mouse Button zooms out\n\
490 - Right Mouse Button moves the graph (if zoomed in)\n\
491 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
493 - Space bar toggles crosshairs\n\
494 - 's' - Toggles relative/absolute sequence numbers\n\
495 - 't' - Toggles time origin\n\
499 static void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
501 struct segment current;
503 struct tcpheader *thdr;
505 guint graph_type = GPOINTER_TO_INT(data);
507 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
509 if (! (g = graph_new()))
513 graph_initialize_values (g);
515 g->type = graph_type;
516 if (!(thdr=select_tcpip_session (&cfile, ¤t))) {
520 graph_segment_list_get(g);
522 /* display_text(g); */
523 graph_init_sequence(g);
526 static void create_gui (struct graph *g)
528 debug(DBS_FENTRY) puts ("create_gui()");
529 /* create_text_widget(g); */
530 control_panel_create (g);
531 create_drawing_area(g);
535 static void create_text_widget (struct graph *g)
537 GtkWidget *streamwindow, *txt_scrollw, *box;
539 debug(DBS_FENTRY) puts ("create_text_widget()");
540 streamwindow = dlg_window_new ("Wireshark: Packet chain");
541 gtk_widget_set_name (streamwindow, "Packet chain");
542 gtk_widget_set_size_request(streamwindow, TXT_WIDTH, TXT_HEIGHT);
543 gtk_container_set_border_width (GTK_CONTAINER(streamwindow), 2);
545 box = gtk_vbox_new (FALSE, 0);
546 gtk_container_add (GTK_CONTAINER (streamwindow), box);
547 gtk_widget_show (box);
549 txt_scrollw = scrolled_window_new (NULL, NULL);
550 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
552 gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
553 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
554 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
555 gtk_widget_show (txt_scrollw);
557 g->text = gtk_text_view_new();
558 gtk_text_view_set_editable(GTK_TEXT_VIEW(g->text), FALSE);
559 gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
560 gtk_widget_show (g->text);
561 gtk_widget_show (streamwindow);
563 static void display_text (struct graph *g)
567 double first_time, prev_time;
568 unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
573 debug(DBS_FENTRY) puts ("display_text()");
574 if (!gdk_color_parse ("SlateGray", &color)) {
576 * XXX - do more than just warn.
578 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
579 "Could not parse color SlateGray.");
581 g_snprintf (line, sizeof(line), "%10s%15s%15s%15s%15s%15s%15s%10s\n",
582 "pkt num", "time", "delta first", "delta prev",
583 "seqno", "delta first", "delta prev", "data (B)");
584 gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL, line, -1);
586 first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
587 prev_time = first_time;
588 /* we have to find Initial Sequence Number for both ends of connection */
589 for (ptr=g->segments; ptr; ptr=ptr->next) {
590 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
591 isn_this = ptr->th_seq;
595 for (ptr=g->segments; ptr; ptr=ptr->next) {
596 if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
597 isn_opposite = ptr->th_seq;
601 seq_this_prev = isn_this;
602 seq_opposite_prev = isn_opposite;
603 for (ptr=g->segments; ptr; ptr=ptr->next) {
604 double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
605 unsigned int seq = ptr->th_seq;
606 int seq_delta_isn, seq_delta_prev;
608 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
609 seq_delta_isn = seq - isn_this;
610 seq_delta_prev = seq - seq_this_prev;
614 seq_delta_isn = seq - isn_opposite;
615 seq_delta_prev = seq - seq_opposite_prev;
616 seq_opposite_prev = seq;
619 g_snprintf (line, sizeof(line), "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
620 ptr->num, time, time-first_time, time-prev_time,
621 seq, seq_delta_isn, seq_delta_prev,
623 gtk_text_buffer_insert(buf, &iter, line, -1);
629 static void create_drawing_area (struct graph *g)
631 GdkColormap *colormap;
633 #define WINDOW_TITLE_LENGTH 64
634 char window_title[WINDOW_TITLE_LENGTH];
635 struct segment current;
636 struct tcpheader *thdr;
638 debug(DBS_FENTRY) puts ("create_drawing_area()");
640 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
642 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
643 "-*-*-m-*-iso8859-2");
645 thdr=select_tcpip_session (&cfile, ¤t);
646 g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
648 cf_get_display_name(&cfile),
649 address_to_str(&(thdr->ip_src)),
651 address_to_str(&(thdr->ip_dst)),
654 g->toplevel = dlg_window_new ("Tcp Graph");
655 gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
656 gtk_widget_set_name (g->toplevel, "Test Graph");
657 g_object_set_data(G_OBJECT(g->toplevel), "graph", g);
659 /* Create the drawing area */
660 g->drawing_area = gtk_drawing_area_new ();
661 g_object_set_data(G_OBJECT(g->drawing_area), "graph", g);
662 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
663 gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
664 g->wp.width + g->wp.x + RMARGIN_WIDTH,
665 g->wp.height + g->wp.y + g->x_axis->s.height);
666 gtk_widget_show (g->drawing_area);
668 g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), NULL);
669 /* this has to be done later, after the widget has been shown */
671 g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
674 g_signal_connect(g->drawing_area, "motion_notify_event",
675 G_CALLBACK(motion_notify_event), NULL);
676 g_signal_connect(g->drawing_area, "button_press_event",
677 G_CALLBACK(button_press_event), NULL);
678 g_signal_connect(g->drawing_area, "button_release_event",
679 G_CALLBACK(button_release_event), NULL);
680 g_signal_connect(g->drawing_area, "leave_notify_event",
681 G_CALLBACK(leave_notify_event), NULL);
682 g_signal_connect(g->drawing_area, "enter_notify_event",
683 G_CALLBACK(enter_notify_event), NULL);
684 g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
685 /* why doesn't drawing area send key_press_signals? */
686 g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), NULL);
687 g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
689 gtk_widget_set_events(g->toplevel,
690 GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
692 gtk_widget_set_events (g->drawing_area,
694 | GDK_LEAVE_NOTIFY_MASK
695 | GDK_ENTER_NOTIFY_MASK
696 | GDK_BUTTON_PRESS_MASK
697 | GDK_BUTTON_RELEASE_MASK
698 | GDK_POINTER_MOTION_MASK
699 | GDK_POINTER_MOTION_HINT_MASK);
702 frame = gtk_frame_new (NULL);
703 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
704 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
706 box = gtk_hbox_new (FALSE, 0);
707 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
708 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
709 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
710 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
711 gtk_widget_show (frame);
712 gtk_widget_show (box);
715 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
716 gtk_widget_show (g->toplevel);
718 /* in case we didn't get what we asked for */
719 g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
720 g->wp.x - RMARGIN_WIDTH;
721 g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
722 g->wp.y - g->x_axis->s.height;
724 g->font = g->drawing_area->style->font_desc;
726 colormap = gdk_window_get_colormap (g->drawing_area->window);
728 xor_gc = gdk_gc_new (g->drawing_area->window);
729 gdk_gc_set_function (xor_gc, GDK_XOR);
730 if (!gdk_color_parse ("gray15", &color)) {
732 * XXX - do more than just warn.
734 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
735 "Could not parse color gray15.");
737 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
739 * XXX - do more than just warn.
741 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
742 "Could not allocate color gray15.");
744 gdk_gc_set_foreground (xor_gc, &color);
746 g->fg_gc = gdk_gc_new (g->drawing_area->window);
747 g->bg_gc = gdk_gc_new (g->drawing_area->window);
748 if (!gdk_color_parse ("white", &color)) {
750 * XXX - do more than just warn.
752 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
753 "Could not parse color white.");
755 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
757 * XXX - do more than just warn.
759 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
760 "Could not allocate color white.");
762 gdk_gc_set_foreground (g->bg_gc, &color);
764 /* this is probably quite an ugly way to get rid of the first configure
766 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
767 * event which is handled during the next return to gtk_main which is
768 * probably the gdk_gc_new() call. configure handler calls
769 * graph_element_lists_make() which is not good because the graph struct is
770 * not fully set up yet - namely we're not sure about actual geometry
771 * and we don't have the GC's at all. so we just postpone installation
772 * of configure handler until we're ready to deal with it.
774 * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
777 g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
780 /* puts ("exiting create_drawing_area()"); */
783 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
785 struct graph *g = (struct graph * )data;
787 if (!(g->flags & GRAPH_DESTROYED)) {
788 g->flags |= GRAPH_DESTROYED;
789 graph_destroy ((struct graph * )data);
793 static void control_panel_create (struct graph *g)
795 GtkWidget *toplevel, *notebook;
797 GtkWidget *help_bt, *close_bt, *bbox;
798 #define WINDOW_TITLE_LENGTH 64
799 char window_title[WINDOW_TITLE_LENGTH];
801 debug(DBS_FENTRY) puts ("control_panel_create()");
803 notebook = gtk_notebook_new ();
804 control_panel_add_zoom_page (g, notebook);
805 control_panel_add_magnify_page (g, notebook);
806 control_panel_add_origin_page (g, notebook);
807 control_panel_add_cross_page (g, notebook);
808 control_panel_add_graph_type_page (g, notebook);
810 g_snprintf (window_title, WINDOW_TITLE_LENGTH,
811 "Graph %d - Control - Wireshark", refnum);
812 toplevel = dlg_window_new ("tcp-graph-control");
813 gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
815 table = gtk_table_new (2, 1, FALSE);
816 gtk_container_add (GTK_CONTAINER (toplevel), table);
818 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
819 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
822 bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
823 gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
824 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
826 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
827 g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
829 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
830 window_set_cancel_button(toplevel, close_bt, NULL);
831 g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
833 g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
834 g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
836 /* gtk_widget_show_all (table); */
837 /* g->gui.control_panel = table; */
838 gtk_widget_show_all (toplevel);
839 window_present(toplevel);
841 g->gui.control_panel = toplevel;
844 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
846 GtkWidget *zoom_frame;
847 GtkWidget *zoom_lock_frame;
851 zoom_frame = control_panel_create_zoom_group (g);
852 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
853 zoom_lock_frame = control_panel_create_zoomlock_group (g);
854 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
855 box = gtk_vbox_new (FALSE, 0);
856 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
857 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
858 gtk_widget_show (box);
859 label = gtk_label_new ("Zoom");
860 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
863 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
865 GtkWidget *mag_frame, *label;
867 mag_frame = control_panel_create_magnify_group (g);
868 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
869 label = gtk_label_new ("Magnify");
870 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
873 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
875 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
876 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
877 GtkWidget *box, *label;
879 /* time origin box */
881 gtk_radio_button_new_with_label (NULL, "beginning of capture");
882 time_orig_conn = gtk_radio_button_new_with_label (
883 gtk_radio_button_get_group (GTK_RADIO_BUTTON (time_orig_cap)),
884 "beginning of this TCP connection");
885 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
886 time_orig_box = gtk_vbox_new (TRUE, 0);
887 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
888 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
889 time_orig_frame = gtk_frame_new ("Time origin");
890 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
891 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
893 /* sequence number origin group */
895 gtk_radio_button_new_with_label (NULL, "initial sequence number");
896 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_get_group (
897 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
898 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
899 seq_orig_box = gtk_vbox_new (TRUE, 0);
900 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
901 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
902 seq_orig_frame = gtk_frame_new ("Sequence number origin");
903 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
904 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
906 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
907 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
909 g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
910 g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
912 box = gtk_vbox_new (FALSE, 0);
913 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
914 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
915 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
916 gtk_widget_show (box);
917 label = gtk_label_new ("Origin");
918 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
921 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
923 GtkWidget *cross_frame, *label;
925 cross_frame = control_panel_create_cross_group (g);
926 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
927 label = gtk_label_new ("Cross");
928 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
931 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
933 GtkWidget *frame, *label;
935 frame = control_panel_create_graph_type_group (g);
936 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
937 label = gtk_label_new ("Graph type");
938 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
941 /* Treat this as a cancel, by calling "callback_close()" */
943 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
946 callback_close(NULL, data);
950 static void callback_close (GtkWidget *widget _U_, gpointer data)
952 struct graph *g = (struct graph * )data;
954 if (!(g->flags & GRAPH_DESTROYED)) {
955 g->flags |= GRAPH_DESTROYED;
956 graph_destroy ((struct graph * )data);
960 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
962 GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
965 toplevel = dlg_window_new ("Help for TCP graphing");
966 gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
968 vbox = gtk_vbox_new (FALSE, 3);
969 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
970 gtk_container_add (GTK_CONTAINER (toplevel), vbox);
972 scroll = scrolled_window_new (NULL, NULL);
973 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
975 gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
976 text = gtk_text_view_new();
977 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
978 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
979 gtk_text_buffer_set_text(buf, helptext, -1);
980 gtk_container_add (GTK_CONTAINER (scroll), text);
983 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
984 gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
985 gtk_widget_show(bbox);
987 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
988 window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
990 g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
992 gtk_widget_show_all (toplevel);
993 window_present(toplevel);
996 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
998 toggle_time_origin ((struct graph * )data);
1001 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1003 toggle_seq_origin ((struct graph * )data);
1006 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1008 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1009 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1010 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1011 GtkWidget *zoom_v_step_label, *zoom_v_step;
1012 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1013 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1014 GtkWidget *zoom_h_entry, *zoom_v_entry;
1015 GtkWidget *zoom_h_label, *zoom_v_label;
1017 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1018 zoom_out = gtk_radio_button_new_with_label (
1019 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1020 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1021 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1022 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1023 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1025 zoom_separator1 = gtk_hseparator_new ();
1027 zoom_h_entry = gtk_entry_new ();
1028 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1029 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1030 zoom_h_label = gtk_label_new ("Horizontal:");
1032 zoom_v_entry = gtk_entry_new ();
1033 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1034 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1035 zoom_v_label = gtk_label_new ("Vertical:");
1037 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1038 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1040 zoom_table = gtk_table_new (2, 2, FALSE);
1041 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1042 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1043 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1044 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1045 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1046 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1047 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1048 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1050 zoom_separator2 = gtk_hseparator_new ();
1052 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1053 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1054 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1055 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1057 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1058 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1059 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1060 zoom_v_step_label = gtk_label_new ("Vertical step:");
1062 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1063 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1065 zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1066 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1067 g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
1068 g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
1069 (gpointer)ZOOM_STEPS_KEEP_RATIO);
1070 g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1071 g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1073 zoom_step_table = gtk_table_new (4, 2, FALSE);
1074 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1075 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1076 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1077 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1078 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1079 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1080 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1081 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1082 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1083 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1084 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1085 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1087 zoom_box = gtk_vbox_new (FALSE, 0);
1088 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1089 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1090 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1091 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1092 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1093 zoom_frame = gtk_frame_new ("Zoom");
1094 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1096 g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
1097 g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
1099 g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
1100 g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
1101 g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
1103 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1104 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1108 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1110 struct graph *g = (struct graph * )data;
1112 if (GTK_TOGGLE_BUTTON (toggle)->active)
1113 g->zoom.flags &= ~ZOOM_OUT;
1115 g->zoom.flags |= ZOOM_OUT;
1118 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1120 struct graph *g = (struct graph * )data;
1123 double *zoom_this, *zoom_other;
1124 GtkSpinButton *widget_this, *widget_other;
1127 direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1128 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1131 zoom_this = &g->zoom.step_y;
1132 zoom_other = &g->zoom.step_x;
1133 widget_this = g->zoom.widget.v_step;
1134 widget_other = g->zoom.widget.h_step;
1136 zoom_this = &g->zoom.step_x;
1137 zoom_other = &g->zoom.step_y;
1138 widget_this = g->zoom.widget.h_step;
1139 widget_other = g->zoom.widget.v_step;
1142 old_this = *zoom_this;
1144 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1145 *zoom_other = value;
1146 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1147 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1148 double old_other = *zoom_other;
1149 *zoom_other *= value / old_this;
1150 if (*zoom_other < 1.0) {
1152 *zoom_this = old_this * 1.0 / old_other;
1153 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1154 } else if (*zoom_other > 5.0) {
1156 *zoom_this = old_this * 5.0 / old_other;
1157 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1159 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1163 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1165 struct graph *g = (struct graph * )data;
1166 int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1168 if (GTK_TOGGLE_BUTTON (toggle)->active)
1169 g->zoom.flags |= flag;
1171 g->zoom.flags &= ~flag;
1174 static void update_zoom_spins (struct graph *g)
1178 g_snprintf (s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
1179 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1180 g_snprintf (s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
1181 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1184 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1186 GtkWidget *mag_width_label, *mag_width;
1187 GtkWidget *mag_height_label, *mag_height;
1188 GtkWidget *mag_x_label, *mag_x;
1189 GtkWidget *mag_y_label, *mag_y;
1190 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1191 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1192 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1193 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1194 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1195 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1196 GtkWidget *mag_box, *mag_frame;
1198 mag_width_label = gtk_label_new ("Width:");
1199 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1200 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1202 mag_height_label = gtk_label_new ("Height:");
1203 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1204 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1206 mag_x_label = gtk_label_new ("X:");
1207 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1208 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1210 mag_y_label = gtk_label_new ("Y:");
1211 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1212 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1214 mag_wh_table = gtk_table_new (4, 2, FALSE);
1215 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1216 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1217 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1218 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1219 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1220 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1221 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1222 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1223 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1224 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1225 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1226 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1227 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1228 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1229 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1230 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1232 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1233 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1234 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1236 mag_v_zoom_label = gtk_label_new ("Vertical:");
1237 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1238 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1240 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1241 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1243 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1244 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1245 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1246 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1247 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1248 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1249 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1250 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1251 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1252 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1253 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1254 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1255 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1257 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1258 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1259 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1261 mag_box = gtk_vbox_new (FALSE, 0);
1262 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1263 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1264 mag_frame = gtk_frame_new ("Magnify");
1265 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1267 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1268 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1269 g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
1270 g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
1271 g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
1272 g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1274 g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
1275 g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
1276 g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
1277 g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
1278 g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1279 g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1280 g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
1281 g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
1286 static void callback_mag_width (GtkWidget *spin, gpointer data)
1288 struct graph *g = (struct graph * )data;
1290 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1293 static void callback_mag_height (GtkWidget *spin, gpointer data)
1295 struct graph *g = (struct graph * )data;
1297 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1300 static void callback_mag_x (GtkWidget *spin, gpointer data)
1302 struct graph *g = (struct graph * )data;
1304 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1307 static void callback_mag_y (GtkWidget *spin, gpointer data)
1309 struct graph *g = (struct graph * )data;
1311 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1314 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1316 struct graph *g = (struct graph * )data;
1319 double *zoom_this, *zoom_other;
1320 GtkSpinButton *widget_this, *widget_other;
1323 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1324 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1325 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1328 direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1329 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1332 zoom_this = &g->magnify.zoom.y;
1333 zoom_other = &g->magnify.zoom.x;
1334 widget_this = g->magnify.widget.v_zoom;
1335 widget_other = g->magnify.widget.h_zoom;
1337 zoom_this = &g->magnify.zoom.x;
1338 zoom_other = &g->magnify.zoom.y;
1339 widget_this = g->magnify.widget.h_zoom;
1340 widget_other = g->magnify.widget.v_zoom;
1343 old_this = *zoom_this;
1345 if (g->magnify.flags & MAGZOOMS_SAME) {
1346 *zoom_other = value;
1347 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1348 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1349 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1350 double old_other = *zoom_other;
1351 *zoom_other *= value / old_this;
1352 if (*zoom_other < 1.0) {
1354 *zoom_this = old_this * 1.0 / old_other;
1355 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1356 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1357 } else if (*zoom_other > 25.0) {
1359 *zoom_this = old_this * 25.0 / old_other;
1360 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1361 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1363 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1364 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1368 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1370 struct graph *g = (struct graph * )data;
1371 int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1373 if (GTK_TOGGLE_BUTTON (toggle)->active)
1374 g->magnify.flags |= flag;
1376 g->magnify.flags &= ~flag;
1379 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1381 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1382 GtkWidget *zoom_lock_frame;
1384 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1385 zoom_lock_h = gtk_radio_button_new_with_label (
1386 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1388 zoom_lock_v = gtk_radio_button_new_with_label (
1389 gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1391 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1392 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1393 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1395 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1396 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1397 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1398 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1400 g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
1401 g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
1403 return zoom_lock_frame;
1406 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1408 struct graph *g = (struct graph * )data;
1410 if (GTK_TOGGLE_BUTTON (toggle)->active)
1411 g->zoom.flags |= ZOOM_HLOCK;
1413 g->zoom.flags &= ~ZOOM_HLOCK;
1416 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1418 struct graph *g = (struct graph * )data;
1420 if (GTK_TOGGLE_BUTTON (toggle)->active)
1421 g->zoom.flags |= ZOOM_VLOCK;
1423 g->zoom.flags &= ~ZOOM_VLOCK;
1426 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1428 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1430 label = gtk_label_new ("Crosshairs:");
1431 off = gtk_radio_button_new_with_label (NULL, "off");
1432 on = gtk_radio_button_new_with_label (
1433 gtk_radio_button_get_group (GTK_RADIO_BUTTON (off)), "on");
1434 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1435 box = gtk_hbox_new (FALSE, 0);
1436 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1437 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1438 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1439 vbox = gtk_vbox_new (FALSE, 0);
1440 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1441 /* frame = gtk_frame_new ("Cross:"); */
1442 frame = gtk_frame_new (NULL);
1443 gtk_container_add (GTK_CONTAINER (frame), vbox);
1445 g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
1447 g->cross.on_toggle = (GtkToggleButton * )on;
1448 g->cross.off_toggle = (GtkToggleButton * )off;
1453 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1455 struct graph *g = (struct graph * )data;
1457 if (GTK_TOGGLE_BUTTON (toggle)->active) {
1459 g->cross.draw = TRUE;
1460 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
1461 cross_draw (g, x, y);
1463 g->cross.draw = FALSE;
1468 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1470 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1471 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1472 GtkWidget *graph_frame;
1474 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1475 graph_tseqttrace = gtk_radio_button_new_with_label (
1476 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1477 "Time/Sequence (tcptrace-style)");
1478 graph_tseqstevens = gtk_radio_button_new_with_label (
1479 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1480 "Time/Sequence (Stevens'-style)");
1481 graph_rtt = gtk_radio_button_new_with_label (
1482 gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1485 case GRAPH_TSEQ_STEVENS:
1486 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1488 case GRAPH_TSEQ_TCPTRACE:
1489 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1491 case GRAPH_THROUGHPUT:
1492 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1495 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1498 graph_init = gtk_check_button_new_with_label ("Init on change");
1499 graph_sep = gtk_hseparator_new ();
1500 graph_box = gtk_vbox_new (FALSE, 0);
1501 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1502 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1503 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1504 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1505 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1506 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1507 graph_frame = gtk_frame_new ("Graph type:");
1508 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1510 g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
1511 GINT_TO_POINTER(0));
1512 g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type", GINT_TO_POINTER(1));
1513 g_object_set_data(G_OBJECT(graph_tput), "new-graph-type", GINT_TO_POINTER(2));
1514 g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type", GINT_TO_POINTER(3));
1516 g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
1517 g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
1518 g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
1519 g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
1520 g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg),
1526 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1528 int old_type, new_type;
1529 struct graph *g = (struct graph * )data;
1531 new_type = (long)g_object_get_data(G_OBJECT(toggle),"new-graph-type");
1533 if (!GTK_TOGGLE_BUTTON (toggle)->active)
1539 graph_element_lists_free (g);
1540 graph_element_lists_initialize (g);
1542 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1543 /* throughput graph uses differently constructed segment list so we
1544 * need to recreate it */
1545 graph_segment_list_free (g);
1546 graph_segment_list_get (g);
1549 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1550 g->geom.width = g->wp.width;
1551 g->geom.height = g->wp.height;
1552 g->geom.x = g->wp.x;
1553 g->geom.y = g->wp.y;
1555 g->x_axis->min = g->y_axis->min = 0;
1556 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1557 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1558 graph_init_sequence (g);
1561 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1563 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1566 static struct graph *graph_new (void)
1570 g = (struct graph * )g_malloc0 (sizeof (struct graph));
1571 graph_element_lists_initialize (g);
1573 g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1574 g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1576 g->x_axis->flags = 0;
1577 g->x_axis->flags |= AXIS_ORIENTATION;
1578 g->x_axis->s.x = g->x_axis->s.y = 0;
1579 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1580 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1581 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1583 g->y_axis->flags = 0;
1584 g->y_axis->flags &= ~AXIS_ORIENTATION;
1585 g->y_axis->p.x = g->y_axis->p.y = 0;
1586 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1588 g->y_axis->s.y = TITLEBAR_HEIGHT;
1589 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1594 static void graph_initialize_values (struct graph *g)
1596 g->geom.width = g->wp.width = 750;
1597 g->geom.height = g->wp.height = 550;
1598 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1599 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1601 /* g->zoom.x = g->zoom.y = 1.0; */
1602 g->zoom.step_x = g->zoom.step_y = 1.2;
1604 g->cross.draw = g->cross.erase_needed = 0;
1605 g->grab.grabbed = 0;
1606 g->magnify.active = 0;
1607 g->magnify.offset.x = g->magnify.offset.y = 0;
1608 g->magnify.width = g->magnify.height = 250;
1609 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1610 g->magnify.flags = 0;
1613 static void graph_init_sequence (struct graph *g)
1615 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1617 graph_type_dependent_initialize (g);
1618 g->zoom.initial.x = g->zoom.x;
1619 g->zoom.initial.y = g->zoom.y;
1620 graph_element_lists_make (g);
1621 g->x_axis->s.width = g->wp.width;
1622 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1623 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1624 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1625 g->y_axis->s.height = g->wp.height;
1626 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1627 graph_pixmaps_create (g);
1628 axis_pixmaps_create (g->y_axis);
1629 axis_pixmaps_create (g->x_axis);
1630 graph_title_pixmap_create (g);
1631 graph_title_pixmap_draw (g);
1632 graph_title_pixmap_display (g);
1634 axis_display (g->y_axis);
1635 axis_display (g->x_axis);
1638 static void graph_type_dependent_initialize (struct graph *g)
1641 case GRAPH_TSEQ_STEVENS:
1642 case GRAPH_TSEQ_TCPTRACE:
1643 tseq_initialize (g);
1645 case GRAPH_THROUGHPUT:
1646 tput_initialize (g);
1656 static void graph_destroy (struct graph *g)
1658 debug(DBS_FENTRY) puts ("graph_destroy()");
1660 axis_destroy (g->x_axis);
1661 axis_destroy (g->y_axis);
1662 /* window_destroy (g->drawing_area); */
1663 window_destroy (g->gui.control_panel);
1664 window_destroy (g->toplevel);
1665 /* window_destroy (g->text); */
1666 gdk_gc_unref (g->fg_gc);
1667 gdk_gc_unref (g->bg_gc);
1668 gdk_pixmap_unref (g->pixmap[0]);
1669 gdk_pixmap_unref (g->pixmap[1]);
1672 g_free ( (gpointer) (g->title) );
1673 graph_segment_list_free (g);
1674 graph_element_lists_free (g);
1680 typedef struct _tcp_scan_t {
1681 struct segment *current;
1684 struct segment *last;
1688 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
1690 static struct segment *segment=NULL;
1691 tcp_scan_t *ts=(tcp_scan_t *)pct;
1692 struct tcpheader *tcphdr=(struct tcpheader *)vip;
1695 segment=g_malloc(sizeof (struct segment));
1699 if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
1700 ts->current->th_sport, ts->current->th_dport,
1701 &tcphdr->ip_src, &tcphdr->ip_dst,
1702 tcphdr->th_sport, tcphdr->th_dport,
1704 segment->next = NULL;
1705 segment->num = pinfo->fd->num;
1706 segment->rel_secs = (guint32) pinfo->fd->rel_ts.secs;
1707 segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
1708 segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
1709 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
1710 segment->th_seq=tcphdr->th_seq;
1711 segment->th_ack=tcphdr->th_ack;
1712 segment->th_win=tcphdr->th_win;
1713 segment->th_flags=tcphdr->th_flags;
1714 segment->th_sport=tcphdr->th_sport;
1715 segment->th_dport=tcphdr->th_dport;
1716 segment->th_seglen=tcphdr->th_seglen;
1717 COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
1718 COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
1719 if (ts->g->segments) {
1720 ts->last->next = segment;
1722 ts->g->segments = segment;
1725 if(pinfo->fd->num==ts->current->num){
1726 ts->g->current = segment;
1737 /* here we collect all the external data we will ever need */
1738 static void graph_segment_list_get (struct graph *g)
1740 struct segment current;
1741 GString *error_string;
1745 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1746 select_tcpip_session (&cfile, ¤t);
1747 if (g->type == GRAPH_THROUGHPUT)
1748 ts.direction = COMPARE_CURR_DIR;
1750 ts.direction = COMPARE_ANY_DIR;
1752 /* rescan all the packets and pick up all interesting tcp headers.
1753 * we only filter for TCP here for speed and do the actual compare
1754 * in the tap listener
1756 ts.current=¤t;
1759 error_string=register_tap_listener("tcp", &ts, "tcp", NULL, tapall_tcpip_packet, NULL);
1761 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1763 g_string_free(error_string, TRUE);
1766 cf_retap_packets(&cfile, FALSE);
1767 remove_tap_listener(&ts);
1771 typedef struct _th_t {
1773 struct tcpheader *tcphdr;
1777 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
1782 th->tcphdr=(struct tcpheader *)vip;
1789 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
1790 * then present the user with a dialog where the user can select WHICH tcp
1793 static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
1798 epan_dissect_t *edt;
1800 GString *error_string;
1801 th_t th = {0, NULL};
1803 fdata = cf->current_frame;
1805 /* no real filter yet */
1806 if (!dfilter_compile("tcp", &sfcode)) {
1807 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
1811 /* dissect the current frame */
1812 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
1813 cf->pd, fdata->cap_len, &err, &err_info)) {
1814 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1815 cf_read_error_message(err, err_info), cf->filename);
1820 error_string=register_tap_listener("tcp", &th, NULL, NULL, tap_tcpip_packet, NULL);
1822 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1824 g_string_free(error_string, TRUE);
1828 edt = epan_dissect_new(TRUE, FALSE);
1829 epan_dissect_prime_dfilter(edt, sfcode);
1830 tap_queue_init(edt);
1831 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
1832 tap_push_tapped_queue(edt);
1833 epan_dissect_free(edt);
1834 remove_tap_listener(&th);
1837 /* This "shouldn't happen", as our menu items shouldn't
1838 * even be enabled if the selected packet isn't a TCP
1839 * segment, as tcp_graph_selected_packet_enabled() is used
1840 * to determine whether to enable any of our menu items. */
1841 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1842 "Selected packet isn't a TCP segment");
1845 /* XXX fix this later, we should show a dialog allowing the user
1846 to select which session he wants here
1849 /* can only handle a single tcp layer yet */
1850 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1851 "The selected packet has more than one TCP"
1856 hdrs->num = fdata->num;
1857 hdrs->rel_secs = (guint32) fdata->rel_ts.secs;
1858 hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
1859 hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
1860 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
1861 hdrs->th_seq=th.tcphdr->th_seq;
1862 hdrs->th_ack=th.tcphdr->th_ack;
1863 hdrs->th_win=th.tcphdr->th_win;
1864 hdrs->th_flags=th.tcphdr->th_flags;
1865 hdrs->th_sport=th.tcphdr->th_sport;
1866 hdrs->th_dport=th.tcphdr->th_dport;
1867 hdrs->th_seglen=th.tcphdr->th_seglen;
1868 COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
1869 COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
1874 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
1878 dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
1879 (!(CMP_ADDRESS(daddr1, daddr2))) &&
1883 if(dir==COMPARE_CURR_DIR){
1886 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
1887 (!(CMP_ADDRESS(daddr1, saddr2))) &&
1890 return dir1 || dir2;
1894 static void graph_segment_list_free (struct graph *g)
1896 struct segment *segment;
1898 while (g->segments) {
1899 segment = g->segments->next;
1900 g_free (g->segments);
1901 g->segments = segment;
1906 static void graph_element_lists_initialize (struct graph *g)
1908 g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
1911 static void graph_element_lists_make (struct graph *g)
1913 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1916 case GRAPH_TSEQ_STEVENS:
1917 tseq_stevens_make_elmtlist (g);
1919 case GRAPH_TSEQ_TCPTRACE:
1920 tseq_tcptrace_make_elmtlist (g);
1922 case GRAPH_THROUGHPUT:
1923 tput_make_elmtlist (g);
1926 rtt_make_elmtlist (g);
1929 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1934 static void graph_element_lists_free (struct graph *g)
1936 struct element_list *list, *next_list;
1939 for (list=g->elists; list; list=list->next)
1940 g_free (list->elements);
1941 while (g->elists->next) {
1942 list = g->elists->next->next;
1943 g_free (g->elists->next);
1944 g->elists->next = list;
1948 for (list=g->elists; list; list=next_list) {
1949 g_free (list->elements);
1950 next_list = list->next;
1953 g->elists = NULL; /* just to make debugging easier */
1956 static void graph_title_pixmap_create (struct graph *g)
1958 if (g->title_pixmap)
1959 gdk_pixmap_unref (g->title_pixmap);
1961 g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
1962 g->x_axis->p.width, g->wp.y, -1);
1965 static void graph_title_pixmap_draw (struct graph *g)
1969 gdk_draw_rectangle(g->title_pixmap, g->bg_gc, TRUE, 0, 0,
1970 g->x_axis->p.width, g->wp.y);
1971 for (i=0; g->title[i]; i++) {
1973 PangoLayout *layout;
1974 layout = gtk_widget_create_pango_layout(g->drawing_area,
1976 pango_layout_get_pixel_size(layout, &w, &h);
1977 gdk_draw_layout(g->title_pixmap, g->fg_gc,
1978 g->wp.width/2 - w/2, 20 + i*(h+3), layout);
1979 g_object_unref(G_OBJECT(layout));
1983 static void graph_title_pixmap_display (struct graph *g)
1985 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
1986 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
1989 static void graph_pixmaps_create (struct graph *g)
1991 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
1994 gdk_pixmap_unref (g->pixmap[0]);
1996 gdk_pixmap_unref (g->pixmap[1]);
1998 g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
1999 g->wp.width, g->wp.height, -1);
2000 g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
2001 g->wp.width, g->wp.height, -1);
2006 static void graph_display (struct graph *g)
2008 graph_pixmap_draw (g);
2009 graph_pixmaps_switch (g);
2010 graph_pixmap_display (g);
2013 static void graph_pixmap_display (struct graph *g)
2015 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
2016 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
2017 g->wp.width, g->wp.height);
2018 if (g->cross.erase_needed) {
2019 cross_xor(g, g->cross.x, g->cross.y);
2023 static void graph_pixmaps_switch (struct graph *g)
2025 g->displayed = 1 ^ g->displayed;
2028 static void graph_pixmap_draw (struct graph *g)
2030 struct element_list *list;
2034 debug(DBS_FENTRY) puts ("graph_display()");
2035 not_disp = 1 ^ g->displayed;
2037 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
2038 0, 0, g->wp.width, g->wp.height);
2040 for (list=g->elists; list; list=list->next)
2041 for (e=list->elements; e->type != ELMT_NONE; e++) {
2046 draw_element_line (g, e);
2049 draw_element_arc (g, e);
2057 static void draw_element_line (struct graph *g, struct element *e)
2061 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2062 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2063 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2064 x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2065 x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2066 y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2067 y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2078 if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
2079 (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
2080 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2084 if (x2 > g->wp.width-1)
2088 if (y2 > g->wp.height-1)
2089 y2 = g->wp.height-1;
2092 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
2093 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
2096 static void draw_element_arc (struct graph *g, struct element *e)
2100 x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2101 x2 = (int )e->p.arc.dim.width;
2102 y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2103 y2 = (int )e->p.arc.dim.height;
2104 if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
2106 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
2107 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
2108 y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
2111 static void axis_pixmaps_create (struct axis *axis)
2113 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2114 if (axis->pixmap[0])
2115 gdk_pixmap_unref (axis->pixmap[0]);
2116 if (axis->pixmap[1])
2117 gdk_pixmap_unref (axis->pixmap[1]);
2119 axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
2120 axis->p.width, axis->p.height, -1);
2121 axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
2122 axis->p.width, axis->p.height, -1);
2124 axis->displayed = 0;
2127 static void axis_destroy (struct axis *axis)
2129 gdk_pixmap_unref (axis->pixmap[0]);
2130 gdk_pixmap_unref (axis->pixmap[1]);
2131 g_free ( (gpointer) (axis->label) );
2134 static void axis_display (struct axis *axis)
2136 if (axis->flags & AXIS_ORIENTATION)
2137 h_axis_pixmap_draw (axis);
2139 v_axis_pixmap_draw (axis);
2140 axis_pixmaps_switch (axis);
2141 axis_pixmap_display (axis);
2144 static void v_axis_pixmap_draw (struct axis *axis)
2146 struct graph *g = axis->g;
2149 int not_disp, rdigits, offset, imin, imax;
2150 double bottom, top, j, fl, corr;
2151 PangoLayout *layout;
2153 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2154 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2155 (double )g->geom.height * g->bounds.height;
2156 bottom += axis->min;
2157 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2158 (double )g->geom.height * g->bounds.height;
2160 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2162 j = axis->major - floor (axis->major);
2163 for (rdigits=0; rdigits<=6; rdigits++) {
2170 not_disp = 1 ^ axis->displayed;
2171 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2172 axis->p.width, axis->p.height);
2174 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2175 (gint) ((axis->p.height-axis->s.height)/2.0), axis->s.width - 1,
2178 offset = g->wp.y + (-g->geom.y);
2179 fl = floor (axis->min / axis->major) * axis->major;
2180 corr = rint ((axis->min - fl) * g->zoom.y);
2183 major_tick = axis->major * g->zoom.y;
2184 imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2185 imax = (int) ((g->geom.height - offset + corr) / major_tick);
2186 for (i=imin; i <= imax; i++) {
2189 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2190 offset + corr + axis->s.y);
2192 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2193 i*axis->major + fl, y);
2194 if (y < 0 || y > axis->p.height)
2196 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2197 axis->s.width - 15, y, axis->s.width - 1, y);
2198 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2199 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2200 pango_layout_get_pixel_size(layout, &w, &h);
2201 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2202 axis->s.width-14-4-w, y - h/2, layout);
2203 g_object_unref(G_OBJECT(layout));
2207 double minor_tick = axis->minor * g->zoom.y;
2208 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2209 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2210 for (i=imin; i <= imax; i++) {
2211 int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2212 offset + corr + axis->s.y);
2214 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2215 if (y > 0 && y < axis->p.height)
2216 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2217 axis->s.width - 8, y,
2218 axis->s.width - 1, y);
2221 for (i=0; axis->label[i]; i++) {
2223 layout = gtk_widget_create_pango_layout(g->drawing_area,
2225 pango_layout_get_pixel_size(layout, &w, &h);
2226 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2227 (axis->p.width - w)/2,
2228 TITLEBAR_HEIGHT-10 - i*(h+3) - h,
2230 g_object_unref(G_OBJECT(layout));
2234 static void h_axis_pixmap_draw (struct axis *axis)
2236 struct graph *g = axis->g;
2238 double major_tick, minor_tick;
2239 int not_disp, rdigits, offset, imin, imax;
2240 double left, right, j, fl, corr;
2241 PangoLayout *layout;
2243 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2244 left = (g->wp.x-g->geom.x) /
2245 (double )g->geom.width * g->bounds.width;
2247 right = (g->wp.x-g->geom.x+g->wp.width) /
2248 (double )g->geom.width * g->bounds.width;
2250 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2252 j = axis->major - floor (axis->major);
2253 for (rdigits=0; rdigits<=6; rdigits++) {
2260 not_disp = 1 ^ axis->displayed;
2261 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2262 axis->p.width, axis->p.height);
2264 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2265 (gint) (axis->s.width + (axis->p.width-axis->s.width)/2.0), 0);
2266 offset = g->wp.x - g->geom.x;
2268 fl = floor (axis->min / axis->major) * axis->major;
2269 corr = rint ((axis->min - fl) * g->zoom.x);
2272 major_tick = axis->major*g->zoom.x;
2273 imin = (int) ((offset + corr) / major_tick + 1);
2274 imax = (int) ((offset + corr + axis->s.width) / major_tick);
2275 for (i=imin; i <= imax; i++) {
2278 int x = (int ) (rint (i * major_tick) - offset - corr);
2280 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2281 if (x < 0 || x > axis->s.width)
2283 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2284 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2285 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2286 pango_layout_get_pixel_size(layout, &w, &h);
2287 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2288 x - w/2, 15+4, layout);
2289 g_object_unref(G_OBJECT(layout));
2291 if (axis->minor > 0) {
2293 minor_tick = axis->minor*g->zoom.x;
2294 imin = (int) ((offset + corr) / minor_tick + 1);
2295 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2296 for (i=imin; i <= imax; i++) {
2297 int x = (int) (rint (i * minor_tick) - offset - corr);
2298 if (x > 0 && x < axis->s.width)
2299 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2302 for (i=0; axis->label[i]; i++) {
2304 layout = gtk_widget_create_pango_layout(g->drawing_area,
2306 pango_layout_get_pixel_size(layout, &w, &h);
2307 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2308 axis->s.width - w - 50, 15+h+15 + i*(h+3),
2310 g_object_unref(G_OBJECT(layout));
2314 static void axis_pixmaps_switch (struct axis *axis)
2316 axis->displayed = 1 ^ axis->displayed;
2319 static void axis_pixmap_display (struct axis *axis)
2321 gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
2322 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2323 axis->p.width, axis->p.height);
2326 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2328 int i, j, ii, jj, ms;
2329 double zoom, x, steps[3]={ 0.1, 0.5 };
2330 int dim, check_needed, diminished;
2331 double majthresh[2]={2.0, 3.0};
2333 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2334 debug(DBS_AXES_TICKS)
2335 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2337 zoom = axis_zoom_get (axis, dir);
2339 for (i=-9; i<=12; i++) {
2340 if (x / pow (10, i) < 1)
2344 ms = (int )(x / pow (10, i));
2354 axis->major = steps[j] * pow (10, i);
2356 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2357 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2359 /* let's compute minor ticks */
2362 axis_ticks_down (&ii, &jj);
2363 axis->minor = steps[jj] * pow (10, ii);
2364 /* we don't want minors if they would be less than 10 pixels apart */
2365 if (axis->minor*zoom < 10) {
2366 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2367 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2371 check_needed = TRUE;
2373 while (check_needed) {
2374 check_needed = FALSE;
2375 dim = get_label_dim (axis, dir, xmax);
2376 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2377 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2378 axis->major, axis->minor, axis->major*zoom/dim,
2379 axis->minor*zoom/dim);
2381 /* corrections: if majors are less than majthresh[dir] times label
2382 * dimension apart, we need to use bigger ones */
2383 if (axis->major*zoom / dim < majthresh[dir]) {
2384 axis_ticks_up (&ii, &jj);
2385 axis->minor = axis->major;
2386 axis_ticks_up (&i, &j);
2387 axis->major = steps[j] * pow (10, i);
2388 check_needed = TRUE;
2389 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2392 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2393 * we could promote them to majors as well */
2394 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2395 axis_ticks_down (&i, &j);
2396 axis->major = axis->minor;
2397 axis_ticks_down (&ii, &jj);
2398 axis->minor = steps[jj] * pow (10, ii);
2399 check_needed = TRUE;
2402 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2405 if (axis->minor*zoom < 10) {
2406 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2407 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2413 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2414 "axis->minor == %.1f\n", axis->major, axis->minor);
2417 static void axis_ticks_up (int *i, int *j)
2426 static void axis_ticks_down (int *i, int *j)
2435 static int get_label_dim (struct axis *axis, int dir, double label)
2440 PangoLayout *layout;
2442 /* First, let's compute how many digits to the right of radix
2443 * we need to print */
2444 y = axis->major - floor (axis->major);
2445 for (rdigits=0; rdigits<=6; rdigits++) {
2451 g_snprintf (str, sizeof(str), "%.*f", rdigits, label);
2453 case AXIS_HORIZONTAL:
2454 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2456 pango_layout_get_pixel_size(layout, &dim, NULL);
2457 g_object_unref(G_OBJECT(layout));
2460 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2462 pango_layout_get_pixel_size(layout, NULL, &dim);
2463 g_object_unref(G_OBJECT(layout));
2466 puts ("initialize axis: an axis must be either horizontal or vertical");
2472 static double axis_zoom_get (struct axis *axis, int dir)
2475 case AXIS_HORIZONTAL:
2476 return axis->g->zoom.x;
2478 return axis->g->zoom.y;
2484 static void graph_select_segment (struct graph *g, int x, int y)
2486 struct element_list *list;
2489 debug(DBS_FENTRY) puts ("graph_select_segment()");
2492 y = g->geom.height-1 - (y - g->geom.y);
2494 for (list=g->elists; list; list=list->next)
2495 for (e=list->elements; e->type != ELMT_NONE; e++) {
2500 if (line_detect_collision (e, x, y))
2501 cf_goto_frame(&cfile, e->parent->num);
2504 if (arc_detect_collision (e, x, y))
2505 cf_goto_frame(&cfile, e->parent->num);
2513 static int line_detect_collision (struct element *e, int x, int y)
2517 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2518 x1 = (int )rint (e->p.line.dim.x1);
2519 x2 = (int )rint (e->p.line.dim.x2);
2521 x1 = (int )rint (e->p.line.dim.x2);
2522 x2 = (int )rint (e->p.line.dim.x1);
2524 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2525 y1 = (int )rint (e->p.line.dim.y1);
2526 y2 = (int )rint (e->p.line.dim.y2);
2528 y1 = (int )rint (e->p.line.dim.y2);
2529 y2 = (int )rint (e->p.line.dim.y1);
2532 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2534 if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
2540 static int arc_detect_collision (struct element *e, int x, int y)
2544 x1 = (int )rint (e->p.arc.dim.x);
2545 x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2546 y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2547 y2 = (int )rint (e->p.arc.dim.y);
2549 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2551 if (x1<=x && x<=x2 && y1<=y && y<=y2)
2557 static void cross_xor (struct graph *g, int x, int y)
2559 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2560 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2561 gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
2562 y, g->wp.x + g->wp.width, y);
2563 gdk_draw_line (g->drawing_area->window, xor_gc, x,
2564 g->wp.y, x, g->wp.y + g->wp.height);
2568 static void cross_draw (struct graph *g, int x, int y)
2570 cross_xor (g, x, y);
2573 g->cross.erase_needed = 1;
2576 static void cross_erase (struct graph *g)
2578 cross_xor (g, g->cross.x, g->cross.y);
2579 g->cross.erase_needed = 0;
2582 static void magnify_create (struct graph *g, int x, int y)
2585 struct element_list *list, *new_list;
2586 struct ipoint pos, offsetpos;
2589 mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
2590 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2592 mg->toplevel = dlg_window_new("tcp graph magnify");
2593 mg->drawing_area = mg->toplevel;
2594 gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2595 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2596 /* | GDK_ENTER_NOTIFY_MASK */
2597 /* | GDK_ALL_EVENTS_MASK */
2602 mg->wp.width = g->magnify.width;
2603 mg->wp.height = g->magnify.height;
2604 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2605 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2606 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2607 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2609 /* in order to keep original element lists intact we need our own */
2610 graph_element_lists_initialize (mg);
2611 list = g->elists->next;
2612 new_list = mg->elists;
2613 for ( ; list; list=list->next) {
2615 (struct element_list * )g_malloc (sizeof (struct element_list));
2616 new_list = new_list->next;
2617 new_list->next = NULL;
2618 new_list->elements = NULL;
2620 graph_element_lists_make (mg);
2622 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2623 g->magnify.x = pos.x + x - g->magnify.width/2;
2624 g->magnify.y = pos.y + y - g->magnify.height/2;
2625 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2626 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2627 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2628 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2629 gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
2630 magnify_get_geom (g, x, y);
2632 gtk_widget_show (mg->drawing_area);
2634 /* we need to wait for the first expose event before we start drawing */
2635 while (!gdk_events_pending ());
2637 e = gdk_event_get ();
2639 if (e->any.type == GDK_EXPOSE) {
2647 mg->pixmap[0] = mg->pixmap[1] = NULL;
2648 graph_pixmaps_create (mg);
2650 g->magnify.active = 1;
2653 static void magnify_move (struct graph *g, int x, int y)
2655 struct ipoint pos, offsetpos;
2657 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2658 g->magnify.x = pos.x + x - g->magnify.width/2;
2659 g->magnify.y = pos.y + y - g->magnify.height/2;
2660 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2661 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2662 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2663 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2664 magnify_get_geom (g, x, y);
2665 gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
2670 static void magnify_destroy (struct graph *g)
2672 struct element_list *list;
2673 struct graph *mg = g->magnify.g;
2675 window_destroy (GTK_WIDGET (mg->drawing_area));
2676 gdk_pixmap_unref (mg->pixmap[0]);
2677 gdk_pixmap_unref (mg->pixmap[1]);
2678 for (list=mg->elists; list; list=list->next)
2679 g_free (list->elements);
2680 while (mg->elists->next) {
2681 list = mg->elists->next->next;
2682 g_free (mg->elists->next);
2683 mg->elists->next = list;
2685 g_free (g->magnify.g);
2686 g->magnify.active = 0;
2689 static void magnify_get_geom (struct graph *g, int x, int y)
2693 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
2695 g->magnify.g->geom.x = g->geom.x;
2696 g->magnify.g->geom.y = g->geom.y;
2698 g->magnify.g->geom.x -=
2699 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2700 ((x-g->geom.x)/(double )g->geom.width));
2701 g->magnify.g->geom.y -=
2702 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2703 ((y-g->geom.y)/(double )g->geom.height));
2705 /* we have coords of origin of graph relative to origin of g->toplevel.
2706 * now we need them to relate to origin of magnify window */
2707 g->magnify.g->geom.x -= (g->magnify.x - posx);
2708 g->magnify.g->geom.y -= (g->magnify.y - posy);
2711 static void magnify_draw (struct graph *g)
2713 int not_disp = 1 ^ g->magnify.g->displayed;
2715 graph_pixmap_draw (g->magnify.g);
2716 /* graph pixmap is almost ready, just add border */
2717 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2718 g->magnify.width - 1, 0);
2719 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2720 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2721 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2722 0, g->magnify.height - 1);
2723 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2724 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2726 graph_pixmaps_switch (g->magnify.g);
2727 graph_pixmap_display (g->magnify.g);
2731 static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
2733 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2737 int cur_g_width, cur_g_height;
2738 int cur_wp_width, cur_wp_height;
2740 debug(DBS_FENTRY) puts ("configure_event()");
2742 cur_wp_width = g->wp.width;
2743 cur_wp_height = g->wp.height;
2744 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2745 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2746 g->x_axis->s.width = g->wp.width;
2747 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2748 g->y_axis->p.height = g->wp.height + g->wp.y;
2749 g->y_axis->s.height = g->wp.height;
2750 g->x_axis->p.y = g->y_axis->p.height;
2751 zoom.x = (double )g->wp.width / cur_wp_width;
2752 zoom.y = (double )g->wp.height / cur_wp_height;
2753 cur_g_width = g->geom.width;
2754 cur_g_height = g->geom.height;
2755 g->geom.width = (int )rint (g->geom.width * zoom.x);
2756 g->geom.height = (int )rint (g->geom.height * zoom.y);
2757 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2758 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2759 /* g->zoom.initial.x = g->zoom.x; */
2760 /* g->zoom.initial.y = g->zoom.y; */
2762 g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
2763 (g->wp.x - g->geom.x));
2764 g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
2765 (g->wp.y - g->geom.y));
2767 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2768 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2769 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2770 g->zoom.x, g->zoom.y);
2773 update_zoom_spins (g);
2774 graph_element_lists_make (g);
2775 graph_pixmaps_create (g);
2776 graph_title_pixmap_create (g);
2777 axis_pixmaps_create (g->y_axis);
2778 axis_pixmaps_create (g->x_axis);
2779 /* we don't do actual drawing here; we leave it to expose handler */
2780 graph_pixmap_draw (g);
2781 graph_pixmaps_switch (g);
2782 graph_title_pixmap_draw (g);
2783 h_axis_pixmap_draw (g->x_axis);
2784 axis_pixmaps_switch (g->x_axis);
2785 v_axis_pixmap_draw (g->y_axis);
2786 axis_pixmaps_switch (g->y_axis);
2790 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
2792 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2794 debug(DBS_FENTRY) puts ("expose_event()");
2799 /* lower left corner */
2800 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
2801 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2803 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
2804 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2806 graph_pixmap_display (g);
2807 graph_title_pixmap_display (g);
2808 axis_pixmap_display (g->x_axis);
2809 axis_pixmap_display (g->y_axis);
2814 static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
2816 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2818 debug(DBS_FENTRY) puts ("button_press_event()");
2820 if (event->button == 3) {
2821 if (event->state & GDK_CONTROL_MASK)
2822 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
2824 g->grab.x = (int )rint (event->x) - g->geom.x;
2825 g->grab.y = (int )rint (event->y) - g->geom.y;
2826 g->grab.grabbed = TRUE;
2829 /* Windows mouse control: */
2830 /* [<ctrl>-left] - select packet */
2831 /* [left] - zoom in */
2832 /* [<shift>-left] - zoom out */
2833 } else if (event->button == 1) {
2834 if (event->state & GDK_CONTROL_MASK) {
2835 graph_select_segment (g, (int)event->x, (int)event->y);
2838 } else if (event->button == 2) {
2840 int cur_width = g->geom.width, cur_height = g->geom.height;
2841 struct { double x, y; } factor;
2843 if (g->zoom.flags & ZOOM_OUT) {
2844 if (g->zoom.flags & ZOOM_HLOCK)
2847 factor.x = 1 / g->zoom.step_x;
2848 if (g->zoom.flags & ZOOM_VLOCK)
2851 factor.y = 1 / g->zoom.step_y;
2853 if (g->zoom.flags & ZOOM_HLOCK)
2856 factor.x = g->zoom.step_x;
2857 if (g->zoom.flags & ZOOM_VLOCK)
2860 factor.y = g->zoom.step_y;
2863 g->geom.width = (int )rint (g->geom.width * factor.x);
2864 g->geom.height = (int )rint (g->geom.height * factor.y);
2865 if (g->geom.width < g->wp.width)
2866 g->geom.width = g->wp.width;
2867 if (g->geom.height < g->wp.height)
2868 g->geom.height = g->wp.height;
2869 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2870 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2872 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2873 ((event->x-g->geom.x)/(double )cur_width));
2874 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2875 ((event->y-g->geom.y)/(double )cur_height));
2877 if (g->geom.x > g->wp.x)
2878 g->geom.x = g->wp.x;
2879 if (g->geom.y > g->wp.y)
2880 g->geom.y = g->wp.y;
2881 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2882 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2883 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2884 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2886 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2887 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2888 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2889 g->wp.height, g->zoom.x, g->zoom.y);
2891 graph_element_lists_make (g);
2892 g->cross.erase_needed = 0;
2894 axis_display (g->y_axis);
2895 axis_display (g->x_axis);
2896 update_zoom_spins (g);
2898 cross_draw (g, (int) event->x, (int) event->y);
2900 } else if (event->button == 1) {
2901 graph_select_segment (g, (int )event->x, (int )event->y);
2909 static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
2911 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2913 GdkModifierType state;
2915 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
2918 gdk_window_get_pointer (event->window, &x, &y, &state);
2922 state = event->state;
2925 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
2926 * is pressed while pointer is in motion, we will receive one more motion
2927 * notify *before* we get the button press. This last motion notify works
2928 * with stale grab coordinates */
2929 if (state & GDK_BUTTON3_MASK) {
2930 if (g->grab.grabbed) {
2931 g->geom.x = x-g->grab.x;
2932 g->geom.y = y-g->grab.y;
2934 if (g->geom.x > g->wp.x)
2935 g->geom.x = g->wp.x;
2936 if (g->geom.y > g->wp.y)
2937 g->geom.y = g->wp.y;
2938 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2939 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2940 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2941 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2942 g->cross.erase_needed = 0;
2944 axis_display (g->y_axis);
2945 axis_display (g->x_axis);
2947 cross_draw (g, x, y);
2948 } else if (g->magnify.active)
2949 magnify_move (g, x, y);
2950 } else if (state & GDK_BUTTON1_MASK) {
2951 graph_select_segment (g, x, y);
2952 if (g->cross.erase_needed)
2955 cross_draw (g, x, y);
2957 if (g->cross.erase_needed)
2960 cross_draw (g, x, y);
2966 static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
2968 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2970 debug(DBS_FENTRY) puts ("button_release_event()");
2972 if (event->button == 3)
2973 g->grab.grabbed = FALSE;
2975 if (g->magnify.active)
2976 magnify_destroy (g);
2980 static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
2982 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
2984 debug(DBS_FENTRY) puts ("key_press_event()");
2986 if (event->keyval == 32 /*space*/) {
2989 if (g->cross.draw) {
2991 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
2993 } else if (g->cross.erase_needed) {
2997 /* toggle buttons emit their "toggled" signals so don't bother doing
2998 * any real work here, it will be done in signal handlers */
3000 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
3002 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
3003 } else if (event->keyval == 't')
3004 toggle_time_origin (g);
3005 else if (event->keyval == 's')
3006 toggle_seq_origin (g);
3007 else if (event->keyval == GDK_Shift_L) {
3008 /* g->zoom.flags |= ZOOM_OUT; */
3009 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3014 static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
3016 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3018 debug(DBS_FENTRY) puts ("key_release_event()");
3020 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3021 /* g->zoom.flags &= ~ZOOM_OUT; */
3022 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3027 static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3029 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3031 if (g->cross.erase_needed)
3037 static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3039 struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3041 /* graph_pixmap_display (g); */
3042 if (g->cross.draw) {
3044 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3045 cross_draw (g, x, y);
3050 static void toggle_time_origin (struct graph *g)
3053 case GRAPH_TSEQ_STEVENS:
3054 tseq_stevens_toggle_time_origin (g);
3056 case GRAPH_TSEQ_TCPTRACE:
3057 tseq_tcptrace_toggle_time_origin (g);
3059 case GRAPH_THROUGHPUT:
3060 tput_toggle_time_origin (g);
3065 axis_display (g->x_axis);
3068 static void toggle_seq_origin (struct graph *g)
3071 case GRAPH_TSEQ_STEVENS:
3072 tseq_stevens_toggle_seq_origin (g);
3073 axis_display (g->y_axis);
3075 case GRAPH_TSEQ_TCPTRACE:
3076 tseq_tcptrace_toggle_seq_origin (g);
3077 axis_display (g->y_axis);
3080 rtt_toggle_seq_origin (g);
3081 axis_display (g->x_axis);
3088 static int get_num_dsegs (struct graph *g)
3091 struct segment *tmp;
3093 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3094 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3095 g->current->th_sport, g->current->th_dport,
3096 &tmp->ip_src, &tmp->ip_dst,
3097 tmp->th_sport, tmp->th_dport,
3098 COMPARE_CURR_DIR)) {
3105 static int get_num_acks (struct graph *g)
3108 struct segment *tmp;
3110 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3111 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3112 g->current->th_sport, g->current->th_dport,
3113 &tmp->ip_src, &tmp->ip_dst,
3114 tmp->th_sport, tmp->th_dport,
3115 COMPARE_CURR_DIR)) {
3123 * Stevens-style time-sequence grapH
3126 static void tseq_stevens_read_config (struct graph *g)
3128 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3130 g->s.tseq_stevens.seq_width = 4;
3131 g->s.tseq_stevens.seq_height = 4;
3132 g->s.tseq_stevens.flags = 0;
3134 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3135 g->title[0] = "Time/Sequence Graph";
3137 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3138 g->y_axis->label[0] = "number[B]";
3139 g->y_axis->label[1] = "Sequence";
3140 g->y_axis->label[2] = NULL;
3141 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3142 g->x_axis->label[0] = "Time[s]";
3143 g->x_axis->label[1] = NULL;
3146 /* Used by both 'stevens' and 'tcptrace' */
3147 static void tseq_initialize (struct graph *g)
3149 debug(DBS_FENTRY) puts ("tseq_initialize()");
3150 tseq_get_bounds (g);
3156 case GRAPH_TSEQ_STEVENS:
3157 tseq_stevens_read_config(g);
3159 case GRAPH_TSEQ_TCPTRACE:
3160 tseq_tcptrace_read_config(g);
3166 /* Determine "bounds"
3167 * Essentially: look for lowest/highest time and seq in the list of segments
3168 * Note that for tcptrace the "(ack + window) sequence number" would normally be expected
3169 * to be the upper bound; However, just to be safe, include the data seg sequence numbers
3170 * in the comparison for tcptrace
3171 * (e.g. to handle the case of only data segments).
3174 /* ToDo: worry about handling cases such as trying to plot seq of just 1 frame */
3176 static void tseq_get_bounds (struct graph *g)
3178 struct segment *tmp;
3180 gboolean data_frame_seen=FALSE;
3181 double data_tim_low=0;
3182 double data_tim_high=0;
3183 guint32 data_seq_cur;
3184 guint32 data_seq_nxt;
3185 guint32 data_seq_low=0;
3186 guint32 data_seq_high=0;
3187 gboolean ack_frame_seen=FALSE;
3188 double ack_tim_low=0;
3189 double ack_tim_high=0;
3190 guint32 ack_seq_cur;
3191 guint32 ack_seq_low=0;
3192 guint32 win_seq_cur;
3193 guint32 win_seq_high=0;
3195 /* go thru all segments to determine "bounds" */
3196 for (tmp=g->segments; tmp; tmp=tmp->next) {
3197 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3198 g->current->th_sport, g->current->th_dport,
3199 &tmp->ip_src, &tmp->ip_dst,
3200 tmp->th_sport, tmp->th_dport,
3201 COMPARE_CURR_DIR)) {
3204 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3205 data_seq_cur = tmp->th_seq;
3206 data_seq_nxt = data_seq_cur + tmp->th_seglen;
3207 if (! data_frame_seen) {
3208 data_tim_low = data_tim_high = tim;
3209 data_seq_low = data_seq_cur;
3210 data_seq_high = data_seq_nxt;
3211 data_frame_seen = TRUE;
3213 if (tim < data_tim_low) data_tim_low = tim;
3214 if (tim > data_tim_high) data_tim_high = tim;
3215 if (data_seq_cur < data_seq_low) data_seq_low = data_seq_cur;
3216 if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
3218 else { /* ack seg */
3219 /* skip ack processing if no ACK (e.g. in RST) */
3220 if (TCP_ACK (tmp->th_flags)) {
3221 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3222 ack_seq_cur = tmp->th_ack;
3223 win_seq_cur = ack_seq_cur + tmp->th_win;
3224 if (! ack_frame_seen) {
3225 ack_tim_low = ack_tim_high = tim;
3226 ack_seq_low = ack_seq_cur;
3227 win_seq_high = win_seq_cur;
3228 ack_frame_seen = TRUE;
3230 if (tim < ack_tim_low) ack_tim_low = tim;
3231 if (tim > ack_tim_high) ack_tim_high = tim;
3232 if (ack_seq_cur < ack_seq_low) ack_seq_low = ack_seq_cur;
3233 if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
3238 /* if 'stevens': use only data segments to determine bounds */
3239 /* if 'tcptrace': use both data and ack segments to determine bounds */
3241 case GRAPH_TSEQ_STEVENS:
3242 g->bounds.x0 = data_tim_low;
3243 g->bounds.width = data_tim_high - data_tim_low;
3244 g->bounds.y0 = data_seq_low;
3245 g->bounds.height = data_seq_high - data_seq_low;
3247 case GRAPH_TSEQ_TCPTRACE:
3248 /* If (ack_frame_seen == false) -> use 'data' segments.
3249 * Else If (data_frame_seen == false) -> use 'ack' segments.
3250 * Else -> use both data and ack segments.
3252 g->bounds.x0 = ((data_tim_low <= ack_tim_low && data_frame_seen) || (! ack_frame_seen)) ? data_tim_low : ack_tim_low;
3253 g->bounds.width = (((data_tim_high >= ack_tim_high && data_frame_seen) || (! ack_frame_seen)) ? data_tim_high : ack_tim_high) - g->bounds.x0;
3254 g->bounds.y0 = ((data_seq_low <= ack_seq_low && data_frame_seen) || (! ack_frame_seen)) ? data_seq_low : ack_seq_low;
3255 g->bounds.height = (((data_seq_high >= win_seq_high && data_frame_seen) || (! ack_frame_seen)) ? data_seq_high : win_seq_high) - g->bounds.y0;
3259 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3260 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3264 static void tseq_stevens_make_elmtlist (struct graph *g)
3266 struct segment *tmp;
3267 struct element *elements, *e;
3268 double x0 = g->bounds.x0, y0 = g->bounds.y0;
3269 guint32 seq_base = (guint32) y0;
3272 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3273 if (g->elists->elements == NULL) {
3274 int n = 1 + get_num_dsegs (g);
3275 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3277 e = elements = g->elists->elements;
3279 for (tmp=g->segments; tmp; tmp=tmp->next) {
3282 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3283 g->current->th_sport, g->current->th_dport,
3284 &tmp->ip_src, &tmp->ip_dst,
3285 tmp->th_sport, tmp->th_dport,
3286 COMPARE_CURR_DIR)) {
3290 seq_cur = tmp->th_seq - seq_base;
3291 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
3292 seqno = g->zoom.y * seq_cur;
3297 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3298 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3299 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3300 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3301 e->p.arc.filled = TRUE;
3302 e->p.arc.angle1 = 0;
3303 e->p.arc.angle2 = 23040;
3306 e->type = ELMT_NONE;
3307 g->elists->elements = elements;
3310 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3312 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3314 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3315 g->y_axis->min = g->bounds.y0;
3316 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3320 static void tseq_stevens_toggle_time_origin (struct graph *g)
3322 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3324 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3325 g->x_axis->min = g->bounds.x0;
3326 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3331 * tcptrace-style time-sequence graph
3334 static void tseq_tcptrace_read_config (struct graph *g)
3336 GdkColormap *colormap;
3339 g->s.tseq_tcptrace.flags = 0;
3340 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
3341 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
3342 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
3343 colormap = gdk_window_get_colormap (g->drawing_area->window);
3344 if (!gdk_color_parse ("black", &color)) {
3346 * XXX - do more than just warn.
3348 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3349 "Could not parse color black.");
3351 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3353 * XXX - do more than just warn.
3355 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3356 "Could not allocate color black.");
3358 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3359 if (!gdk_color_parse ("LightSlateGray", &color)) {
3361 * XXX - do more than just warn.
3363 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3364 "Could not parse color LightSlateGray.");
3366 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3368 * XXX - do more than just warn.
3370 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3371 "Could not allocate color LightSlateGray.");
3373 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3374 if (!gdk_color_parse ("LightGray", &color)) {
3376 * XXX - do more than just warn.
3378 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3379 "Could not parse color LightGray.");
3381 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3383 * XXX - do more than just warn.
3385 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3386 "Could not allocate color LightGray.");
3388 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3390 g->elists->next = (struct element_list * )
3391 g_malloc (sizeof (struct element_list));
3392 g->elists->next->next = NULL;
3393 g->elists->next->elements = NULL;
3395 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3396 g->title[0] = "Time/Sequence Graph";
3398 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3399 g->y_axis->label[0] = "number[B]";
3400 g->y_axis->label[1] = "Sequence";
3401 g->y_axis->label[2] = NULL;
3402 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3403 g->x_axis->label[0] = "Time[s]";
3404 g->x_axis->label[1] = NULL;
3407 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3409 struct segment *tmp;
3410 struct element *elements0, *e0; /* list of elmts with prio 0 */
3411 struct element *elements1, *e1; /* list of elmts with prio 1 */
3413 double p_t = 0; /* ackno, window and time of previous segment */
3414 double p_ackno = 0, p_win = 0;
3415 gboolean ack_seen=FALSE;
3420 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3422 if (g->elists->elements == NULL) {
3423 int n = 1 + 4*get_num_acks(g);
3424 e0 = elements0 = (struct element * )g_malloc (n*sizeof (struct element));
3426 e0 = elements0 = g->elists->elements;
3428 if (g->elists->next->elements == NULL ) {
3429 int n = 1 + 3*get_num_dsegs(g);
3430 e1 = elements1 = (struct element * )g_malloc (n*sizeof (struct element));
3432 e1 = elements1 = g->elists->next->elements;
3436 seq_base = (guint32) y0;
3438 for (tmp=g->segments; tmp; tmp=tmp->next) {
3442 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3445 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3446 g->current->th_sport, g->current->th_dport,
3447 &tmp->ip_src, &tmp->ip_dst,
3448 tmp->th_sport, tmp->th_dport,
3449 COMPARE_CURR_DIR)) {
3450 /* forward direction -> we need seqno and amount of data */
3453 seq_cur = tmp->th_seq - seq_base;
3454 if (TCP_SYN (tmp->th_flags) || TCP_FIN (tmp->th_flags))
3457 data = tmp->th_seglen;
3459 y1 = g->zoom.y * (seq_cur);
3460 y2 = g->zoom.y * (seq_cur + data);
3461 e1->type = ELMT_LINE;
3463 e1->gc = g->s.tseq_tcptrace.gc_seq;
3464 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3465 e1->p.line.dim.y1 = y1;
3466 e1->p.line.dim.y2 = y2;
3468 e1->type = ELMT_LINE;
3470 e1->gc = g->s.tseq_tcptrace.gc_seq;
3471 e1->p.line.dim.x1 = x - 1;
3472 e1->p.line.dim.x2 = x + 1;
3473 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
3475 e1->type = ELMT_LINE;
3477 e1->gc = g->s.tseq_tcptrace.gc_seq;
3478 e1->p.line.dim.x1 = x + 1;
3479 e1->p.line.dim.x2 = x - 1;
3480 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
3484 if (! TCP_ACK (tmp->th_flags))
3485 /* SYN's and RST's do not necessarily have ACK's*/
3487 /* backward direction -> we need ackno and window */
3488 seq_cur = tmp->th_ack - seq_base;
3489 ackno = seq_cur * g->zoom.y;
3490 win = tmp->th_win * g->zoom.y;
3493 if (ack_seen == TRUE) { /* don't plot the first ack */
3494 e0->type = ELMT_LINE;
3496 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3497 e0->p.line.dim.x1 = p_t;
3498 e0->p.line.dim.y1 = p_ackno;
3499 e0->p.line.dim.x2 = x;
3500 e0->p.line.dim.y2 = p_ackno;
3502 e0->type = ELMT_LINE;
3504 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3505 e0->p.line.dim.x1 = x;
3506 e0->p.line.dim.y1 = p_ackno;
3507 e0->p.line.dim.x2 = x;
3508 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3511 e0->type = ELMT_LINE;
3513 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3514 e0->p.line.dim.x1 = p_t;
3515 e0->p.line.dim.y1 = p_win + p_ackno;
3516 e0->p.line.dim.x2 = x;
3517 e0->p.line.dim.y2 = p_win + p_ackno;
3519 e0->type = ELMT_LINE;
3521 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3522 e0->p.line.dim.x1 = x;
3523 e0->p.line.dim.y1 = p_win + p_ackno;
3524 e0->p.line.dim.x2 = x;
3525 e0->p.line.dim.y2 = win + ackno;
3535 e0->type = ELMT_NONE;
3536 e1->type = ELMT_NONE;
3537 g->elists->elements = elements0;
3538 g->elists->next->elements = elements1;
3541 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3543 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3545 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3546 g->y_axis->min = g->bounds.y0;
3547 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3551 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3553 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3555 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3556 g->x_axis->min = g->bounds.x0;
3557 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3565 static void tput_make_elmtlist (struct graph *g)
3567 struct segment *tmp, *oldest;
3568 struct element *elements, *e;
3572 if (g->elists->elements == NULL) {
3573 int n = 1 + get_num_dsegs (g);
3574 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3576 e = elements = g->elists->elements;
3578 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3579 double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3580 dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3581 if (i>g->s.tput.nsegs) {
3582 sum -= oldest->th_seglen;
3583 oldest=oldest->next;
3585 sum += tmp->th_seglen;
3587 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3592 e->p.arc.dim.width = g->s.tput.width;
3593 e->p.arc.dim.height = g->s.tput.height;
3594 e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
3595 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3596 e->p.arc.filled = TRUE;
3597 e->p.arc.angle1 = 0;
3598 e->p.arc.angle2 = 23040;
3601 e->type = ELMT_NONE;
3602 g->elists->elements = elements;
3605 /* Purpose of <graph_type>_initialize functions:
3606 * - find maximum and minimum for both axes
3607 * - call setup routine for style struct */
3608 static void tput_initialize (struct graph *g)
3610 struct segment *tmp, *oldest, *last;
3612 double dtime, tput, tputmax=0;
3613 double t, t0, tmax = 0, y0, ymax;
3615 debug(DBS_FENTRY) puts ("tput_initialize()");
3617 tput_read_config(g);
3619 for (last=g->segments; last->next; last=last->next);
3620 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3621 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3622 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3623 if (i>g->s.tput.nsegs) {
3624 sum -= oldest->th_seglen;
3625 oldest=oldest->next;
3627 sum += tmp->th_seglen;
3629 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3632 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3637 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3643 g->bounds.width = tmax - t0;
3644 g->bounds.height = ymax - y0;
3645 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3646 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3649 static void tput_read_config (struct graph *g)
3651 debug(DBS_FENTRY) puts ("tput_read_config()");
3653 g->s.tput.width = 4;
3654 g->s.tput.height = 4;
3655 g->s.tput.nsegs = 20;
3657 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3658 g->title[0] = "Throughput Graph";
3660 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3661 g->y_axis->label[0] = "[B/s]";
3662 g->y_axis->label[1] = "Throughput";
3663 g->y_axis->label[2] = NULL;
3664 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3665 g->x_axis->label[0] = "Time[s]";
3666 g->x_axis->label[1] = NULL;
3667 g->s.tput.flags = 0;
3670 static void tput_toggle_time_origin (struct graph *g)
3672 g->s.tput.flags ^= TIME_ORIGIN;
3674 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3675 g->x_axis->min = g->bounds.x0;
3676 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3682 static void rtt_read_config (struct graph *g)
3684 debug(DBS_FENTRY) puts ("rtt_read_config()");
3687 g->s.rtt.height = 4;
3690 g->title = (const char ** )g_malloc (2 * sizeof (char *));
3691 g->title[0] = "Round Trip Time Graph";
3693 g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3694 g->y_axis->label[0] = "RTT [s]";
3695 g->y_axis->label[1] = NULL;
3696 g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3697 g->x_axis->label[0] = "Sequence Number[B]";
3698 g->x_axis->label[1] = NULL;
3701 static void rtt_initialize (struct graph *g)
3703 struct segment *tmp, *first=NULL;
3704 struct unack *unack = NULL, *u;
3706 double x0, y0, ymax;
3708 guint32 seq_base = 0;
3710 debug(DBS_FENTRY) puts ("rtt_initialize()");
3712 rtt_read_config (g);
3714 for (tmp=g->segments; tmp; tmp=tmp->next) {
3715 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3716 g->current->th_sport, g->current->th_dport,
3717 &tmp->ip_src, &tmp->ip_dst,
3718 tmp->th_sport, tmp->th_dport,
3719 COMPARE_CURR_DIR)) {
3720 guint32 seqno = tmp->th_seq;
3727 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
3728 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3729 u = rtt_get_new_unack (time, seqno);
3731 rtt_put_unack_on_list (&unack, u);
3734 if (seqno + tmp->th_seglen > xmax)
3735 xmax = seqno + tmp->th_seglen;
3737 guint32 ackno = tmp->th_ack -seq_base;
3738 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3741 for (u=unack; u; u=v)
3742 if (ackno > u->seqno) {
3743 double rtt = time - u->time;
3747 rtt_delete_unack_from_list (&unack, u);
3759 g->bounds.width = xmax;
3760 g->bounds.height = ymax - y0;
3761 g->zoom.x = g->geom.width / g->bounds.width;
3762 g->zoom.y = g->geom.height / g->bounds.height;
3765 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
3769 for (u=list; u; u=u->next)
3770 if (u->seqno== seqno)
3776 static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
3780 u = (struct unack * )g_malloc (sizeof (struct unack));
3789 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
3791 struct unack *u, *list = *l;
3793 for (u=list; u; u=u->next)
3803 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
3805 struct unack *u, *list = *l;
3814 for (u=list; u; u=u->next)
3815 if (u->next == dead) {
3816 u->next = u->next->next;
3822 static void rtt_make_elmtlist (struct graph *g)
3824 struct segment *tmp;
3825 struct unack *unack = NULL, *u;
3826 struct element *elements, *e;
3827 guint32 seq_base = (guint32) g->bounds.x0;
3829 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
3831 if (g->elists->elements == NULL) {
3832 int n = 1 + get_num_dsegs (g);
3833 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3835 e = elements = g->elists->elements;
3838 for (tmp=g->segments; tmp; tmp=tmp->next) {
3839 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3840 g->current->th_sport, g->current->th_dport,
3841 &tmp->ip_src, &tmp->ip_dst,
3842 tmp->th_sport, tmp->th_dport,
3843 COMPARE_CURR_DIR)) {
3844 guint32 seqno = tmp->th_seq -seq_base;
3846 if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
3847 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3848 u = rtt_get_new_unack (time, seqno);
3850 rtt_put_unack_on_list (&unack, u);
3853 guint32 ackno = tmp->th_ack -seq_base;
3854 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3857 for (u=unack; u; u=v)
3858 if (ackno > u->seqno) {
3859 double rtt = time - u->time;
3864 e->p.arc.dim.width = g->s.rtt.width;
3865 e->p.arc.dim.height = g->s.rtt.height;
3866 e->p.arc.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
3867 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
3868 e->p.arc.filled = TRUE;
3869 e->p.arc.angle1 = 0;
3870 e->p.arc.angle2 = 23040;
3874 rtt_delete_unack_from_list (&unack, u);
3879 e->type = ELMT_NONE;
3880 g->elists->elements = elements;
3883 static void rtt_toggle_seq_origin (struct graph *g)
3885 g->s.rtt.flags ^= SEQ_ORIGIN;
3887 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3888 g->x_axis->min = g->bounds.x0;
3893 #if defined(_WIN32) && !defined(__MINGW32__)
3894 /* replacement of Unix rint() for Windows */
3895 static int rint (double x)
3900 buf = _fcvt(x, 0, &dec, &sig);
3910 static gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
3912 return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
3917 register_tap_listener_tcp_graph(void)
3919 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (Stevens)", REGISTER_STAT_GROUP_UNSORTED,
3920 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(0));
3921 register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (tcptrace)", REGISTER_STAT_GROUP_UNSORTED,
3922 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(1));
3923 register_stat_menu_item("TCP Stream Graph/Throughput Graph", REGISTER_STAT_GROUP_UNSORTED,
3924 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(2));
3925 register_stat_menu_item("TCP Stream Graph/Round Trip Time Graph", REGISTER_STAT_GROUP_UNSORTED,
3926 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(3));