2 * TCP graph drawing code
3 * By Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
6 * $Id: tcp_graph.c,v 1.55 2004/02/23 20:28:31 ulfl Exp $
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@ethereal.com>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include <gdk/gdkkeysyms.h>
33 #include <math.h> /* rint() */
36 #ifdef NEED_SNPRINTF_H
37 # include "snprintf.h"
41 #include "globals.h" /* cfile */
42 #include <epan/packet.h> /* frame_data */
43 #include "gtkglobals.h" /* packet_list */
44 #include "simple_dialog.h"
47 #include "compat_macros.h"
50 #include "dlg_utils.h"
51 #include <epan/epan_dissect.h>
54 /* from <net/ethernet.h> */
56 guint8 ether_dhost[6]; /* destination eth addr */
57 guint8 ether_shost[6]; /* source ether addr */
58 guint16 ether_type; /* packet type ID field */
61 /* reverse engineered from capture file, not too difficult :) */
63 guint8 ppp_type; /* Protocol on PPP connection */
72 /* from <netinet/ip.h> */
86 #define IPHDR_IHL_SHIFT 0
87 #define IPHDR_IHL_MASK (0xf << IPHDR_IHL_SHIFT)
88 #define IHL(iphdrptr) ( ((iphdrptr)->version_ihl & IPHDR_IHL_MASK) >> IPHDR_IHL_SHIFT )
90 /* from <netinet/tcp.h> */
108 #define TCP_SYN(tcphdr) ( g_ntohs ((tcphdr).flags) & TH_SYN )
109 #define TCP_ACK(tcphdr) ( g_ntohs ((tcphdr).flags) & TH_ACK )
110 #define TCP_DOFF_SHIFT 12
111 #define TCP_DOFF_MASK (0xf << TCP_DOFF_SHIFT)
112 #define DOFF(tcphdr) ( ( g_ntohs ((tcphdr).flags) & TCP_DOFF_MASK) >> TCP_DOFF_SHIFT )
114 #define TXT_WIDTH 850
115 #define TXT_HEIGHT 550
117 /* for compare_headers() */
118 /* segment went the same direction as the currently selected one */
119 #define COMPARE_CURR_DIR 0
120 #define COMPARE_ANY_DIR 1
122 /* initalize_axis() */
123 #define AXIS_HORIZONTAL 0
124 #define AXIS_VERTICAL 1
127 struct segment *next;
134 struct tcphdr tcphdr;
135 int data; /* amount of data in this segment */
139 double x, y, width, height;
143 double x1, y1, x2, y2;
147 int x, y, width, height;
179 struct segment *parent;
181 struct arc_params arc;
182 struct rect_params rect;
183 struct line_params line;
187 struct element_list {
188 struct element_list *next;
189 struct element *elements;
193 struct graph *g; /* which graph we belong to */
194 GtkWidget *drawing_area;
195 GdkPixmap *pixmap[2];
197 #define AXIS_ORIENTATION 1 << 0
199 /* dim and orig (relative to origin of window) of axis' pixmap */
201 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
204 gdouble major, minor; /* major and minor ticks */
208 #define HAXIS_INIT_HEIGHT 70
209 #define VAXIS_INIT_WIDTH 100
210 #define TITLEBAR_HEIGHT 50
211 #define RMARGIN_WIDTH 30
213 struct style_tseq_tcptrace {
219 struct style_tseq_stevens {
237 #define SEQ_ORIGIN 0x1
238 /* show absolute sequence numbers (not differences from isn) */
239 #define SEQ_ORIGIN_ZERO 0x1
240 #define SEQ_ORIGIN_ISN 0x0
241 #define TIME_ORIGIN 0x10
242 /* show time from beginning of capture as opposed to time from beginning
243 * of the connection */
244 #define TIME_ORIGIN_CAP 0x10
245 #define TIME_ORIGIN_CONN 0x0
247 /* this is used by rtt module only */
256 int draw; /* indicates whether we should draw cross at all */
258 GtkToggleButton *on_toggle;
259 GtkToggleButton *off_toggle;
263 double x0, y0, width, height;
272 double step_x, step_y;
274 #define ZOOM_OUT (1 << 0)
275 #define ZOOM_HLOCK (1 << 1)
276 #define ZOOM_VLOCK (1 << 2)
277 #define ZOOM_STEPS_SAME (1 << 3)
278 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
280 /* unfortunately, we need them both because gtk_toggle_button_set_active ()
281 * with second argument FALSE doesn't do anything, somehow */
283 GtkToggleButton *in_toggle;
284 GtkToggleButton *out_toggle;
287 GtkSpinButton *h_step;
288 GtkSpinButton *v_step;
300 struct ipoint offset;
304 #define MAGZOOMS_SAME (1 << 0)
305 #define MAGZOOMS_SAME_RATIO (1 << 1)
306 #define MAGZOOMS_IGNORE (1 << 31)
309 GtkSpinButton *h_zoom, *v_zoom;
315 #define GRAPH_TSEQ_STEVENS 0
316 #define GRAPH_TSEQ_TCPTRACE 1
317 #define GRAPH_THROUGHPUT 2
320 #define GRAPH_DESTROYED (1 << 0)
321 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
323 GtkWidget *toplevel; /* keypress handler needs this */
324 GtkWidget *drawing_area;
325 GtkWidget *text; /* text widget for seg list - probably
327 #if GTK_MAJOR_VERSION < 2
328 GdkFont *font; /* font used for annotations etc. */
330 PangoFontDescription *font; /* font used for annotations etc. */
334 GdkPixmap *title_pixmap;
335 GdkPixmap *pixmap[2];
336 int displayed; /* which of both pixmaps is on screen right now */
338 GtkWidget *control_panel;
339 /* this belongs to style structs of graph types that make use of it */
340 GtkToggleButton *time_orig_conn, *seq_orig_isn;
343 /* Next 4 attribs describe the graph in natural units, before any scaling.
344 * For example, if we want to display graph of TCP conversation that
345 * started 112.309845 s after beginning of the capture and ran until
346 * 479.093582 s, 237019 B went through the connection (in one direction)
347 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
348 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
349 struct bounds bounds;
350 /* dimensions and position of the graph, both expressed already in pixels.
351 * x and y give the position of upper left corner of the graph relative
352 * to origin of the graph window, size is basically bounds*zoom */
354 /* viewport (=graph window area which is reserved for graph itself), its
355 * size and position relative to origin of the graph window */
358 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
359 * pixels, we have to scale the graph down by factor of 0.002109. This
360 * number would be zoom.y. Obviously, both directions have separate zooms.*/
363 struct magnify magnify;
364 struct axis *x_axis, *y_axis;
365 struct segment *segments;
366 struct segment *current;
367 struct element_list *elists; /* element lists */
369 struct style_tseq_stevens tseq_stevens;
370 struct style_tseq_tcptrace tseq_tcptrace;
371 struct style_tput tput;
372 struct style_rtt rtt;
376 static struct graph *graphs = NULL;
377 static GdkGC *xor_gc = NULL;
380 #define debug(section) if (debugging & section)
381 /* print function entry points */
382 #define DBS_FENTRY (1 << 0)
383 #define DBS_AXES_TICKS (1 << 1)
384 #define DBS_AXES_DRAWING (1 << 2)
385 #define DBS_GRAPH_DRAWING (1 << 3)
386 #define DBS_TPUT_ELMTS (1 << 4)
387 /*int debugging = DBS_FENTRY;*/
389 /*int debugging = DBS_AXES_TICKS;*/
390 /*int debugging = DBS_AXES_DRAWING;*/
391 /*int debugging = DBS_GRAPH_DRAWING;*/
392 /*int debugging = DBS_TPUT_ELMTS;*/
394 static void create_gui (struct graph * );
396 static void create_text_widget (struct graph * );
397 static void display_text (struct graph * );
399 static void create_drawing_area (struct graph * );
400 static void control_panel_create (struct graph * );
401 static GtkWidget *control_panel_create_zoom_group (struct graph * );
402 static GtkWidget *control_panel_create_magnify_group (struct graph * );
403 static GtkWidget *control_panel_create_cross_group (struct graph * );
404 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
405 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
406 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
407 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
408 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
409 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
410 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
411 static void callback_toplevel_destroy (GtkWidget * , gpointer );
412 static void callback_close (GtkWidget * , gpointer );
413 static void callback_time_origin (GtkWidget * , gpointer );
414 static void callback_seq_origin (GtkWidget * , gpointer );
415 static void callback_zoomlock_h (GtkWidget * , gpointer );
416 static void callback_zoomlock_v (GtkWidget * , gpointer );
417 static void callback_zoom_inout (GtkWidget * , gpointer );
418 static void callback_zoom_step (GtkWidget * , gpointer );
419 static void callback_zoom_flags (GtkWidget * , gpointer );
420 static void callback_cross_on_off (GtkWidget * , gpointer );
421 static void callback_mag_width (GtkWidget * , gpointer );
422 static void callback_mag_height (GtkWidget * , gpointer );
423 static void callback_mag_x (GtkWidget * , gpointer );
424 static void callback_mag_y (GtkWidget * , gpointer );
425 static void callback_mag_zoom (GtkWidget * , gpointer );
426 static void callback_mag_flags (GtkWidget * , gpointer );
427 static void callback_graph_type (GtkWidget * , gpointer );
428 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
429 static void callback_create_help (GtkWidget * , gpointer );
430 static void callback_close_help (GtkWidget * , gpointer );
431 static void update_zoom_spins (struct graph * );
432 static int get_headers (frame_data *, char * , struct segment * );
433 static int compare_headers (struct segment * , struct segment * , int );
434 static int get_num_dsegs (struct graph * );
435 static int get_num_acks (struct graph * );
436 static void graph_type_dependent_initialize (struct graph * );
437 static void graph_put (struct graph * );
438 static struct graph *graph_new (void);
439 static void graph_destroy (struct graph * );
440 static void graph_initialize_values (struct graph * );
441 static void graph_init_sequence (struct graph * );
442 static void draw_element_line (struct graph * , struct element * );
443 static void draw_element_arc (struct graph * , struct element * );
444 static void graph_display (struct graph * );
445 static void graph_pixmaps_create (struct graph * );
446 static void graph_pixmaps_switch (struct graph * );
447 static void graph_pixmap_draw (struct graph * );
448 static void graph_pixmap_display (struct graph * );
449 static void graph_element_lists_make (struct graph * );
450 static void graph_element_lists_free (struct graph * );
451 static void graph_element_lists_initialize (struct graph * );
452 static void graph_title_pixmap_create (struct graph * );
453 static void graph_title_pixmap_draw (struct graph * );
454 static void graph_title_pixmap_display (struct graph * );
455 static void graph_segment_list_get (struct graph * );
456 static void graph_segment_list_free (struct graph * );
457 static void graph_select_segment (struct graph * , int , int );
458 static int line_detect_collision (struct element * , int , int );
459 static int arc_detect_collision (struct element * , int , int );
460 static void axis_pixmaps_create (struct axis * );
461 static void axis_pixmaps_switch (struct axis * );
462 static void axis_display (struct axis * );
463 static void v_axis_pixmap_draw (struct axis * );
464 static void h_axis_pixmap_draw (struct axis * );
465 static void axis_pixmap_display (struct axis * );
466 static void axis_compute_ticks (struct axis * , double , double , int );
467 static double axis_zoom_get (struct axis * , int );
468 static void axis_ticks_up (int * , int * );
469 static void axis_ticks_down (int * , int * );
470 static void axis_destroy (struct axis * );
471 static int get_label_dim (struct axis * , int , double );
472 static void toggle_time_origin (struct graph * );
473 static void toggle_seq_origin (struct graph * );
474 static void cross_xor (struct graph * , int , int );
475 static void cross_draw (struct graph * , int , int );
476 static void cross_erase (struct graph * );
477 static void magnify_create (struct graph * , int , int );
478 static void magnify_move (struct graph * , int , int );
479 static void magnify_destroy (struct graph * );
480 static void magnify_draw (struct graph * );
481 static void magnify_get_geom (struct graph * , int , int );
482 static gint configure_event (GtkWidget * , GdkEventConfigure * );
483 static gint expose_event (GtkWidget * , GdkEventExpose * );
484 static gint button_press_event (GtkWidget * , GdkEventButton * );
485 static gint button_release_event (GtkWidget * , GdkEventButton * );
486 static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
487 static gint key_press_event (GtkWidget * , GdkEventKey * );
488 static gint key_release_event (GtkWidget * , GdkEventKey * );
489 static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
490 static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
491 static void tseq_stevens_initialize (struct graph * );
492 static void tseq_stevens_get_bounds (struct graph * );
493 static void tseq_stevens_read_config (struct graph * );
494 static void tseq_stevens_make_elmtlist (struct graph * );
495 static void tseq_stevens_toggle_seq_origin (struct graph * );
496 static void tseq_stevens_toggle_time_origin (struct graph * );
497 static void tseq_tcptrace_read_config (struct graph * );
498 static void tseq_tcptrace_make_elmtlist (struct graph * );
499 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
500 static void tseq_tcptrace_toggle_time_origin (struct graph * );
501 static void tput_initialize (struct graph * );
502 static void tput_read_config (struct graph * );
503 static void tput_make_elmtlist (struct graph * );
504 static void tput_toggle_time_origin (struct graph * );
505 static void rtt_read_config (struct graph * );
506 static void rtt_initialize (struct graph * );
507 static int rtt_is_retrans (struct unack * , unsigned int );
508 static struct unack *rtt_get_new_unack (double , unsigned int );
509 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
510 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
511 static void rtt_make_elmtlist (struct graph * );
512 static void rtt_toggle_seq_origin (struct graph * );
513 #if defined(WIN32) && !defined(__MINGW32__)
514 static int rint (double ); /* compiler template for Windows */
517 static char helptext[] =
519 "Here's what you can do:\n\
520 - Left Mouse Button selects segment in ethereal's packet list\n\
521 - Middle Mouse Button zooms in\n\
522 - <shift>-Middle Button zooms out\n\
523 - Right Mouse Button moves the graph (if zoomed in)\n\
524 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
525 - Space toggles crosshairs\n\
526 - 's' toggles relative/absolute sequence numbers\n\
527 - 't' toggles time origin\n\
530 "Here's what you can do:\n\
531 - <ctrl>-Left Mouse Button selects segment in ethereal's packet list\n\
532 - Left Mouse Button zooms in\n\
533 - <shift>-Left Mouse Button zooms out\n\
534 - Right Mouse Button moves the graph (if zoomed in)\n\
535 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
537 - Space bar toggles crosshairs\n\
538 - 's' - Toggles relative/absolute sequence numbers\n\
539 - 't' - Toggles time origin\n\
543 void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
545 struct segment current;
547 guint graph_type = GPOINTER_TO_INT(data);
549 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
551 if (! (g = graph_new()))
555 graph_initialize_values (g);
558 g->type = graph_type;
559 if (!get_headers (cfile.current_frame, cfile.pd, ¤t)) {
560 /* currently selected packet is neither TCP over IP over Ethernet II/PPP
561 * nor TCP over IP alone - should display some
562 * kind of warning dialog */
563 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
564 "Selected packet is not a TCP segment");
568 graph_segment_list_get(g);
570 /* display_text(g); */
571 graph_init_sequence(g);
574 static void create_gui (struct graph *g)
576 debug(DBS_FENTRY) puts ("create_gui()");
577 /* create_text_widget(g); */
578 control_panel_create (g);
579 create_drawing_area(g);
583 static void create_text_widget (struct graph *g)
585 GtkWidget *streamwindow, *txt_scrollw, *box;
587 debug(DBS_FENTRY) puts ("create_text_widget()");
588 streamwindow = window_new (GTK_WINDOW_TOPLEVEL, "Ethereal: Packet chain");
589 gtk_widget_set_name (streamwindow, "Packet chain");
590 WIDGET_SET_SIZE(streamwindow, TXT_WIDTH, TXT_HEIGHT);
591 gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
593 box = gtk_vbox_new (FALSE, 0);
594 gtk_container_add (GTK_CONTAINER (streamwindow), box);
595 gtk_widget_show (box);
597 txt_scrollw = scrolled_window_new (NULL, NULL);
598 #if GTK_MAJOR_VERSION >= 2
599 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
602 gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
603 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
604 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
605 gtk_widget_show (txt_scrollw);
607 #if GTK_MAJOR_VERSION < 2
608 g->text = gtk_text_new(NULL, NULL);
609 gtk_text_set_editable(GTK_TEXT(g->text), FALSE);
611 g->text = gtk_text_view_new();
612 gtk_text_view_set_editable(GTK_TEXT_VIEW(g->text), FALSE);
614 gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
615 gtk_widget_show (g->text);
616 gtk_widget_show (streamwindow);
618 static void display_text (struct graph *g)
622 double first_time, prev_time;
623 unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
625 #if GTK_MAJOR_VERSION >= 2
630 debug(DBS_FENTRY) puts ("display_text()");
631 if (!gdk_color_parse ("SlateGray", &color)) {
633 * XXX - do more than just warn.
635 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
636 "Could not parse color SlateGray.");
638 #if GTK_MAJOR_VERSION < 2
639 gtk_text_freeze (GTK_TEXT (g->text));
641 snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
642 "pkt num", "time", "delta first", "delta prev",
643 "seqno", "delta first", "delta prev", "data (B)");
644 gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL,
645 (const char *)line, -1);
647 first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
648 prev_time = first_time;
649 /* we have to find Initial Sequence Number for both ends of connection */
650 for (ptr=g->segments; ptr; ptr=ptr->next) {
651 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
652 isn_this = g_ntohl (ptr->tcphdr.seq);
656 for (ptr=g->segments; ptr; ptr=ptr->next) {
657 if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
658 isn_opposite = g_ntohl (ptr->tcphdr.seq);
662 seq_this_prev = isn_this;
663 seq_opposite_prev = isn_opposite;
664 for (ptr=g->segments; ptr; ptr=ptr->next) {
665 double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
666 unsigned int seq = g_ntohl (ptr->tcphdr.seq);
667 int seq_delta_isn, seq_delta_prev;
669 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
670 seq_delta_isn = seq - isn_this;
671 seq_delta_prev = seq - seq_this_prev;
675 seq_delta_isn = seq - isn_opposite;
676 seq_delta_prev = seq - seq_opposite_prev;
677 seq_opposite_prev = seq;
680 snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
681 ptr->num, time, time-first_time, time-prev_time,
682 seq, seq_delta_isn, seq_delta_prev,
683 g_ntohs (ptr->iphdr.tot_len) - 4*IHL(&(ptr->iphdr)) -
684 4*DOFF(ptr->tcphdr));
685 #if GTK_MAJOR_VERSION < 2
686 gtk_text_insert(GTK_TEXT(g->text), g->font, c, NULL,
687 (const char * )line, -1);
689 gtk_text_buffer_insert(buf, &iter, (const char *)line, -1);
693 #if GTK_MAJOR_VERSION < 2
694 gtk_text_thaw (GTK_TEXT (g->text));
699 static void create_drawing_area (struct graph *g)
701 GdkColormap *colormap;
703 #define WINDOW_TITLE_LENGTH 64
704 char window_title[WINDOW_TITLE_LENGTH];
706 debug(DBS_FENTRY) puts ("create_drawing_area()");
708 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
710 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
711 "-*-*-m-*-iso8859-2");
713 snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d - Ethereal",
715 g->toplevel = window_new (GTK_WINDOW_TOPLEVEL, window_title);
716 gtk_widget_set_name (g->toplevel, "Test Graph");
718 /* Create the drawing area */
719 g->drawing_area = gtk_drawing_area_new ();
720 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
721 gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
722 g->wp.width + g->wp.x + RMARGIN_WIDTH,
723 g->wp.height + g->wp.y + g->x_axis->s.height);
724 gtk_widget_show (g->drawing_area);
726 SIGNAL_CONNECT(g->drawing_area, "expose_event", expose_event, NULL);
727 /* this has to be done later, after the widget has been shown */
729 SIGNAL_CONNECT(g->drawing_area,"configure_event", configure_event,
732 SIGNAL_CONNECT(g->drawing_area, "motion_notify_event",
733 motion_notify_event, NULL);
734 SIGNAL_CONNECT(g->drawing_area, "button_press_event",
735 button_press_event, NULL);
736 SIGNAL_CONNECT(g->drawing_area, "button_release_event",
737 button_release_event, NULL);
738 SIGNAL_CONNECT(g->drawing_area, "leave_notify_event",
739 leave_notify_event, NULL);
740 SIGNAL_CONNECT(g->drawing_area, "enter_notify_event",
741 enter_notify_event, NULL);
742 SIGNAL_CONNECT(g->toplevel, "destroy", callback_toplevel_destroy, g);
743 /* why doesn't drawing area send key_press_signals? */
744 SIGNAL_CONNECT(g->toplevel, "key_press_event", key_press_event, NULL);
745 SIGNAL_CONNECT(g->toplevel, "key_release_event", key_release_event,
747 gtk_widget_set_events(g->toplevel,
748 GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
750 gtk_widget_set_events (g->drawing_area,
752 | GDK_LEAVE_NOTIFY_MASK
753 | GDK_ENTER_NOTIFY_MASK
754 | GDK_BUTTON_PRESS_MASK
755 | GDK_BUTTON_RELEASE_MASK
756 | GDK_POINTER_MOTION_MASK
757 | GDK_POINTER_MOTION_HINT_MASK);
760 frame = gtk_frame_new (NULL);
761 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
762 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
764 box = gtk_hbox_new (FALSE, 0);
765 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
766 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
767 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
768 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
769 gtk_widget_show (frame);
770 gtk_widget_show (box);
773 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
774 gtk_widget_show (g->toplevel);
776 /* in case we didn't get what we asked for */
777 g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
778 g->wp.x - RMARGIN_WIDTH;
779 g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
780 g->wp.y - g->x_axis->s.height;
782 #if GTK_MAJOR_VERSION < 2
783 g->font = g->drawing_area->style->font;
784 gdk_font_ref (g->font);
786 g->font = g->drawing_area->style->font_desc;
789 colormap = gdk_window_get_colormap (g->drawing_area->window);
791 xor_gc = gdk_gc_new (g->drawing_area->window);
792 gdk_gc_set_function (xor_gc, GDK_XOR);
793 if (!gdk_color_parse ("gray15", &color)) {
795 * XXX - do more than just warn.
797 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
798 "Could not parse color gray15.");
800 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
802 * XXX - do more than just warn.
804 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
805 "Could not allocate color gray15.");
807 gdk_gc_set_foreground (xor_gc, &color);
809 g->fg_gc = gdk_gc_new (g->drawing_area->window);
810 g->bg_gc = gdk_gc_new (g->drawing_area->window);
811 if (!gdk_color_parse ("white", &color)) {
813 * XXX - do more than just warn.
815 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
816 "Could not parse color white.");
818 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
820 * XXX - do more than just warn.
822 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
823 "Could not allocate color white.");
825 gdk_gc_set_foreground (g->bg_gc, &color);
827 /* this is probably quite an ugly way to get rid of the first configure
829 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
830 * event which is handled during the next return to gtk_main which is
831 * probably the gdk_gc_new() call. configure handler calls
832 * graph_element_lists_make() which is not good because the graph struct is
833 * not fully set up yet - namely we're not sure about actual geometry
834 * and we don't have the GC's at all. so we just postpone installation
835 * of configure handler until we're ready to deal with it.
837 * !!! NEMÌLO BY TO BÝT NA KONCI graph_init_sequence()? !!!
840 SIGNAL_CONNECT(g->drawing_area,"configure_event", configure_event,
843 /* puts ("exiting create_drawing_area()"); */
846 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
848 struct graph *g = (struct graph * )data;
850 if (!(g->flags & GRAPH_DESTROYED)) {
851 g->flags |= GRAPH_DESTROYED;
852 graph_destroy ((struct graph * )data);
856 static void control_panel_create (struct graph *g)
858 GtkWidget *toplevel, *notebook;
860 GtkWidget *help_bt, *close_bt, *bbox;
861 #define WINDOW_TITLE_LENGTH 64
862 char window_title[WINDOW_TITLE_LENGTH];
864 debug(DBS_FENTRY) puts ("control_panel_create()");
866 notebook = gtk_notebook_new ();
867 control_panel_add_zoom_page (g, notebook);
868 control_panel_add_magnify_page (g, notebook);
869 control_panel_add_origin_page (g, notebook);
870 control_panel_add_cross_page (g, notebook);
871 control_panel_add_graph_type_page (g, notebook);
873 snprintf (window_title, WINDOW_TITLE_LENGTH,
874 "Graph %d - Control - Ethereal", refnum);
875 toplevel = window_new (GTK_WINDOW_TOPLEVEL, window_title);
876 SIGNAL_CONNECT(toplevel, "destroy", callback_toplevel_destroy, g);
878 table = gtk_table_new (2, 1, FALSE);
879 gtk_container_add (GTK_CONTAINER (toplevel), table);
881 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
882 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
885 bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
886 gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
887 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
888 gtk_widget_show(bbox);
890 help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
891 SIGNAL_CONNECT(help_bt, "clicked", callback_create_help, g);
893 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
894 gtk_widget_grab_default(close_bt);
895 SIGNAL_CONNECT(close_bt, "clicked", callback_close, g);
897 /* gtk_widget_show_all (table); */
898 /* g->gui.control_panel = table; */
899 gtk_widget_show_all (toplevel);
900 g->gui.control_panel = toplevel;
903 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
905 GtkWidget *zoom_frame;
906 GtkWidget *zoom_lock_frame;
910 zoom_frame = control_panel_create_zoom_group (g);
911 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
912 zoom_lock_frame = control_panel_create_zoomlock_group (g);
913 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
914 box = gtk_vbox_new (FALSE, 0);
915 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
916 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
917 gtk_widget_show (box);
918 label = gtk_label_new ("Zoom");
919 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
922 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
924 GtkWidget *mag_frame, *label;
926 mag_frame = control_panel_create_magnify_group (g);
927 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
928 label = gtk_label_new ("Magnify");
929 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
932 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
934 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
935 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
936 GtkWidget *box, *label;
938 /* time origin box */
940 gtk_radio_button_new_with_label (NULL, "beginning of capture");
941 time_orig_conn = gtk_radio_button_new_with_label (
942 gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)),
943 "beginning of this TCP connection");
944 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
945 time_orig_box = gtk_vbox_new (TRUE, 0);
946 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
947 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
948 time_orig_frame = gtk_frame_new ("Time origin");
949 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
950 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
952 /* sequence number origin group */
954 gtk_radio_button_new_with_label (NULL, "initial sequence number");
955 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
956 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
957 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
958 seq_orig_box = gtk_vbox_new (TRUE, 0);
959 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
960 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
961 seq_orig_frame = gtk_frame_new ("Sequence number origin");
962 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
963 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
965 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
966 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
968 SIGNAL_CONNECT(time_orig_conn, "toggled", callback_time_origin, g);
969 SIGNAL_CONNECT(seq_orig_isn, "toggled", callback_seq_origin, g);
971 box = gtk_vbox_new (FALSE, 0);
972 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
973 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
974 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
975 gtk_widget_show (box);
976 label = gtk_label_new ("Origin");
977 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
980 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
982 GtkWidget *cross_frame, *label;
984 cross_frame = control_panel_create_cross_group (g);
985 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
986 label = gtk_label_new ("Cross");
987 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
990 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
992 GtkWidget *frame, *label;
994 frame = control_panel_create_graph_type_group (g);
995 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
996 label = gtk_label_new ("Graph type");
997 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
1000 static void callback_close (GtkWidget *widget _U_, gpointer data)
1002 struct graph *g = (struct graph * )data;
1004 if (!(g->flags & GRAPH_DESTROYED)) {
1005 g->flags |= GRAPH_DESTROYED;
1006 graph_destroy ((struct graph * )data);
1010 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
1012 GtkWidget *toplevel, *box, *text, *scroll, *bbox, *close_bt;
1013 #if GTK_MAJOR_VERSION < 2
1014 struct graph *g = (struct graph * )data;
1019 toplevel = window_new (GTK_WINDOW_TOPLEVEL, "Help for TCP graphing");
1020 WIDGET_SET_SIZE(toplevel, 500, 400);
1022 box = gtk_vbox_new (FALSE, 0);
1023 gtk_container_add (GTK_CONTAINER (toplevel), box);
1024 scroll = scrolled_window_new (NULL, NULL);
1025 #if GTK_MAJOR_VERSION >= 2
1026 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1029 gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
1030 #if GTK_MAJOR_VERSION < 2
1031 text = gtk_text_new (NULL, NULL);
1032 gtk_text_set_editable (GTK_TEXT (text), FALSE);
1033 gtk_text_set_line_wrap (GTK_TEXT (text), FALSE);
1034 gtk_text_set_word_wrap (GTK_TEXT (text), FALSE);
1035 gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1);
1037 text = gtk_text_view_new();
1038 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1039 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1040 gtk_text_buffer_set_text(buf, helptext, -1);
1042 gtk_container_add (GTK_CONTAINER (scroll), text);
1044 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1045 gtk_box_pack_start (GTK_BOX (box), bbox, FALSE, FALSE, 0);
1046 gtk_widget_show(bbox);
1048 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
1049 SIGNAL_CONNECT(close_bt, "clicked", callback_close_help, toplevel);
1050 gtk_widget_grab_default(close_bt);
1052 gtk_widget_show_all (toplevel);
1055 static void callback_close_help (GtkWidget *widget _U_, gpointer data)
1057 gtk_widget_destroy ((GtkWidget * )data);
1060 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1062 toggle_time_origin ((struct graph * )data);
1065 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1067 toggle_seq_origin ((struct graph * )data);
1070 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1072 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1073 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1074 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1075 GtkWidget *zoom_v_step_label, *zoom_v_step;
1076 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1077 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1078 GtkWidget *zoom_h_entry, *zoom_v_entry;
1079 GtkWidget *zoom_h_label, *zoom_v_label;
1081 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1082 zoom_out = gtk_radio_button_new_with_label (
1083 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1084 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1085 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1086 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1087 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1089 zoom_separator1 = gtk_hseparator_new ();
1091 zoom_h_entry = gtk_entry_new ();
1092 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1093 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1094 zoom_h_label = gtk_label_new ("Horizontal:");
1096 zoom_v_entry = gtk_entry_new ();
1097 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1098 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1099 zoom_v_label = gtk_label_new ("Vertical:");
1101 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1102 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1104 zoom_table = gtk_table_new (2, 2, FALSE);
1105 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1106 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1107 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1108 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1109 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1110 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1111 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1112 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1114 zoom_separator2 = gtk_hseparator_new ();
1116 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1117 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1118 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1119 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1121 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1122 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1123 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1124 zoom_v_step_label = gtk_label_new ("Vertical step:");
1126 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1127 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1129 zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1130 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1131 OBJECT_SET_DATA(zoom_same_toggle, "flag", (gpointer)ZOOM_STEPS_SAME);
1132 OBJECT_SET_DATA(zoom_ratio_toggle, "flag",
1133 (gpointer)ZOOM_STEPS_KEEP_RATIO);
1134 SIGNAL_CONNECT(zoom_same_toggle, "clicked", callback_zoom_flags, g);
1135 SIGNAL_CONNECT(zoom_ratio_toggle, "clicked", callback_zoom_flags, g);
1137 zoom_step_table = gtk_table_new (4, 2, FALSE);
1138 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1139 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1140 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1141 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1142 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1143 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1144 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1145 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1146 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1147 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1148 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1149 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1151 zoom_box = gtk_vbox_new (FALSE, 0);
1152 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1153 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1154 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1155 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1156 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1157 zoom_frame = gtk_frame_new ("Zoom");
1158 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1160 OBJECT_SET_DATA(zoom_h_step, "direction", GINT_TO_POINTER(0));
1161 OBJECT_SET_DATA(zoom_v_step, "direction", GINT_TO_POINTER(1));
1163 SIGNAL_CONNECT(zoom_in, "toggled", callback_zoom_inout, g);
1164 SIGNAL_CONNECT(zoom_h_step, "changed", callback_zoom_step, g);
1165 SIGNAL_CONNECT(zoom_v_step, "changed", callback_zoom_step, g);
1167 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1168 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1172 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1174 struct graph *g = (struct graph * )data;
1176 if (GTK_TOGGLE_BUTTON (toggle)->active)
1177 g->zoom.flags &= ~ZOOM_OUT;
1179 g->zoom.flags |= ZOOM_OUT;
1182 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1184 struct graph *g = (struct graph * )data;
1187 double *zoom_this, *zoom_other;
1188 GtkSpinButton *widget_this, *widget_other;
1191 direction = (int)OBJECT_GET_DATA(spin, "direction");
1192 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1195 zoom_this = &g->zoom.step_y;
1196 zoom_other = &g->zoom.step_x;
1197 widget_this = g->zoom.widget.v_step;
1198 widget_other = g->zoom.widget.h_step;
1200 zoom_this = &g->zoom.step_x;
1201 zoom_other = &g->zoom.step_y;
1202 widget_this = g->zoom.widget.h_step;
1203 widget_other = g->zoom.widget.v_step;
1206 old_this = *zoom_this;
1208 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1209 *zoom_other = value;
1210 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1211 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1212 double old_other = *zoom_other;
1213 *zoom_other *= value / old_this;
1214 if (*zoom_other < 1.0) {
1216 *zoom_this = old_this * 1.0 / old_other;
1217 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1218 } else if (*zoom_other > 5.0) {
1220 *zoom_this = old_this * 5.0 / old_other;
1221 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1223 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1227 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1229 struct graph *g = (struct graph * )data;
1230 int flag = (int)OBJECT_GET_DATA(toggle, "flag");
1232 if (GTK_TOGGLE_BUTTON (toggle)->active)
1233 g->zoom.flags |= flag;
1235 g->zoom.flags &= ~flag;
1238 static void update_zoom_spins (struct graph *g)
1242 snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
1243 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1244 snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
1245 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1248 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1250 GtkWidget *mag_width_label, *mag_width;
1251 GtkWidget *mag_height_label, *mag_height;
1252 GtkWidget *mag_x_label, *mag_x;
1253 GtkWidget *mag_y_label, *mag_y;
1254 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1255 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1256 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1257 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1258 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1259 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1260 GtkWidget *mag_box, *mag_frame;
1262 mag_width_label = gtk_label_new ("Width:");
1263 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1264 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1266 mag_height_label = gtk_label_new ("Height:");
1267 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1268 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1270 mag_x_label = gtk_label_new ("X:");
1271 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1272 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1274 mag_y_label = gtk_label_new ("Y:");
1275 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1276 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1278 mag_wh_table = gtk_table_new (4, 2, FALSE);
1279 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1280 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1281 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1282 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1283 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1284 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1285 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1286 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1287 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1288 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1289 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1290 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1291 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1292 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1293 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1294 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1296 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1297 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1298 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1300 mag_v_zoom_label = gtk_label_new ("Vertical:");
1301 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1302 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1304 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1305 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1307 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1308 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1309 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1310 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1311 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1312 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1313 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1314 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1315 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1316 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1317 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1318 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1319 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1321 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1322 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1323 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1325 mag_box = gtk_vbox_new (FALSE, 0);
1326 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1327 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1328 mag_frame = gtk_frame_new ("Magnify");
1329 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1331 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1332 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1333 OBJECT_SET_DATA(mag_h_zoom, "direction", GINT_TO_POINTER(0));
1334 OBJECT_SET_DATA(mag_v_zoom, "direction", GINT_TO_POINTER(1));
1335 OBJECT_SET_DATA(mag_zoom_same, "flag", (gpointer)MAGZOOMS_SAME);
1336 OBJECT_SET_DATA(mag_zoom_ratio, "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1338 SIGNAL_CONNECT(mag_width, "changed", callback_mag_width, g);
1339 SIGNAL_CONNECT(mag_height, "changed", callback_mag_height, g);
1340 SIGNAL_CONNECT(mag_x, "changed", callback_mag_x, g);
1341 SIGNAL_CONNECT(mag_y, "changed", callback_mag_y, g);
1342 SIGNAL_CONNECT(mag_h_zoom, "changed", callback_mag_zoom, g);
1343 SIGNAL_CONNECT(mag_v_zoom, "changed", callback_mag_zoom, g);
1344 SIGNAL_CONNECT(mag_zoom_same, "clicked", callback_mag_flags, g);
1345 SIGNAL_CONNECT(mag_zoom_ratio, "clicked", callback_mag_flags, g);
1350 static void callback_mag_width (GtkWidget *spin, gpointer data)
1352 struct graph *g = (struct graph * )data;
1354 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1357 static void callback_mag_height (GtkWidget *spin, gpointer data)
1359 struct graph *g = (struct graph * )data;
1361 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1364 static void callback_mag_x (GtkWidget *spin, gpointer data)
1366 struct graph *g = (struct graph * )data;
1368 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1371 static void callback_mag_y (GtkWidget *spin, gpointer data)
1373 struct graph *g = (struct graph * )data;
1375 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1378 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1380 struct graph *g = (struct graph * )data;
1383 double *zoom_this, *zoom_other;
1384 GtkSpinButton *widget_this, *widget_other;
1387 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1388 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1389 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1392 direction = (int)OBJECT_GET_DATA(spin, "direction");
1393 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1396 zoom_this = &g->magnify.zoom.y;
1397 zoom_other = &g->magnify.zoom.x;
1398 widget_this = g->magnify.widget.v_zoom;
1399 widget_other = g->magnify.widget.h_zoom;
1401 zoom_this = &g->magnify.zoom.x;
1402 zoom_other = &g->magnify.zoom.y;
1403 widget_this = g->magnify.widget.h_zoom;
1404 widget_other = g->magnify.widget.v_zoom;
1407 old_this = *zoom_this;
1409 if (g->magnify.flags & MAGZOOMS_SAME) {
1410 *zoom_other = value;
1411 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1412 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1413 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1414 double old_other = *zoom_other;
1415 *zoom_other *= value / old_this;
1416 if (*zoom_other < 1.0) {
1418 *zoom_this = old_this * 1.0 / old_other;
1419 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1420 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1421 } else if (*zoom_other > 25.0) {
1423 *zoom_this = old_this * 25.0 / old_other;
1424 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1425 gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1427 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1428 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1432 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1434 struct graph *g = (struct graph * )data;
1435 int flag = (int)OBJECT_GET_DATA(toggle, "flag");
1437 if (GTK_TOGGLE_BUTTON (toggle)->active)
1438 g->magnify.flags |= flag;
1440 g->magnify.flags &= ~flag;
1443 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1445 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1446 GtkWidget *zoom_lock_frame;
1448 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1449 zoom_lock_h = gtk_radio_button_new_with_label (
1450 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1452 zoom_lock_v = gtk_radio_button_new_with_label (
1453 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1455 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1456 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1457 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1459 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1460 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1461 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1462 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1464 SIGNAL_CONNECT(zoom_lock_h, "toggled", callback_zoomlock_h, g);
1465 SIGNAL_CONNECT(zoom_lock_v, "toggled", callback_zoomlock_v, g);
1467 return zoom_lock_frame;
1470 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1472 struct graph *g = (struct graph * )data;
1474 if (GTK_TOGGLE_BUTTON (toggle)->active)
1475 g->zoom.flags |= ZOOM_HLOCK;
1477 g->zoom.flags &= ~ZOOM_HLOCK;
1480 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1482 struct graph *g = (struct graph * )data;
1484 if (GTK_TOGGLE_BUTTON (toggle)->active)
1485 g->zoom.flags |= ZOOM_VLOCK;
1487 g->zoom.flags &= ~ZOOM_VLOCK;
1490 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1492 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1494 label = gtk_label_new ("Crosshairs:");
1495 off = gtk_radio_button_new_with_label (NULL, "off");
1496 on = gtk_radio_button_new_with_label (
1497 gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on");
1498 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1499 box = gtk_hbox_new (FALSE, 0);
1500 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1501 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1502 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1503 vbox = gtk_vbox_new (FALSE, 0);
1504 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1505 /* frame = gtk_frame_new ("Cross:"); */
1506 frame = gtk_frame_new (NULL);
1507 gtk_container_add (GTK_CONTAINER (frame), vbox);
1509 SIGNAL_CONNECT(on, "toggled", callback_cross_on_off, g);
1511 g->cross.on_toggle = (GtkToggleButton * )on;
1512 g->cross.off_toggle = (GtkToggleButton * )off;
1517 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1519 struct graph *g = (struct graph * )data;
1521 if (GTK_TOGGLE_BUTTON (toggle)->active) {
1523 g->cross.draw = TRUE;
1524 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
1525 cross_draw (g, x, y);
1527 g->cross.draw = FALSE;
1532 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1534 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1535 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1536 GtkWidget *graph_frame;
1538 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1539 graph_tseqttrace = gtk_radio_button_new_with_label (
1540 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1541 "Time/Sequence (tcptrace-style)");
1542 graph_tseqstevens = gtk_radio_button_new_with_label (
1543 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1544 "Time/Sequence (Stevens'-style)");
1545 graph_rtt = gtk_radio_button_new_with_label (
1546 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1549 case GRAPH_TSEQ_STEVENS:
1550 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1552 case GRAPH_TSEQ_TCPTRACE:
1553 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1555 case GRAPH_THROUGHPUT:
1556 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1559 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1562 graph_init = gtk_check_button_new_with_label ("Init on change");
1563 graph_sep = gtk_hseparator_new ();
1564 graph_box = gtk_vbox_new (FALSE, 0);
1565 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1566 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1567 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1568 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1569 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1570 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1571 graph_frame = gtk_frame_new ("Graph type:");
1572 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1574 OBJECT_SET_DATA(graph_tseqstevens, "new-graph-type",
1575 GINT_TO_POINTER(0));
1576 OBJECT_SET_DATA(graph_tseqttrace, "new-graph-type", GINT_TO_POINTER(1));
1577 OBJECT_SET_DATA(graph_tput, "new-graph-type", GINT_TO_POINTER(2));
1578 OBJECT_SET_DATA(graph_rtt, "new-graph-type", GINT_TO_POINTER(3));
1580 SIGNAL_CONNECT(graph_tseqttrace, "toggled", callback_graph_type, g);
1581 SIGNAL_CONNECT(graph_tseqstevens, "toggled", callback_graph_type, g);
1582 SIGNAL_CONNECT(graph_tput, "toggled", callback_graph_type, g);
1583 SIGNAL_CONNECT(graph_rtt, "toggled", callback_graph_type, g);
1584 SIGNAL_CONNECT(graph_init, "toggled", callback_graph_init_on_typechg,
1590 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1592 int old_type, new_type;
1593 struct graph *g = (struct graph * )data;
1595 new_type = (int)OBJECT_GET_DATA(toggle,"new-graph-type");
1597 if (!GTK_TOGGLE_BUTTON (toggle)->active)
1603 graph_element_lists_free (g);
1604 graph_element_lists_initialize (g);
1606 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1607 /* throughput graph uses differently constructed segment list so we
1608 * need to recreate it */
1609 graph_segment_list_free (g);
1610 graph_segment_list_get (g);
1613 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1614 g->geom.width = g->wp.width;
1615 g->geom.height = g->wp.height;
1616 g->geom.x = g->wp.x;
1617 g->geom.y = g->wp.y;
1619 g->x_axis->min = g->y_axis->min = 0;
1620 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1621 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1622 graph_init_sequence (g);
1625 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1627 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1630 static struct graph *graph_new (void)
1634 g = (struct graph * )calloc (1, sizeof (struct graph));
1635 graph_element_lists_initialize (g);
1637 g->x_axis = (struct axis * )calloc (1, sizeof (struct axis));
1638 g->y_axis = (struct axis * )calloc (1, sizeof (struct axis));
1640 g->x_axis->flags = 0;
1641 g->x_axis->flags |= AXIS_ORIENTATION;
1642 g->x_axis->s.x = g->x_axis->s.y = 0;
1643 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1644 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1645 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1647 g->y_axis->flags = 0;
1648 g->y_axis->flags &= ~AXIS_ORIENTATION;
1649 g->y_axis->p.x = g->y_axis->p.y = 0;
1650 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1652 g->y_axis->s.y = TITLEBAR_HEIGHT;
1653 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1658 static void graph_initialize_values (struct graph *g)
1660 g->geom.width = g->wp.width = 750;
1661 g->geom.height = g->wp.height = 550;
1662 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1663 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1665 /* g->zoom.x = g->zoom.y = 1.0; */
1666 g->zoom.step_x = g->zoom.step_y = 1.2;
1668 g->cross.draw = g->cross.erase_needed = 0;
1669 g->grab.grabbed = 0;
1670 g->magnify.active = 0;
1671 g->magnify.offset.x = g->magnify.offset.y = 0;
1672 g->magnify.width = g->magnify.height = 250;
1673 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1674 g->magnify.flags = 0;
1677 static void graph_put (struct graph *graph)
1681 for (g=graphs; g->next; g=g->next);
1687 static void graph_init_sequence (struct graph *g)
1689 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1691 graph_type_dependent_initialize (g);
1692 g->zoom.initial.x = g->zoom.x;
1693 g->zoom.initial.y = g->zoom.y;
1694 graph_element_lists_make (g);
1695 g->x_axis->s.width = g->wp.width;
1696 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1697 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1698 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1699 g->y_axis->s.height = g->wp.height;
1700 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1701 graph_pixmaps_create (g);
1702 axis_pixmaps_create (g->y_axis);
1703 axis_pixmaps_create (g->x_axis);
1704 graph_title_pixmap_create (g);
1705 graph_title_pixmap_draw (g);
1706 graph_title_pixmap_display (g);
1708 axis_display (g->y_axis);
1709 axis_display (g->x_axis);
1712 static void graph_type_dependent_initialize (struct graph *g)
1715 case GRAPH_TSEQ_STEVENS:
1716 case GRAPH_TSEQ_TCPTRACE:
1717 tseq_stevens_initialize (g);
1719 case GRAPH_THROUGHPUT:
1720 tput_initialize (g);
1730 static void graph_destroy (struct graph *g)
1733 struct graph *p=NULL;
1734 /* struct graph *tmp; */
1736 debug(DBS_FENTRY) puts ("graph_destroy()");
1738 for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next)
1742 axis_destroy (g->x_axis);
1743 axis_destroy (g->y_axis);
1744 /* gtk_widget_destroy (g->drawing_area); */
1745 gtk_widget_destroy (g->gui.control_panel);
1746 gtk_widget_destroy (g->toplevel);
1747 /* gtk_widget_destroy (g->text); */
1748 gdk_gc_unref (g->fg_gc);
1749 gdk_gc_unref (g->bg_gc);
1750 #if GTK_MAJOR_VERSION < 2
1751 gdk_font_unref (g->font);
1753 gdk_pixmap_unref (g->pixmap[0]);
1754 gdk_pixmap_unref (g->pixmap[1]);
1758 graph_segment_list_free (g);
1759 graph_element_lists_free (g);
1761 for (tmp=graphs; tmp; tmp=tmp->next)
1762 printf ("%p next: %p\n", tmp, tmp->next);
1763 printf ("p=%p, g=%p, p->next=%p, g->next=%p\n",
1764 p, g, p ? p->next : NULL, g->next);
1772 for (tmp=graphs; tmp; tmp=tmp->next)
1773 printf ("%p next: %p\n", tmp, tmp->next);
1777 /* here we collect all the external data we will ever need */
1778 static void graph_segment_list_get (struct graph *g)
1781 union wtap_pseudo_header pseudo_header;
1782 char pd[WTAP_MAX_PACKET_SIZE];
1783 struct segment *segment=NULL, *last=NULL;
1784 struct segment current;
1789 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1790 get_headers (cfile.current_frame, cfile.pd, ¤t);
1791 if (g->type == GRAPH_THROUGHPUT)
1792 condition = COMPARE_CURR_DIR;
1794 condition = COMPARE_ANY_DIR;
1796 for (ptr=cfile.plist; ptr; ptr=ptr->next) {
1797 if (!wtap_seek_read (cfile.wth, ptr->file_off, &pseudo_header,
1798 pd, ptr->cap_len, &err, &err_info)) {
1799 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1800 cf_read_error_message(err, err_info),
1805 segment = (struct segment * )malloc (sizeof (struct segment));
1807 perror ("malloc failed");
1808 if (!get_headers (ptr, pd, segment))
1809 continue; /* not TCP over IP over Ethernet II */
1810 if (compare_headers (¤t, segment, condition)) {
1811 segment->next = NULL;
1812 segment->num = ptr->num;
1813 segment->rel_secs = ptr->rel_secs;
1814 segment->rel_usecs = ptr->rel_usecs;
1815 segment->abs_secs = ptr->abs_secs;
1816 segment->abs_usecs = ptr->abs_usecs;
1817 segment->data = g_ntohs (segment->iphdr.tot_len) -
1818 4*IHL(&(segment->iphdr)) - 4*DOFF(segment->tcphdr);
1820 last->next = segment;
1822 g->segments = segment;
1825 if (ptr==cfile.current_frame)
1826 g->current = segment;
1832 static int get_headers (frame_data *fd, char *pd, struct segment *hdrs)
1834 struct ether_header *e;
1835 struct ppp_header *p;
1836 struct vlan_802_1_q *vlan;
1841 * XXX - on Alpha, even fetching one-byte fields from structures
1842 * pointed to by unaligned pointers may be risky, as, unless
1843 * the BWX instructions are being used, a one-byte load is done
1844 * by loading the word containing the byte and then extracting
1847 * This means that the references to "p->ppp_type" and
1848 * "((struct iphdr *)ip)->protocol" may turn into a load of
1849 * an unaligned word.
1851 switch (fd->lnk_t) {
1853 case WTAP_ENCAP_ETHERNET:
1855 e = (struct ether_header *)pd;
1856 switch (pntohs (&e->ether_type))
1862 case ETHERTYPE_VLAN:
1864 * This code is awful but no more than the
1865 * rest of this file!!
1867 * This really needs to be converted to
1868 * work as a TCP tap, so that the
1869 * regular dissectors take care of
1870 * finding the TCP header, rather than
1871 * doing our own *ad hoc* header
1874 vlan = (struct vlan_802_1_q *)(e + 1);
1875 if (ETHERTYPE_IP != pntohs(&vlan->type))
1885 case WTAP_ENCAP_PPP:
1886 case WTAP_ENCAP_PPP_WITH_PHDR:
1888 p = (struct ppp_header *)pd;
1889 if (p->ppp_type != PPP_IP)
1890 return FALSE; /* not IP */
1894 case WTAP_ENCAP_RAW_IP:
1900 /* Those are the only encapsulation types we handle */
1903 if (((struct iphdr *)ip)->protocol != IP_PROTO_TCP) {
1904 /* printf ("transport protocol not TCP: %#1x\n", ip->protocol); */
1907 tcp = (struct tcphdr *)((guint8 *)ip + 4*IHL((struct iphdr *)ip));
1909 memcpy(&hdrs->iphdr, ip, sizeof (struct iphdr));
1910 memcpy(&hdrs->tcphdr, tcp, sizeof (struct tcphdr));
1914 static int compare_headers (struct segment *h1, struct segment *h2, int dir)
1916 if (dir == COMPARE_CURR_DIR)
1917 return h1->iphdr.saddr == h2->iphdr.saddr &&
1918 h1->iphdr.daddr == h2->iphdr.daddr &&
1919 h1->tcphdr.source == h2->tcphdr.source &&
1920 h1->tcphdr.dest == h2->tcphdr.dest;
1922 return (h1->iphdr.saddr == h2->iphdr.saddr &&
1923 h1->iphdr.daddr == h2->iphdr.daddr &&
1924 h1->tcphdr.source == h2->tcphdr.source &&
1925 h1->tcphdr.dest == h2->tcphdr.dest) ||
1926 (h1->iphdr.saddr == h2->iphdr.daddr &&
1927 h1->iphdr.daddr == h2->iphdr.saddr &&
1928 h1->tcphdr.source == h2->tcphdr.dest &&
1929 h1->tcphdr.dest == h2->tcphdr.source);
1932 static void graph_segment_list_free (struct graph *g)
1934 struct segment *segment;
1936 while (g->segments) {
1937 segment = g->segments->next;
1939 g->segments = segment;
1944 static void graph_element_lists_initialize (struct graph *g)
1946 g->elists = (struct element_list *)calloc (1, sizeof (struct element_list));
1949 static void graph_element_lists_make (struct graph *g)
1951 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1954 case GRAPH_TSEQ_STEVENS:
1955 tseq_stevens_make_elmtlist (g);
1957 case GRAPH_TSEQ_TCPTRACE:
1958 tseq_tcptrace_make_elmtlist (g);
1960 case GRAPH_THROUGHPUT:
1961 tput_make_elmtlist (g);
1964 rtt_make_elmtlist (g);
1967 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1972 static void graph_element_lists_free (struct graph *g)
1974 struct element_list *list, *next_list;
1977 for (list=g->elists; list; list=list->next)
1978 free (list->elements);
1979 while (g->elists->next) {
1980 list = g->elists->next->next;
1981 free (g->elists->next);
1982 g->elists->next = list;
1986 for (list=g->elists; list; list=next_list) {
1987 free (list->elements);
1988 next_list = list->next;
1991 g->elists = NULL; /* just to make debugging easier */
1994 static void graph_title_pixmap_create (struct graph *g)
1996 if (g->title_pixmap)
1997 gdk_pixmap_unref (g->title_pixmap);
1999 g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
2000 g->x_axis->p.width, g->wp.y, -1);
2003 static void graph_title_pixmap_draw (struct graph *g)
2007 gdk_draw_rectangle(g->title_pixmap, g->bg_gc, TRUE, 0, 0,
2008 g->x_axis->p.width, g->wp.y);
2009 for (i=0; g->title[i]; i++) {
2011 #if GTK_MAJOR_VERSION < 2
2012 w = gdk_string_width(g->font, g->title[i]);
2013 h = gdk_string_height(g->font, g->title[i]);
2014 gdk_draw_string(g->title_pixmap, g->font, g->fg_gc,
2015 g->wp.width/2 - w/2, 20+h + i*(h+3),
2018 PangoLayout *layout;
2019 layout = gtk_widget_create_pango_layout(g->drawing_area,
2021 pango_layout_get_pixel_size(layout, &w, &h);
2022 gdk_draw_layout(g->title_pixmap, g->fg_gc,
2023 g->wp.width/2 - w/2, 20 + i*(h+3), layout);
2024 g_object_unref(G_OBJECT(layout));
2029 static void graph_title_pixmap_display (struct graph *g)
2031 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
2032 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
2035 static void graph_pixmaps_create (struct graph *g)
2037 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
2040 gdk_pixmap_unref (g->pixmap[0]);
2042 gdk_pixmap_unref (g->pixmap[1]);
2044 g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
2045 g->wp.width, g->wp.height, -1);
2046 g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
2047 g->wp.width, g->wp.height, -1);
2052 static void graph_display (struct graph *g)
2054 graph_pixmap_draw (g);
2055 graph_pixmaps_switch (g);
2056 graph_pixmap_display (g);
2059 static void graph_pixmap_display (struct graph *g)
2061 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
2062 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
2063 g->wp.width, g->wp.height);
2064 if (g->cross.erase_needed) {
2065 cross_xor(g, g->cross.x, g->cross.y);
2069 static void graph_pixmaps_switch (struct graph *g)
2071 g->displayed = 1 ^ g->displayed;
2074 static void graph_pixmap_draw (struct graph *g)
2076 struct element_list *list;
2080 debug(DBS_FENTRY) puts ("graph_display()");
2081 not_disp = 1 ^ g->displayed;
2083 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
2084 0, 0, g->wp.width, g->wp.height);
2086 for (list=g->elists; list; list=list->next)
2087 for (e=list->elements; e->type != ELMT_NONE; e++) {
2092 draw_element_line (g, e);
2095 draw_element_arc (g, e);
2103 static void draw_element_line (struct graph *g, struct element *e)
2107 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2108 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2109 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2110 x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2111 x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2112 y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2113 y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2124 if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
2125 (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
2126 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2130 if (x2 > g->wp.width-1)
2134 if (y2 > g->wp.height-1)
2135 y2 = g->wp.height-1;
2138 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
2139 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
2142 static void draw_element_arc (struct graph *g, struct element *e)
2146 x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2147 x2 = (int )e->p.arc.dim.width;
2148 y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2149 y2 = (int )e->p.arc.dim.height;
2150 if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
2152 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
2153 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
2154 y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
2157 static void axis_pixmaps_create (struct axis *axis)
2159 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2160 if (axis->pixmap[0])
2161 gdk_pixmap_unref (axis->pixmap[0]);
2162 if (axis->pixmap[1])
2163 gdk_pixmap_unref (axis->pixmap[1]);
2165 axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
2166 axis->p.width, axis->p.height, -1);
2167 axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
2168 axis->p.width, axis->p.height, -1);
2170 axis->displayed = 0;
2173 static void axis_destroy (struct axis *axis)
2175 gdk_pixmap_unref (axis->pixmap[0]);
2176 gdk_pixmap_unref (axis->pixmap[1]);
2180 static void axis_display (struct axis *axis)
2182 if (axis->flags & AXIS_ORIENTATION)
2183 h_axis_pixmap_draw (axis);
2185 v_axis_pixmap_draw (axis);
2186 axis_pixmaps_switch (axis);
2187 axis_pixmap_display (axis);
2190 static void v_axis_pixmap_draw (struct axis *axis)
2192 struct graph *g = axis->g;
2195 int not_disp, rdigits, offset, imin, imax;
2196 double bottom, top, j, fl, corr;
2197 #if GTK_MAJOR_VERSION >= 2
2198 PangoLayout *layout;
2201 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2202 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2203 (double )g->geom.height * g->bounds.height;
2204 bottom += axis->min;
2205 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2206 (double )g->geom.height * g->bounds.height;
2208 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2210 j = axis->major - floor (axis->major);
2211 for (rdigits=0; rdigits<=6; rdigits++) {
2218 not_disp = 1 ^ axis->displayed;
2219 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2220 axis->p.width, axis->p.height);
2222 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2223 (gint) ((axis->p.height-axis->s.height)/2.0), axis->s.width - 1,
2226 offset = g->wp.y + (-g->geom.y);
2227 fl = floor (axis->min / axis->major) * axis->major;
2228 corr = rint ((axis->min - fl) * g->zoom.y);
2231 major_tick = axis->major * g->zoom.y;
2232 imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2233 imax = (int) ((g->geom.height - offset + corr) / major_tick);
2234 for (i=imin; i <= imax; i++) {
2237 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2238 offset + corr + axis->s.y);
2240 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2241 i*axis->major + fl, y);
2242 if (y < 0 || y > axis->p.height)
2244 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2245 axis->s.width - 15, y, axis->s.width - 1, y);
2246 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2247 #if GTK_MAJOR_VERSION < 2
2248 w = gdk_string_width(g->font, desc);
2249 h = gdk_string_height(g->font, desc);
2250 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2251 axis->s.width-15-4-w, y + h/2, desc);
2253 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2254 pango_layout_get_pixel_size(layout, &w, &h);
2255 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2256 axis->s.width-14-4-w, y - h/2, layout);
2257 g_object_unref(G_OBJECT(layout));
2262 double minor_tick = axis->minor * g->zoom.y;
2263 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2264 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2265 for (i=imin; i <= imax; i++) {
2266 int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2267 offset + corr + axis->s.y);
2269 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2270 if (y > 0 && y < axis->p.height)
2271 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2272 axis->s.width - 8, y,
2273 axis->s.width - 1, y);
2276 for (i=0; axis->label[i]; i++) {
2278 #if GTK_MAJOR_VERSION < 2
2279 w = gdk_string_width (g->font, axis->label[i]);
2280 h = gdk_string_height (g->font, axis->label[i]);
2281 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2282 (axis->p.width - w)/2 ,
2283 TITLEBAR_HEIGHT-15 - i*(h+3), axis->label[i]);
2285 layout = gtk_widget_create_pango_layout(g->drawing_area,
2287 pango_layout_get_pixel_size(layout, &w, &h);
2288 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2289 (axis->p.width - w)/2,
2290 TITLEBAR_HEIGHT-10 - i*(h+3) - h,
2292 g_object_unref(G_OBJECT(layout));
2297 static void h_axis_pixmap_draw (struct axis *axis)
2299 struct graph *g = axis->g;
2301 double major_tick, minor_tick;
2302 int not_disp, rdigits, offset, imin, imax;
2303 double left, right, j, fl, corr;
2304 #if GTK_MAJOR_VERSION >= 2
2305 PangoLayout *layout;
2308 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2309 left = (g->wp.x-g->geom.x) /
2310 (double )g->geom.width * g->bounds.width;
2312 right = (g->wp.x-g->geom.x+g->wp.width) /
2313 (double )g->geom.width * g->bounds.width;
2315 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2317 j = axis->major - floor (axis->major);
2318 for (rdigits=0; rdigits<=6; rdigits++) {
2325 not_disp = 1 ^ axis->displayed;
2326 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2327 axis->p.width, axis->p.height);
2329 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2330 (gint) (axis->s.width + (axis->p.width-axis->s.width)/2.0), 0);
2331 offset = g->wp.x - g->geom.x;
2333 fl = floor (axis->min / axis->major) * axis->major;
2334 corr = rint ((axis->min - fl) * g->zoom.x);
2337 major_tick = axis->major*g->zoom.x;
2338 imin = (int) ((offset + corr) / major_tick + 1);
2339 imax = (int) ((offset + corr + axis->s.width) / major_tick);
2340 for (i=imin; i <= imax; i++) {
2343 int x = (int ) (rint (i * major_tick) - offset - corr);
2345 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2346 if (x < 0 || x > axis->s.width)
2348 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2349 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2350 #if GTK_MAJOR_VERSION < 2
2351 w = gdk_string_width (g->font, desc);
2352 h = gdk_string_height (g->font, desc);
2353 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2354 x - w/2, 15+h+4, desc);
2356 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2357 pango_layout_get_pixel_size(layout, &w, &h);
2358 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2359 x - w/2, 15+4, layout);
2360 g_object_unref(G_OBJECT(layout));
2363 if (axis->minor > 0) {
2365 minor_tick = axis->minor*g->zoom.x;
2366 imin = (int) ((offset + corr) / minor_tick + 1);
2367 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2368 for (i=imin; i <= imax; i++) {
2369 int x = (int) (rint (i * minor_tick) - offset - corr);
2370 if (x > 0 && x < axis->s.width)
2371 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2374 for (i=0; axis->label[i]; i++) {
2376 #if GTK_MAJOR_VERSION < 2
2377 w = gdk_string_width (g->font, axis->label[i]);
2378 h = gdk_string_height (g->font, axis->label[i]);
2379 gdk_draw_string(axis->pixmap[not_disp], g->font, g->fg_gc,
2380 axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
2383 layout = gtk_widget_create_pango_layout(g->drawing_area,
2385 pango_layout_get_pixel_size(layout, &w, &h);
2386 gdk_draw_layout(axis->pixmap[not_disp], g->fg_gc,
2387 axis->s.width - w - 50, 15+h+15 + i*(h+3),
2389 g_object_unref(G_OBJECT(layout));
2394 static void axis_pixmaps_switch (struct axis *axis)
2396 axis->displayed = 1 ^ axis->displayed;
2399 static void axis_pixmap_display (struct axis *axis)
2401 gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
2402 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2403 axis->p.width, axis->p.height);
2406 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2408 int i, j, ii, jj, ms;
2409 double zoom, x, steps[3]={ 0.1, 0.5 };
2410 int dim, check_needed, diminished;
2411 double majthresh[2]={2.0, 3.0};
2413 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2414 debug(DBS_AXES_TICKS)
2415 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2417 zoom = axis_zoom_get (axis, dir);
2419 for (i=-9; i<=12; i++) {
2420 if (x / pow (10, i) < 1)
2424 ms = (int )(x / pow (10, i));
2434 axis->major = steps[j] * pow (10, i);
2436 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2437 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2439 /* let's compute minor ticks */
2442 axis_ticks_down (&ii, &jj);
2443 axis->minor = steps[jj] * pow (10, ii);
2444 /* we don't want minors if they would be less than 10 pixels apart */
2445 if (axis->minor*zoom < 10) {
2446 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2447 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2451 check_needed = TRUE;
2453 while (check_needed) {
2454 check_needed = FALSE;
2455 dim = get_label_dim (axis, dir, xmax);
2456 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2457 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2458 axis->major, axis->minor, axis->major*zoom/dim,
2459 axis->minor*zoom/dim);
2461 /* corrections: if majors are less than majthresh[dir] times label
2462 * dimension apart, we need to use bigger ones */
2463 if (axis->major*zoom / dim < majthresh[dir]) {
2464 axis_ticks_up (&ii, &jj);
2465 axis->minor = axis->major;
2466 axis_ticks_up (&i, &j);
2467 axis->major = steps[j] * pow (10, i);
2468 check_needed = TRUE;
2469 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2472 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2473 * we could promote them to majors as well */
2474 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2475 axis_ticks_down (&i, &j);
2476 axis->major = axis->minor;
2477 axis_ticks_down (&ii, &jj);
2478 axis->minor = steps[jj] * pow (10, ii);
2479 check_needed = TRUE;
2482 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2485 if (axis->minor*zoom < 10) {
2486 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2487 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2493 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2494 "axis->minor == %.1f\n", axis->major, axis->minor);
2497 static void axis_ticks_up (int *i, int *j)
2506 static void axis_ticks_down (int *i, int *j)
2515 static int get_label_dim (struct axis *axis, int dir, double label)
2520 #if GTK_MAJOR_VERSION >= 2
2521 PangoLayout *layout;
2524 /* First, let's compute how many digits to the right of radix
2525 * we need to print */
2526 y = axis->major - floor (axis->major);
2527 for (rdigits=0; rdigits<=6; rdigits++) {
2533 snprintf (str, 32, "%.*f", rdigits, label);
2535 case AXIS_HORIZONTAL:
2536 #if GTK_MAJOR_VERSION < 2
2537 dim = gdk_string_width(axis->g->font, str);
2539 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2541 pango_layout_get_pixel_size(layout, &dim, NULL);
2542 g_object_unref(G_OBJECT(layout));
2546 #if GTK_MAJOR_VERSION < 2
2547 dim = gdk_string_height(axis->g->font, str);
2549 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2551 pango_layout_get_pixel_size(layout, NULL, &dim);
2552 g_object_unref(G_OBJECT(layout));
2556 puts ("initialize axis: an axis must be either horizontal or vertical");
2563 static double axis_zoom_get (struct axis *axis, int dir)
2566 case AXIS_HORIZONTAL:
2567 return axis->g->zoom.x;
2570 return axis->g->zoom.y;
2578 static void graph_select_segment (struct graph *g, int x, int y)
2580 struct element_list *list;
2583 debug(DBS_FENTRY) puts ("graph_select_segment()");
2586 y = g->geom.height-1 - (y - g->geom.y);
2588 for (list=g->elists; list; list=list->next)
2589 for (e=list->elements; e->type != ELMT_NONE; e++) {
2594 if (line_detect_collision (e, x, y))
2595 goto_frame(&cfile, e->parent->num);
2598 if (arc_detect_collision (e, x, y))
2599 goto_frame(&cfile, e->parent->num);
2607 static int line_detect_collision (struct element *e, int x, int y)
2611 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2612 x1 = (int )rint (e->p.line.dim.x1);
2613 x2 = (int )rint (e->p.line.dim.x2);
2615 x1 = (int )rint (e->p.line.dim.x2);
2616 x2 = (int )rint (e->p.line.dim.x1);
2618 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2619 y1 = (int )rint (e->p.line.dim.y1);
2620 y2 = (int )rint (e->p.line.dim.y2);
2622 y1 = (int )rint (e->p.line.dim.y2);
2623 y2 = (int )rint (e->p.line.dim.y1);
2626 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2628 if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
2634 static int arc_detect_collision (struct element *e, int x, int y)
2638 x1 = (int )rint (e->p.arc.dim.x);
2639 x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2640 y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2641 y2 = (int )rint (e->p.arc.dim.y);
2643 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2645 if (x1<=x && x<=x2 && y1<=y && y<=y2)
2651 static void cross_xor (struct graph *g, int x, int y)
2653 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2654 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2655 gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
2656 y, g->wp.x + g->wp.width, y);
2657 gdk_draw_line (g->drawing_area->window, xor_gc, x,
2658 g->wp.y, x, g->wp.y + g->wp.height);
2662 static void cross_draw (struct graph *g, int x, int y)
2664 cross_xor (g, x, y);
2667 g->cross.erase_needed = 1;
2670 static void cross_erase (struct graph *g)
2672 cross_xor (g, g->cross.x, g->cross.y);
2673 g->cross.erase_needed = 0;
2676 static void magnify_create (struct graph *g, int x, int y)
2679 struct element_list *list, *new_list;
2680 struct ipoint pos, offsetpos;
2683 mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph));
2684 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2686 mg->toplevel = window_new (GTK_WINDOW_POPUP, NULL);
2687 mg->drawing_area = mg->toplevel;
2688 WIDGET_SET_SIZE(mg->toplevel, g->magnify.width, g->magnify.height);
2689 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2690 /* | GDK_ENTER_NOTIFY_MASK */
2691 /* | GDK_ALL_EVENTS_MASK */
2696 mg->wp.width = g->magnify.width;
2697 mg->wp.height = g->magnify.height;
2698 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2699 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2700 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2701 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2703 /* in order to keep original element lists intact we need our own */
2704 graph_element_lists_initialize (mg);
2705 list = g->elists->next;
2706 new_list = mg->elists;
2707 for ( ; list; list=list->next) {
2709 (struct element_list * )malloc (sizeof (struct element_list));
2710 new_list = new_list->next;
2711 new_list->next = NULL;
2712 new_list->elements = NULL;
2714 graph_element_lists_make (mg);
2716 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2717 g->magnify.x = pos.x + x - g->magnify.width/2;
2718 g->magnify.y = pos.y + y - g->magnify.height/2;
2719 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2720 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2721 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2722 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2723 gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
2724 magnify_get_geom (g, x, y);
2726 gtk_widget_show (mg->drawing_area);
2728 /* we need to wait for the first expose event before we start drawing */
2729 while (!gdk_events_pending ());
2731 e = gdk_event_get ();
2733 if (e->any.type == GDK_EXPOSE) {
2741 mg->pixmap[0] = mg->pixmap[1] = NULL;
2742 graph_pixmaps_create (mg);
2744 g->magnify.active = 1;
2747 static void magnify_move (struct graph *g, int x, int y)
2749 struct ipoint pos, offsetpos;
2751 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2752 g->magnify.x = pos.x + x - g->magnify.width/2;
2753 g->magnify.y = pos.y + y - g->magnify.height/2;
2754 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2755 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2756 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2757 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2758 magnify_get_geom (g, x, y);
2759 gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
2764 static void magnify_destroy (struct graph *g)
2766 struct element_list *list;
2767 struct graph *mg = g->magnify.g;
2769 gtk_widget_destroy (GTK_WIDGET (mg->drawing_area));
2770 gdk_pixmap_unref (mg->pixmap[0]);
2771 gdk_pixmap_unref (mg->pixmap[1]);
2772 for (list=mg->elists; list; list=list->next)
2773 free (list->elements);
2774 while (mg->elists->next) {
2775 list = mg->elists->next->next;
2776 free (mg->elists->next);
2777 mg->elists->next = list;
2779 free (g->magnify.g);
2780 g->magnify.active = 0;
2783 static void magnify_get_geom (struct graph *g, int x, int y)
2787 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
2789 g->magnify.g->geom.x = g->geom.x;
2790 g->magnify.g->geom.y = g->geom.y;
2792 g->magnify.g->geom.x -=
2793 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2794 ((x-g->geom.x)/(double )g->geom.width));
2795 g->magnify.g->geom.y -=
2796 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2797 ((y-g->geom.y)/(double )g->geom.height));
2799 /* we have coords of origin of graph relative to origin of g->toplevel.
2800 * now we need them to relate to origin of magnify window */
2801 g->magnify.g->geom.x -= (g->magnify.x - posx);
2802 g->magnify.g->geom.y -= (g->magnify.y - posy);
2805 static void magnify_draw (struct graph *g)
2807 int not_disp = 1 ^ g->magnify.g->displayed;
2809 graph_pixmap_draw (g->magnify.g);
2810 /* graph pixmap is almost ready, just add border */
2811 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2812 g->magnify.width - 1, 0);
2813 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2814 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2815 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2816 0, g->magnify.height - 1);
2817 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2818 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2820 graph_pixmaps_switch (g->magnify.g);
2821 graph_pixmap_display (g->magnify.g);
2825 static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
2831 int cur_g_width, cur_g_height;
2832 int cur_wp_width, cur_wp_height;
2834 debug(DBS_FENTRY) puts ("configure_event()");
2836 for (g=graphs; g; g=g->next)
2837 if (g->drawing_area == widget)
2840 cur_wp_width = g->wp.width;
2841 cur_wp_height = g->wp.height;
2842 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2843 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2844 g->x_axis->s.width = g->wp.width;
2845 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2846 g->y_axis->p.height = g->wp.height + g->wp.y;
2847 g->y_axis->s.height = g->wp.height;
2848 g->x_axis->p.y = g->y_axis->p.height;
2849 zoom.x = (double )g->wp.width / cur_wp_width;
2850 zoom.y = (double )g->wp.height / cur_wp_height;
2851 cur_g_width = g->geom.width;
2852 cur_g_height = g->geom.height;
2853 g->geom.width = (int )rint (g->geom.width * zoom.x);
2854 g->geom.height = (int )rint (g->geom.height * zoom.y);
2855 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2856 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2857 /* g->zoom.initial.x = g->zoom.x; */
2858 /* g->zoom.initial.y = g->zoom.y; */
2860 g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
2861 (g->wp.x - g->geom.x));
2862 g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
2863 (g->wp.y - g->geom.y));
2865 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2866 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2867 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2868 g->zoom.x, g->zoom.y);
2871 update_zoom_spins (g);
2872 graph_element_lists_make (g);
2873 graph_pixmaps_create (g);
2874 graph_title_pixmap_create (g);
2875 axis_pixmaps_create (g->y_axis);
2876 axis_pixmaps_create (g->x_axis);
2877 /* we don't do actual drawing here; we leave it to expose handler */
2878 graph_pixmap_draw (g);
2879 graph_pixmaps_switch (g);
2880 graph_title_pixmap_draw (g);
2881 h_axis_pixmap_draw (g->x_axis);
2882 axis_pixmaps_switch (g->x_axis);
2883 v_axis_pixmap_draw (g->y_axis);
2884 axis_pixmaps_switch (g->y_axis);
2888 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
2892 debug(DBS_FENTRY) puts ("expose_event()");
2897 for (g=graphs; g; g=g->next)
2898 if (g->drawing_area == widget)
2901 /* lower left corner */
2902 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
2903 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2905 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
2906 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2908 graph_pixmap_display (g);
2909 graph_title_pixmap_display (g);
2910 axis_pixmap_display (g->x_axis);
2911 axis_pixmap_display (g->y_axis);
2916 static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
2920 debug(DBS_FENTRY) puts ("button_press_event()");
2922 for (g=graphs; g; g=g->next)
2923 if (g->drawing_area == widget)
2926 if (event->button == 3) {
2927 if (event->state & GDK_CONTROL_MASK)
2928 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
2930 g->grab.x = (int )rint (event->x) - g->geom.x;
2931 g->grab.y = (int )rint (event->y) - g->geom.y;
2932 g->grab.grabbed = TRUE;
2935 /* Windows mouse control: */
2936 /* [<ctrl>-left] - select packet */
2937 /* [left] - zoom in */
2938 /* [<shift>-left] - zoom out */
2939 } else if (event->button == 1) {
2940 if (event->state & GDK_CONTROL_MASK) {
2941 graph_select_segment (g, (int)event->x, (int)event->y);
2944 } else if (event->button == 2) {
2946 int cur_width = g->geom.width, cur_height = g->geom.height;
2947 struct { double x, y; } factor;
2949 if (g->zoom.flags & ZOOM_OUT) {
2950 if (g->zoom.flags & ZOOM_HLOCK)
2953 factor.x = 1 / g->zoom.step_x;
2954 if (g->zoom.flags & ZOOM_VLOCK)
2957 factor.y = 1 / g->zoom.step_y;
2959 if (g->zoom.flags & ZOOM_HLOCK)
2962 factor.x = g->zoom.step_x;
2963 if (g->zoom.flags & ZOOM_VLOCK)
2966 factor.y = g->zoom.step_y;
2969 g->geom.width = (int )rint (g->geom.width * factor.x);
2970 g->geom.height = (int )rint (g->geom.height * factor.y);
2971 if (g->geom.width < g->wp.width)
2972 g->geom.width = g->wp.width;
2973 if (g->geom.height < g->wp.height)
2974 g->geom.height = g->wp.height;
2975 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2976 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2978 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2979 ((event->x-g->geom.x)/(double )cur_width));
2980 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2981 ((event->y-g->geom.y)/(double )cur_height));
2983 if (g->geom.x > g->wp.x)
2984 g->geom.x = g->wp.x;
2985 if (g->geom.y > g->wp.y)
2986 g->geom.y = g->wp.y;
2987 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2988 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2989 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2990 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2992 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2993 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2994 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2995 g->wp.height, g->zoom.x, g->zoom.y);
2997 graph_element_lists_make (g);
2998 g->cross.erase_needed = 0;
3000 axis_display (g->y_axis);
3001 axis_display (g->x_axis);
3002 update_zoom_spins (g);
3004 cross_draw (g, (int) event->x, (int) event->y);
3006 } else if (event->button == 1) {
3007 graph_select_segment (g, (int )event->x, (int )event->y);
3015 static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
3019 GdkModifierType state;
3021 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
3023 for (g=graphs; g; g=g->next)
3024 if (g->drawing_area == widget)
3028 gdk_window_get_pointer (event->window, &x, &y, &state);
3032 state = event->state;
3035 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
3036 * is pressed while pointer is in motion, we will receive one more motion
3037 * notify *before* we get the button press. This last motion notify works
3038 * with stale grab coordinates */
3039 if (state & GDK_BUTTON3_MASK) {
3040 if (g->grab.grabbed) {
3041 g->geom.x = x-g->grab.x;
3042 g->geom.y = y-g->grab.y;
3044 if (g->geom.x > g->wp.x)
3045 g->geom.x = g->wp.x;
3046 if (g->geom.y > g->wp.y)
3047 g->geom.y = g->wp.y;
3048 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3049 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3050 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3051 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3052 g->cross.erase_needed = 0;
3054 axis_display (g->y_axis);
3055 axis_display (g->x_axis);
3057 cross_draw (g, x, y);
3058 } else if (g->magnify.active)
3059 magnify_move (g, x, y);
3060 } else if (state & GDK_BUTTON1_MASK) {
3061 graph_select_segment (g, x, y);
3062 if (g->cross.erase_needed)
3065 cross_draw (g, x, y);
3067 if (g->cross.erase_needed)
3070 cross_draw (g, x, y);
3076 static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
3080 debug(DBS_FENTRY) puts ("button_release_event()");
3082 for (g=graphs; g; g=g->next)
3083 if (g->drawing_area == widget)
3086 if (event->button == 3)
3087 g->grab.grabbed = FALSE;
3089 if (g->magnify.active)
3090 magnify_destroy (g);
3094 static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
3098 debug(DBS_FENTRY) puts ("key_press_event()");
3100 for (g=graphs; g; g=g->next)
3101 if (g->toplevel == widget)
3104 if (event->keyval == 32 /*space*/) {
3107 if (g->cross.draw) {
3109 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3111 } else if (g->cross.erase_needed) {
3115 /* toggle buttons emit their "toggled" signals so don't bother doing
3116 * any real work here, it will be done in signal handlers */
3118 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
3120 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
3121 } else if (event->keyval == 't')
3122 toggle_time_origin (g);
3123 else if (event->keyval == 's')
3124 toggle_seq_origin (g);
3125 else if (event->keyval == GDK_Shift_L) {
3126 /* g->zoom.flags |= ZOOM_OUT; */
3127 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3132 static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
3136 debug(DBS_FENTRY) puts ("key_release_event()");
3138 for (g=graphs; g; g=g->next)
3139 if (g->toplevel == widget)
3142 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3143 /* g->zoom.flags &= ~ZOOM_OUT; */
3144 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3149 static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3153 for (g=graphs; g; g=g->next)
3154 if (g->drawing_area == widget)
3157 if (g->cross.erase_needed)
3163 static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3167 for (g=graphs; g; g=g->next)
3168 if (g->drawing_area == widget)
3171 /* graph_pixmap_display (g); */
3172 if (g->cross.draw) {
3174 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3175 cross_draw (g, x, y);
3180 static void toggle_time_origin (struct graph *g)
3183 case GRAPH_TSEQ_STEVENS:
3184 tseq_stevens_toggle_time_origin (g);
3186 case GRAPH_TSEQ_TCPTRACE:
3187 tseq_tcptrace_toggle_time_origin (g);
3189 case GRAPH_THROUGHPUT:
3190 tput_toggle_time_origin (g);
3195 axis_display (g->x_axis);
3198 static void toggle_seq_origin (struct graph *g)
3201 case GRAPH_TSEQ_STEVENS:
3202 tseq_stevens_toggle_seq_origin (g);
3203 axis_display (g->y_axis);
3205 case GRAPH_TSEQ_TCPTRACE:
3206 tseq_tcptrace_toggle_seq_origin (g);
3207 axis_display (g->y_axis);
3210 rtt_toggle_seq_origin (g);
3211 axis_display (g->x_axis);
3218 static int get_num_dsegs (struct graph *g)
3221 struct segment *tmp;
3223 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3224 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3231 static int get_num_acks (struct graph *g)
3234 struct segment *tmp;
3236 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3237 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3245 * Stevens-style time-sequence grapH
3248 static void tseq_stevens_read_config (struct graph *g)
3250 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3252 g->s.tseq_stevens.seq_width = 4;
3253 g->s.tseq_stevens.seq_height = 4;
3254 g->s.tseq_stevens.flags = 0;
3256 g->title = (char ** )malloc (2 * sizeof (char *));
3257 g->title[0] = "Time/Sequence Graph";
3259 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3260 g->y_axis->label[0] = "number[B]";
3261 g->y_axis->label[1] = "Sequence";
3262 g->y_axis->label[2] = NULL;
3263 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3264 g->x_axis->label[0] = "Time[s]";
3265 g->x_axis->label[1] = NULL;
3268 static void tseq_stevens_initialize (struct graph *g)
3270 debug(DBS_FENTRY) puts ("tseq_stevens_initialize()");
3271 tseq_stevens_get_bounds (g);
3277 case GRAPH_TSEQ_STEVENS:
3278 tseq_stevens_read_config(g);
3280 case GRAPH_TSEQ_TCPTRACE:
3281 tseq_tcptrace_read_config(g);
3286 static void tseq_stevens_get_bounds (struct graph *g)
3288 struct segment *tmp, *last, *first;
3289 double t, t0, tmax, ymax;
3292 guint32 ack_base = 0;
3294 for (first=g->segments; first->next; first=first->next) {
3295 if (compare_headers (g->current, first, COMPARE_CURR_DIR))
3302 seq_base = g_ntohl (first->tcphdr.seq);
3303 for (tmp=g->segments; tmp; tmp=tmp->next) {
3304 unsigned int highest_byte_num;
3306 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3307 seq_cur = g_ntohl (tmp->tcphdr.seq) -seq_base;
3308 highest_byte_num = seq_cur + tmp->data;
3311 seq_cur = g_ntohl (tmp->tcphdr.ack_seq);
3314 highest_byte_num = seq_cur - ack_base;
3316 if (highest_byte_num > ymax)
3317 ymax = highest_byte_num;
3318 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3323 puts ("tseq_stevens_get_bounds: segment list corrupted!");
3327 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3329 g->bounds.y0 = seq_base;
3330 g->bounds.width = tmax - t0;
3331 g->bounds.height = ymax;
3332 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3333 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3336 static void tseq_stevens_make_elmtlist (struct graph *g)
3338 struct segment *tmp;
3339 struct element *elements, *e;
3340 double x0 = g->bounds.x0, y0 = g->bounds.y0;
3341 guint32 seq_base = (guint32) y0;
3344 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3345 if (g->elists->elements == NULL) {
3346 int n = 1 + get_num_dsegs (g);
3347 e = elements = (struct element * )malloc (n*sizeof (struct element));
3349 e = elements = g->elists->elements;
3351 for (tmp=g->segments; tmp; tmp=tmp->next) {
3354 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3356 seq_cur = g_ntohl (tmp->tcphdr.seq) - seq_base;
3357 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
3358 seqno = g->zoom.y * seq_cur;
3363 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3364 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3365 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3366 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3367 e->p.arc.filled = TRUE;
3368 e->p.arc.angle1 = 0;
3369 e->p.arc.angle2 = 23040;
3372 e->type = ELMT_NONE;
3373 g->elists->elements = elements;
3376 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3378 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3380 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3381 g->y_axis->min = g->bounds.y0;
3382 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3386 static void tseq_stevens_toggle_time_origin (struct graph *g)
3388 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3390 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3391 g->x_axis->min = g->bounds.x0;
3392 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3397 * tcptrace-style time-sequence graph
3400 static void tseq_tcptrace_read_config (struct graph *g)
3402 GdkColormap *colormap;
3405 g->s.tseq_tcptrace.flags = 0;
3406 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
3407 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
3408 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
3409 colormap = gdk_window_get_colormap (g->drawing_area->window);
3410 if (!gdk_color_parse ("black", &color)) {
3412 * XXX - do more than just warn.
3414 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3415 "Could not parse color black.");
3417 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3419 * XXX - do more than just warn.
3421 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3422 "Could not allocate color black.");
3424 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3425 if (!gdk_color_parse ("LightSlateGray", &color)) {
3427 * XXX - do more than just warn.
3429 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3430 "Could not parse color LightSlateGray.");
3432 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3434 * XXX - do more than just warn.
3436 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3437 "Could not allocate color LightSlateGray.");
3439 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3440 if (!gdk_color_parse ("LightGray", &color)) {
3442 * XXX - do more than just warn.
3444 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3445 "Could not parse color LightGray.");
3447 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
3449 * XXX - do more than just warn.
3451 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
3452 "Could not allocate color LightGray.");
3454 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3456 g->elists->next = (struct element_list * )
3457 malloc (sizeof (struct element_list));
3458 g->elists->next->next = NULL;
3459 g->elists->next->elements = NULL;
3461 g->title = (char ** )malloc (2 * sizeof (char *));
3462 g->title[0] = "Time/Sequence Graph";
3464 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3465 g->y_axis->label[0] = "number[B]";
3466 g->y_axis->label[1] = "Sequence";
3467 g->y_axis->label[2] = NULL;
3468 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3469 g->x_axis->label[0] = "Time[s]";
3470 g->x_axis->label[1] = NULL;
3473 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3475 struct segment *tmp;
3476 struct element *elements0, *e0; /* list of elmts with prio 0 */
3477 struct element *elements1, *e1; /* list of elmts with prio 1 */
3479 double p_t; /* ackno, window and time of previous segment */
3480 double p_ackno, p_win;
3485 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3487 if (g->elists->elements == NULL) {
3488 int n = 1 + 4*get_num_acks(g);
3489 e0 = elements0 = (struct element * )malloc (n*sizeof (struct element));
3491 e0 = elements0 = g->elists->elements;
3493 if (g->elists->next->elements == NULL ) {
3494 int n = 1 + 3*get_num_dsegs(g);
3495 e1 = elements1 = (struct element * )malloc (n*sizeof (struct element));
3497 e1 = elements1 = g->elists->next->elements;
3501 seq_base = (guint32) y0;
3502 /* initialize "previous" values */
3503 for (tmp=g->segments; tmp; tmp=tmp->next)
3504 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3507 p_ackno = (unsigned int )(g->zoom.y * (g_ntohl (tmp->tcphdr.ack_seq) - y0));
3510 p_win = g->zoom.y * g_ntohs (tmp->tcphdr.window);
3511 p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0;
3512 for (tmp=g->segments; tmp; tmp=tmp->next) {
3516 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3519 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3520 /* forward direction -> we need seqno and amount of data */
3523 seq_cur = g_ntohl (tmp->tcphdr.seq) -seq_base;
3524 if (TCP_SYN (tmp->tcphdr))
3529 y1 = g->zoom.y * (seq_cur);
3530 y2 = g->zoom.y * (seq_cur + data);
3531 e1->type = ELMT_LINE;
3533 e1->gc = g->s.tseq_tcptrace.gc_seq;
3534 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3535 e1->p.line.dim.y1 = y1;
3536 e1->p.line.dim.y2 = y2;
3538 e1->type = ELMT_LINE;
3540 e1->gc = g->s.tseq_tcptrace.gc_seq;
3541 e1->p.line.dim.x1 = x - 1;
3542 e1->p.line.dim.x2 = x + 1;
3543 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
3545 e1->type = ELMT_LINE;
3547 e1->gc = g->s.tseq_tcptrace.gc_seq;
3548 e1->p.line.dim.x1 = x + 1;
3549 e1->p.line.dim.x2 = x - 1;
3550 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
3554 if (TCP_SYN (tmp->tcphdr) && ! TCP_ACK (tmp->tcphdr))
3555 /* SYN's have ACK==0 and are useless here */
3557 /* backward direction -> we need ackno and window */
3558 seq_cur = g_ntohl (tmp->tcphdr.ack_seq) - seq_base;
3559 ackno = seq_cur * g->zoom.y;
3560 win = g_ntohs (tmp->tcphdr.window) * g->zoom.y;
3563 e0->type = ELMT_LINE;
3565 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3566 e0->p.line.dim.x1 = p_t;
3567 e0->p.line.dim.y1 = p_ackno;
3568 e0->p.line.dim.x2 = x;
3569 e0->p.line.dim.y2 = p_ackno;
3571 e0->type = ELMT_LINE;
3573 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3574 e0->p.line.dim.x1 = x;
3575 e0->p.line.dim.y1 = p_ackno;
3576 e0->p.line.dim.x2 = x;
3577 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3580 e0->type = ELMT_LINE;
3582 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3583 e0->p.line.dim.x1 = p_t;
3584 e0->p.line.dim.y1 = p_win + p_ackno;
3585 e0->p.line.dim.x2 = x;
3586 e0->p.line.dim.y2 = p_win + p_ackno;
3588 e0->type = ELMT_LINE;
3590 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3591 e0->p.line.dim.x1 = x;
3592 e0->p.line.dim.y1 = p_win + p_ackno;
3593 e0->p.line.dim.x2 = x;
3594 e0->p.line.dim.y2 = win + ackno;
3602 e0->type = ELMT_NONE;
3603 e1->type = ELMT_NONE;
3604 g->elists->elements = elements0;
3605 g->elists->next->elements = elements1;
3608 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3610 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3612 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3613 g->y_axis->min = g->bounds.y0;
3614 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3618 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3620 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3622 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3623 g->x_axis->min = g->bounds.x0;
3624 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3632 static void tput_make_elmtlist (struct graph *g)
3634 struct segment *tmp, *oldest;
3635 struct element *elements, *e;
3639 if (g->elists->elements == NULL) {
3640 int n = 1 + get_num_dsegs (g);
3641 e = elements = (struct element * )malloc (n*sizeof (struct element));
3643 e = elements = g->elists->elements;
3645 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3646 double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3647 dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3648 if (i>g->s.tput.nsegs) {
3649 sum -= oldest->data;
3650 oldest=oldest->next;
3654 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3659 e->p.arc.dim.width = g->s.tput.width;
3660 e->p.arc.dim.height = g->s.tput.height;
3661 e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
3662 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3663 e->p.arc.filled = TRUE;
3664 e->p.arc.angle1 = 0;
3665 e->p.arc.angle2 = 23040;
3668 e->type = ELMT_NONE;
3669 g->elists->elements = elements;
3672 /* Purpose of <graph_type>_initialize functions:
3673 * - find maximum and minimum for both axes
3674 * - call setup routine for style struct */
3675 static void tput_initialize (struct graph *g)
3677 struct segment *tmp, *oldest, *last;
3679 double dtime, tput, tputmax=0;
3680 double t, t0, tmax = 0, y0, ymax;
3682 debug(DBS_FENTRY) puts ("tput_initialize()");
3684 tput_read_config(g);
3686 for (last=g->segments; last->next; last=last->next);
3687 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3688 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3689 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3690 if (i>g->s.tput.nsegs) {
3691 sum -= oldest->data;
3692 oldest=oldest->next;
3696 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3699 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3704 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3710 g->bounds.width = tmax - t0;
3711 g->bounds.height = ymax - y0;
3712 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3713 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3716 static void tput_read_config (struct graph *g)
3718 debug(DBS_FENTRY) puts ("tput_read_config()");
3720 g->s.tput.width = 4;
3721 g->s.tput.height = 4;
3722 g->s.tput.nsegs = 20;
3724 g->title = (char ** )malloc (2 * sizeof (char *));
3725 g->title[0] = "Throughput Graph";
3727 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3728 g->y_axis->label[0] = "[B/s]";
3729 g->y_axis->label[1] = "Throughput";
3730 g->y_axis->label[2] = NULL;
3731 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3732 g->x_axis->label[0] = "Time[s]";
3733 g->x_axis->label[1] = NULL;
3734 g->s.tput.flags = 0;
3737 static void tput_toggle_time_origin (struct graph *g)
3739 g->s.tput.flags ^= TIME_ORIGIN;
3741 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3742 g->x_axis->min = g->bounds.x0;
3743 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3749 static void rtt_read_config (struct graph *g)
3751 debug(DBS_FENTRY) puts ("rtt_read_config()");
3754 g->s.rtt.height = 4;
3757 g->title = (char ** )malloc (2 * sizeof (char *));
3758 g->title[0] = "Round Trip Time Graph";
3760 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3761 g->y_axis->label[0] = "RTT [s]";
3762 g->y_axis->label[1] = NULL;
3763 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3764 g->x_axis->label[0] = "Sequence Number[B]";
3765 g->x_axis->label[1] = NULL;
3768 static void rtt_initialize (struct graph *g)
3770 struct segment *tmp, *first=NULL;
3771 struct unack *unack = NULL, *u;
3773 double x0, y0, ymax;
3775 guint32 seq_base = 0;
3777 debug(DBS_FENTRY) puts ("rtt_initialize()");
3779 rtt_read_config (g);
3781 for (tmp=g->segments; tmp; tmp=tmp->next) {
3782 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3783 guint32 seqno = g_ntohl (tmp->tcphdr.seq);
3790 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3791 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3792 u = rtt_get_new_unack (time, seqno);
3794 rtt_put_unack_on_list (&unack, u);
3797 if (seqno + tmp->data > xmax)
3798 xmax = seqno + tmp->data;
3800 guint32 ackno = g_ntohl (tmp->tcphdr.ack_seq) -seq_base;
3801 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3804 for (u=unack; u; u=v)
3805 if (ackno > u->seqno) {
3806 double rtt = time - u->time;
3810 rtt_delete_unack_from_list (&unack, u);
3822 g->bounds.width = xmax;
3823 g->bounds.height = ymax - y0;
3824 g->zoom.x = g->geom.width / g->bounds.width;
3825 g->zoom.y = g->geom.height / g->bounds.height;
3828 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
3832 for (u=list; u; u=u->next)
3833 if (u->seqno== seqno)
3839 static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
3843 u = (struct unack * )malloc (sizeof (struct unack));
3852 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
3854 struct unack *u, *list = *l;
3856 for (u=list; u; u=u->next)
3866 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
3868 struct unack *u, *list = *l;
3877 for (u=list; u; u=u->next)
3878 if (u->next == dead) {
3879 u->next = u->next->next;
3885 static void rtt_make_elmtlist (struct graph *g)
3887 struct segment *tmp;
3888 struct unack *unack = NULL, *u;
3889 struct element *elements, *e;
3890 guint32 seq_base = (guint32) g->bounds.x0;
3892 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
3894 if (g->elists->elements == NULL) {
3895 int n = 1 + get_num_dsegs (g);
3896 e = elements = (struct element * )malloc (n*sizeof (struct element));
3898 e = elements = g->elists->elements;
3900 for (tmp=g->segments; tmp; tmp=tmp->next) {
3901 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3902 guint32 seqno = g_ntohl (tmp->tcphdr.seq) -seq_base;
3904 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3905 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3906 u = rtt_get_new_unack (time, seqno);
3908 rtt_put_unack_on_list (&unack, u);
3911 guint32 ackno = g_ntohl (tmp->tcphdr.ack_seq) -seq_base;
3912 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3915 for (u=unack; u; u=v)
3916 if (ackno > u->seqno) {
3917 double rtt = time - u->time;
3922 e->p.arc.dim.width = g->s.rtt.width;
3923 e->p.arc.dim.height = g->s.rtt.height;
3924 e->p.arc.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
3925 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
3926 e->p.arc.filled = TRUE;
3927 e->p.arc.angle1 = 0;
3928 e->p.arc.angle2 = 23040;
3932 rtt_delete_unack_from_list (&unack, u);
3937 e->type = ELMT_NONE;
3938 g->elists->elements = elements;
3941 static void rtt_toggle_seq_origin (struct graph *g)
3943 g->s.rtt.flags ^= SEQ_ORIGIN;
3945 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3946 g->x_axis->min = g->bounds.x0;
3951 #if defined(WIN32) && !defined(__MINGW32__)
3952 /* replacement of Unix rint() for Windows */
3953 static int rint (double x)
3958 buf = _fcvt(x, 0, &dec, &sig);
3968 gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt)
3970 return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
3975 register_tap_listener_tcp_graph(void)
3977 register_tap_menu_item("TCP Stream Graphs/Time-Sequence Graph (Stevens)", REGISTER_TAP_GROUP_NONE,
3978 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(0));
3979 register_tap_menu_item("TCP Stream Graphs/Time-Sequence Graph (tcptrace)", REGISTER_TAP_GROUP_NONE,
3980 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(1));
3981 register_tap_menu_item("TCP Stream Graphs/Throughput Graph", REGISTER_TAP_GROUP_NONE,
3982 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(2));
3983 register_tap_menu_item("TCP Stream Graphs/Round Trip Time Graph", REGISTER_TAP_GROUP_NONE,
3984 tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(3));