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.17 2002/03/05 12:03:27 guy 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>
35 #include <math.h> /* rint() */
37 #include <sys/types.h> /* freebsd requires this */
39 #ifdef NEED_SNPRINTF_H
40 # include "snprintf.h"
44 # include <netinet/in.h> /* ntohs(), IPPROTO_TCP */
45 # include <arpa/inet.h> /* inet_ntoa() */
50 #include "globals.h" /* cfile */
51 #include <epan/packet.h> /* frame_data */
52 #include "gtkglobals.h" /* packet_list */
53 #include "simple_dialog.h"
55 #include "tcp_graph.h"
57 /* from <net/ethernet.h> */
59 guint8 ether_dhost[6]; /* destination eth addr */
60 guint8 ether_shost[6]; /* source ether addr */
61 guint16 ether_type; /* packet type ID field */
63 #define ETHERTYPE_IP 0x0800
66 /* reverse engineered from capture file, not too difficult :) */
68 guint8 ppp_type; /* Protocol on PPP connection */
70 #define PPPTYPE_IP 0x21
73 /* from <netinet/ip.h> */
87 #define IPHDR_IHL_SHIFT 0
88 #define IPHDR_IHL_MASK (0xf << IPHDR_IHL_SHIFT)
89 #define IHL(iphdrptr) ( ((iphdrptr)->version_ihl & IPHDR_IHL_MASK) >> IPHDR_IHL_SHIFT )
91 /* from <netinet/tcp.h> */
109 #define TCP_SYN(tcphdr) ( ntohs ((tcphdr).flags) & TH_SYN )
110 #define TCP_ACK(tcphdr) ( ntohs ((tcphdr).flags) & TH_ACK )
111 #define TCP_DOFF_SHIFT 12
112 #define TCP_DOFF_MASK (0xf << TCP_DOFF_SHIFT)
113 #define DOFF(tcphdr) ( ( ntohs ((tcphdr).flags) & TCP_DOFF_MASK) >> TCP_DOFF_SHIFT )
115 #define TXT_WIDTH 850
116 #define TXT_HEIGHT 550
118 /* for compare_headers() */
119 /* segment went the same direction as the currently selected one */
120 #define COMPARE_CURR_DIR 0
121 #define COMPARE_ANY_DIR 1
123 /* initalize_axis() */
124 #define AXIS_HORIZONTAL 0
125 #define AXIS_VERTICAL 1
128 struct segment *next;
135 struct tcphdr tcphdr;
136 int data; /* amount of data in this segment */
140 double x, y, width, height;
144 double x1, y1, x2, y2;
148 int x, y, width, height;
180 struct segment *parent;
182 struct arc_params arc;
183 struct rect_params rect;
184 struct line_params line;
188 struct element_list {
189 struct element_list *next;
190 struct element *elements;
194 struct graph *g; /* which graph we belong to */
195 GtkWidget *drawing_area;
196 GdkPixmap *pixmap[2];
198 #define AXIS_ORIENTATION 1 << 0
200 /* dim and orig (relative to origin of window) of axis' pixmap */
202 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
205 gdouble major, minor; /* major and minor ticks */
209 #define HAXIS_INIT_HEIGHT 70
210 #define VAXIS_INIT_WIDTH 100
211 #define TITLEBAR_HEIGHT 50
212 #define RMARGIN_WIDTH 30
214 struct style_tseq_tcptrace {
220 struct style_tseq_stevens {
238 #define SEQ_ORIGIN 0x1
239 /* show absolute sequence numbers (not differences from isn) */
240 #define SEQ_ORIGIN_ZERO 0x1
241 #define SEQ_ORIGIN_ISN 0x0
242 #define TIME_ORIGIN 0x10
243 /* show time from beginning of capture as opposed to time from beginning
244 * of the connection */
245 #define TIME_ORIGIN_CAP 0x10
246 #define TIME_ORIGIN_CONN 0x0
248 /* this is used by rtt module only */
257 int draw; /* indicates whether we should draw cross at all */
259 GtkToggleButton *on_toggle;
260 GtkToggleButton *off_toggle;
264 double x0, y0, width, height;
273 double step_x, step_y;
275 #define ZOOM_OUT (1 << 0)
276 #define ZOOM_HLOCK (1 << 1)
277 #define ZOOM_VLOCK (1 << 2)
278 #define ZOOM_STEPS_SAME (1 << 3)
279 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
281 /* unfortunately, we need them both because gtk_toggle_button_set_active ()
282 * with second argument FALSE doesn't do anything, somehow */
284 GtkToggleButton *in_toggle;
285 GtkToggleButton *out_toggle;
288 GtkSpinButton *h_step;
289 GtkSpinButton *v_step;
301 struct ipoint offset;
305 #define MAGZOOMS_SAME (1 << 0)
306 #define MAGZOOMS_SAME_RATIO (1 << 1)
307 #define MAGZOOMS_IGNORE (1 << 31)
310 GtkSpinButton *h_zoom, *v_zoom;
316 #define GRAPH_TSEQ_STEVENS 0
317 #define GRAPH_TSEQ_TCPTRACE 1
318 #define GRAPH_THROUGHPUT 2
321 #define GRAPH_DESTROYED (1 << 0)
322 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
324 GtkWidget *toplevel; /* keypress handler needs this */
325 GtkWidget *drawing_area;
326 GtkWidget *text; /* text widget for seg list - probably temporary */
327 GdkFont *font; /* font used for annotations etc. */
330 GdkPixmap *title_pixmap;
331 GdkPixmap *pixmap[2];
332 int displayed; /* which of both pixmaps is on screen right now */
334 GtkWidget *control_panel;
335 /* this belongs to style structs of graph types that make use of it */
336 GtkToggleButton *time_orig_conn, *seq_orig_isn;
339 /* Next 4 attribs describe the graph in natural units, before any scaling.
340 * For example, if we want to display graph of TCP conversation that
341 * started 112.309845 s after beginning of the capture and ran until
342 * 479.093582 s, 237019 B went through the connection (in one direction)
343 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
344 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
345 struct bounds bounds;
346 /* dimensions and position of the graph, both expressed already in pixels.
347 * x and y give the position of upper left corner of the graph relative
348 * to origin of the graph window, size is basically bounds*zoom */
350 /* viewport (=graph window area which is reserved for graph itself), its
351 * size and position relative to origin of the graph window */
354 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
355 * pixels, we have to scale the graph down by factor of 0.002109. This
356 * number would be zoom.y. Obviously, both directions have separate zooms.*/
359 struct magnify magnify;
360 struct axis *x_axis, *y_axis;
361 struct segment *segments;
362 struct segment *current;
363 struct element_list *elists; /* element lists */
365 struct style_tseq_stevens tseq_stevens;
366 struct style_tseq_tcptrace tseq_tcptrace;
367 struct style_tput tput;
368 struct style_rtt rtt;
372 static struct graph *graphs = NULL;
373 static GdkGC *xor_gc = NULL;
376 #define debug(section) if (debugging & section)
377 /* print function entry points */
378 #define DBS_FENTRY (1 << 0)
379 #define DBS_AXES_TICKS (1 << 1)
380 #define DBS_AXES_DRAWING (1 << 2)
381 #define DBS_GRAPH_DRAWING (1 << 3)
382 #define DBS_TPUT_ELMTS (1 << 4)
383 /*int debugging = DBS_FENTRY;*/
385 /*int debugging = DBS_AXES_TICKS;*/
386 /*int debugging = DBS_AXES_DRAWING;*/
387 /*int debugging = DBS_GRAPH_DRAWING;*/
388 /*int debugging = DBS_TPUT_ELMTS;*/
390 static void create_gui (struct graph * );
391 static void create_text_widget (struct graph * );
392 static void display_text (struct graph * );
393 static void create_drawing_area (struct graph * );
394 static void control_panel_create (struct graph * );
395 static GtkWidget *control_panel_create_zoom_group (struct graph * );
396 static GtkWidget *control_panel_create_magnify_group (struct graph * );
397 static GtkWidget *control_panel_create_cross_group (struct graph * );
398 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
399 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
400 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
401 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
402 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
403 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
404 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
405 static void callback_toplevel_destroy (GtkWidget * , gpointer );
406 static void callback_close (GtkWidget * , gpointer );
407 static void callback_time_origin (GtkWidget * , gpointer );
408 static void callback_seq_origin (GtkWidget * , gpointer );
409 static void callback_zoomlock_h (GtkWidget * , gpointer );
410 static void callback_zoomlock_v (GtkWidget * , gpointer );
411 static void callback_zoom_inout (GtkWidget * , gpointer );
412 static void callback_zoom_step (GtkWidget * , gpointer );
413 static void callback_zoom_flags (GtkWidget * , gpointer );
414 static void callback_cross_on_off (GtkWidget * , gpointer );
415 static void callback_mag_width (GtkWidget * , gpointer );
416 static void callback_mag_height (GtkWidget * , gpointer );
417 static void callback_mag_x (GtkWidget * , gpointer );
418 static void callback_mag_y (GtkWidget * , gpointer );
419 static void callback_mag_zoom (GtkWidget * , gpointer );
420 static void callback_mag_flags (GtkWidget * , gpointer );
421 static void callback_graph_type (GtkWidget * , gpointer );
422 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
423 static void callback_create_help (GtkWidget * , gpointer );
424 static void callback_close_help (GtkWidget * , gpointer );
425 static void update_zoom_spins (struct graph * );
426 static int get_headers (frame_data *, char * , struct segment * );
427 static int compare_headers (struct segment * , struct segment * , int );
428 static int get_num_dsegs (struct graph * );
429 static int get_num_acks (struct graph * );
430 static void graph_type_dependent_initialize (struct graph * );
431 static void graph_put (struct graph * );
432 static struct graph *graph_new (void);
433 static void graph_destroy (struct graph * );
434 static void graph_initialize_values (struct graph * );
435 static void graph_init_sequence (struct graph * );
436 static void draw_element_line (struct graph * , struct element * );
437 static void draw_element_arc (struct graph * , struct element * );
438 static void graph_display (struct graph * );
439 static void graph_pixmaps_create (struct graph * );
440 static void graph_pixmaps_switch (struct graph * );
441 static void graph_pixmap_draw (struct graph * );
442 static void graph_pixmap_display (struct graph * );
443 static void graph_element_lists_make (struct graph * );
444 static void graph_element_lists_free (struct graph * );
445 static void graph_element_lists_initialize (struct graph * );
446 static void graph_title_pixmap_create (struct graph * );
447 static void graph_title_pixmap_draw (struct graph * );
448 static void graph_title_pixmap_display (struct graph * );
449 static void graph_segment_list_get (struct graph * );
450 static void graph_segment_list_free (struct graph * );
451 static void graph_select_segment (struct graph * , int , int );
452 static int line_detect_collision (struct element * , int , int );
453 static int arc_detect_collision (struct element * , int , int );
454 static void update_packet_list (int );
455 static void axis_pixmaps_create (struct axis * );
456 static void axis_pixmaps_switch (struct axis * );
457 static void axis_display (struct axis * );
458 static void v_axis_pixmap_draw (struct axis * );
459 static void h_axis_pixmap_draw (struct axis * );
460 static void axis_pixmap_display (struct axis * );
461 static void axis_compute_ticks (struct axis * , double , double , int );
462 static double axis_zoom_get (struct axis * , int );
463 static void axis_ticks_up (int * , int * );
464 static void axis_ticks_down (int * , int * );
465 static void axis_destroy (struct axis * );
466 static int get_label_dim (struct axis * , int , double );
467 static void toggle_time_origin (struct graph * );
468 static void toggle_seq_origin (struct graph * );
469 static void cross_xor (struct graph * , int , int );
470 static void cross_draw (struct graph * , int , int );
471 static void cross_erase (struct graph * );
472 static void magnify_create (struct graph * , int , int );
473 static void magnify_move (struct graph * , int , int );
474 static void magnify_destroy (struct graph * );
475 static void magnify_draw (struct graph * );
476 static void magnify_get_geom (struct graph * , int , int );
477 static gint configure_event (GtkWidget * , GdkEventConfigure * );
478 static gint expose_event (GtkWidget * , GdkEventExpose * );
479 static gint button_press_event (GtkWidget * , GdkEventButton * );
480 static gint button_release_event (GtkWidget * , GdkEventButton * );
481 static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
482 static gint key_press_event (GtkWidget * , GdkEventKey * );
483 static gint key_release_event (GtkWidget * , GdkEventKey * );
484 static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
485 static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
486 static void tseq_stevens_initialize (struct graph * );
487 static void tseq_stevens_get_bounds (struct graph * );
488 static void tseq_stevens_read_config (struct graph * );
489 static void tseq_stevens_make_elmtlist (struct graph * );
490 static void tseq_stevens_toggle_seq_origin (struct graph * );
491 static void tseq_stevens_toggle_time_origin (struct graph * );
492 static void tseq_tcptrace_read_config (struct graph * );
493 static void tseq_tcptrace_make_elmtlist (struct graph * );
494 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
495 static void tseq_tcptrace_toggle_time_origin (struct graph * );
496 static void tput_initialize (struct graph * );
497 static void tput_read_config (struct graph * );
498 static void tput_make_elmtlist (struct graph * );
499 static void tput_toggle_time_origin (struct graph * );
500 static void rtt_read_config (struct graph * );
501 static void rtt_initialize (struct graph * );
502 static int rtt_is_retrans (struct unack * , unsigned int );
503 static struct unack *rtt_get_new_unack (double , unsigned int );
504 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
505 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
506 static void rtt_make_elmtlist (struct graph * );
507 static void rtt_toggle_seq_origin (struct graph * );
509 static int rint (double ); /* compiler template for Windows */
512 static char helptext[] =
514 "Here's what you can do:\n\
515 - Left Mouse Button selects segment in ethereal's packet list\n\
516 - Middle Mouse Button zooms in\n\
517 - <shift>-Middle Button zooms out\n\
518 - Right Mouse Button moves the graph (if zoomed in)\n\
519 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
520 - Space toggles crosshairs\n\
521 - 's' toggles relative/absolute sequence numbers\n\
522 - 't' toggles time origin\n\
525 "Here's what you can do:\n\
526 - <ctrl>-Left Mouse Button selects segment in ethereal's packet list\n\
527 - Left Mouse Button zooms in\n\
528 - <shift>-Left Mouse Button zooms out\n\
529 - Right Mouse Button moves the graph (if zoomed in)\n\
530 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
532 - Space bar toggles crosshairs\n\
533 - 's' - Toggles relative/absolute sequence numbers\n\
534 - 't' - Toggles time origin\n\
538 void tcp_graph_cb (GtkWidget *w _U_, gpointer data _U_, guint graph_type)
540 struct segment current;
543 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
545 if (! (g = graph_new()))
549 graph_initialize_values (g);
552 g->type = graph_type;
553 if (!get_headers (cfile.current_frame, cfile.pd, ¤t)) {
554 /* currently selected packet is neither TCP over IP over Ethernet II/PPP
555 * nor TCP over IP alone - should display some
556 * kind of warning dialog */
557 simple_dialog(ESD_TYPE_WARN, NULL,
558 "Selected packet is not a TCP segment");
562 graph_segment_list_get(g);
564 /* display_text(g); */
565 graph_init_sequence(g);
568 static void create_gui (struct graph *g)
570 debug(DBS_FENTRY) puts ("create_gui()");
571 /* create_text_widget(g); */
572 control_panel_create (g);
573 create_drawing_area(g);
576 static void create_text_widget (struct graph *g)
578 GtkWidget *streamwindow, *txt_scrollw, *box;
580 debug(DBS_FENTRY) puts ("create_text_widget()");
581 streamwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
582 gtk_widget_set_name (streamwindow, "Packet chain");
583 gtk_widget_set_usize (GTK_WIDGET (streamwindow), TXT_WIDTH, TXT_HEIGHT);
584 gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
585 gtk_signal_connect (GTK_OBJECT (streamwindow), "realize",
586 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
588 box = gtk_vbox_new (FALSE, 0);
589 gtk_container_add (GTK_CONTAINER (streamwindow), box);
590 gtk_widget_show (box);
592 txt_scrollw = scrolled_window_new (NULL, NULL);
593 gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
594 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
595 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
596 gtk_widget_show (txt_scrollw);
598 g->text = gtk_text_new (NULL, NULL);
599 gtk_text_set_editable (GTK_TEXT (g->text), FALSE);
600 gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
601 gtk_widget_show (g->text);
602 gtk_widget_show (streamwindow);
605 static void display_text (struct graph *g)
609 double first_time, prev_time;
610 unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
613 debug(DBS_FENTRY) puts ("display_text()");
614 gdk_color_parse ("SlateGray", &color);
615 gtk_text_freeze (GTK_TEXT (g->text));
616 snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
617 "pkt num", "time", "delta first", "delta prev",
618 "seqno", "delta first", "delta prev", "data (B)");
619 gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL,
620 (const char *)line, -1);
622 first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
623 prev_time = first_time;
624 /* we have to find Initial Sequence Number for both ends of connection */
625 for (ptr=g->segments; ptr; ptr=ptr->next) {
626 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
627 isn_this = ntohl (ptr->tcphdr.seq);
631 for (ptr=g->segments; ptr; ptr=ptr->next) {
632 if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
633 isn_opposite = ntohl (ptr->tcphdr.seq);
637 seq_this_prev = isn_this;
638 seq_opposite_prev = isn_opposite;
639 for (ptr=g->segments; ptr; ptr=ptr->next) {
640 double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
641 unsigned int seq = ntohl (ptr->tcphdr.seq);
642 int seq_delta_isn, seq_delta_prev;
644 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
645 seq_delta_isn = seq - isn_this;
646 seq_delta_prev = seq - seq_this_prev;
650 seq_delta_isn = seq - isn_opposite;
651 seq_delta_prev = seq - seq_opposite_prev;
652 seq_opposite_prev = seq;
655 snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
656 ptr->num, time, time-first_time, time-prev_time,
657 seq, seq_delta_isn, seq_delta_prev,
658 ntohs (ptr->iphdr.tot_len) - 4*IHL(&(ptr->iphdr)) -
659 4*DOFF(ptr->tcphdr));
660 gtk_text_insert (GTK_TEXT (g->text), g->font, c, NULL,
661 (const char * )line, -1);
664 gtk_text_thaw (GTK_TEXT (g->text));
667 static void create_drawing_area (struct graph *g)
669 GdkColormap *colormap;
671 #define WINDOW_TITLE_LENGTH 64
672 char window_title[WINDOW_TITLE_LENGTH];
674 debug(DBS_FENTRY) puts ("create_drawing_area()");
676 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
678 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
679 "-*-*-m-*-iso8859-2");
681 g->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
682 gtk_widget_set_name (g->toplevel, "Test Graph");
683 gtk_signal_connect (GTK_OBJECT (g->toplevel), "realize",
684 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
686 /* Create the drawing area */
687 g->drawing_area = gtk_drawing_area_new ();
688 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
689 gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
690 g->wp.width + g->wp.x + RMARGIN_WIDTH,
691 g->wp.height + g->wp.y + g->x_axis->s.height);
692 gtk_widget_show (g->drawing_area);
694 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "expose_event",
695 (GtkSignalFunc )expose_event, NULL);
696 /* this has to be done later, after the widget has been shown */
698 gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
699 (GtkSignalFunc )configure_event, NULL);
701 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "motion_notify_event",
702 (GtkSignalFunc )motion_notify_event, NULL);
703 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_press_event",
704 (GtkSignalFunc )button_press_event, NULL);
705 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_release_event",
706 (GtkSignalFunc )button_release_event, NULL);
707 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "leave_notify_event",
708 (GtkSignalFunc )leave_notify_event, NULL);
709 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "enter_notify_event",
710 (GtkSignalFunc )enter_notify_event, NULL);
711 gtk_signal_connect (GTK_OBJECT (g->toplevel), "destroy",
712 (GtkSignalFunc )callback_toplevel_destroy, g);
713 /* why doesn't drawing area send key_press_signals? */
714 gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_press_event",
715 (GtkSignalFunc )key_press_event, NULL);
716 gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_release_event",
717 (GtkSignalFunc )key_release_event, NULL);
718 gtk_widget_set_events (g->toplevel,GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
720 gtk_widget_set_events (g->drawing_area, GDK_EXPOSURE_MASK
721 | GDK_LEAVE_NOTIFY_MASK
722 | GDK_ENTER_NOTIFY_MASK
723 | GDK_BUTTON_PRESS_MASK
724 | GDK_BUTTON_RELEASE_MASK
725 | GDK_POINTER_MOTION_MASK
726 | GDK_POINTER_MOTION_HINT_MASK);
729 frame = gtk_frame_new (NULL);
730 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
731 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
733 box = gtk_hbox_new (FALSE, 0);
734 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
735 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
736 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
737 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
738 gtk_widget_show (frame);
739 gtk_widget_show (box);
742 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
743 gtk_widget_show (g->toplevel);
744 snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d - Ethereal",
746 gtk_window_set_title (GTK_WINDOW (g->toplevel), window_title);
748 /* in case we didn't get what we asked for */
749 g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
750 g->wp.x - RMARGIN_WIDTH;
751 g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
752 g->wp.y - g->x_axis->s.height;
754 g->font = g->drawing_area->style->font;
755 gdk_font_ref (g->font);
757 colormap = gdk_window_get_colormap (g->drawing_area->window);
759 xor_gc = gdk_gc_new (g->drawing_area->window);
760 gdk_gc_set_function (xor_gc, GDK_XOR);
761 gdk_color_parse ("gray15", &color);
762 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
763 gdk_gc_set_foreground (xor_gc, &color);
765 g->fg_gc = gdk_gc_new (g->drawing_area->window);
766 g->bg_gc = gdk_gc_new (g->drawing_area->window);
767 gdk_color_parse ("white", &color);
768 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
769 gdk_gc_set_foreground (g->bg_gc, &color);
771 /* this is probably quite an ugly way to get rid of the first configure
773 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
774 * event which is handled during the next return to gtk_main which is
775 * probably the gdk_gc_new() call. configure handler calls
776 * graph_element_lists_make() which is not good because the graph struct is
777 * not fully set up yet - namely we're not sure about actual geometry
778 * and we don't have the GC's at all. so we just postpone installation
779 * of configure handler until we're ready to deal with it.
781 * !!! NEMÌLO BY TO BÝT NA KONCI graph_init_sequence()? !!!
784 gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
785 (GtkSignalFunc )configure_event, NULL);
787 /* puts ("exiting create_drawing_area()"); */
790 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
792 struct graph *g = (struct graph * )data;
794 if (!(g->flags & GRAPH_DESTROYED)) {
795 g->flags |= GRAPH_DESTROYED;
796 graph_destroy ((struct graph * )data);
800 static void control_panel_create (struct graph *g)
802 GtkWidget *toplevel, *notebook;
804 GtkWidget *help, *close, *button_box;
805 #define WINDOW_TITLE_LENGTH 64
806 char window_title[WINDOW_TITLE_LENGTH];
808 debug(DBS_FENTRY) puts ("control_panel_create()");
810 notebook = gtk_notebook_new ();
811 control_panel_add_zoom_page (g, notebook);
812 control_panel_add_magnify_page (g, notebook);
813 control_panel_add_origin_page (g, notebook);
814 control_panel_add_cross_page (g, notebook);
815 control_panel_add_graph_type_page (g, notebook);
817 /* bottom buttons group */
818 help = gtk_button_new_with_label ("Help");
819 close = gtk_button_new_with_label ("Close");
820 button_box = gtk_hbox_new (TRUE, 0);
821 gtk_box_pack_start (GTK_BOX (button_box), help, TRUE, TRUE, 0);
822 gtk_box_pack_start (GTK_BOX (button_box), close, TRUE, TRUE, 0);
824 toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
825 gtk_signal_connect (GTK_OBJECT (toplevel), "realize",
826 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
828 table = gtk_table_new (2, 1, FALSE);
829 gtk_container_add (GTK_CONTAINER (toplevel), table);
831 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
832 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
833 gtk_table_attach (GTK_TABLE (table), button_box, 0, 1, 1, 2,
834 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
836 gtk_signal_connect (GTK_OBJECT (close), "clicked",
837 (GtkSignalFunc )callback_close, g);
838 gtk_signal_connect (GTK_OBJECT (help), "clicked",
839 (GtkSignalFunc )callback_create_help, g);
841 /* gtk_widget_show_all (table); */
842 /* g->gui.control_panel = table; */
843 gtk_widget_show_all (toplevel);
844 snprintf (window_title, WINDOW_TITLE_LENGTH,
845 "Graph %d - Control - Ethereal", refnum);
846 gtk_window_set_title (GTK_WINDOW (toplevel), window_title);
847 g->gui.control_panel = toplevel;
850 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
852 GtkWidget *zoom_frame;
853 GtkWidget *zoom_lock_frame;
857 zoom_frame = control_panel_create_zoom_group (g);
858 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
859 zoom_lock_frame = control_panel_create_zoomlock_group (g);
860 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
861 box = gtk_vbox_new (FALSE, 0);
862 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
863 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
864 gtk_widget_show (box);
865 label = gtk_label_new ("Zoom");
866 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
869 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
871 GtkWidget *mag_frame, *label;
873 mag_frame = control_panel_create_magnify_group (g);
874 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
875 label = gtk_label_new ("Magnify");
876 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
879 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
881 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
882 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
883 GtkWidget *box, *label;
885 /* time origin box */
887 gtk_radio_button_new_with_label (NULL, "beginning of capture");
888 time_orig_conn = gtk_radio_button_new_with_label (
889 gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)),
890 "beginning of this TCP connection");
891 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
892 time_orig_box = gtk_vbox_new (TRUE, 0);
893 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
894 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
895 time_orig_frame = gtk_frame_new ("Time origin");
896 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
897 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
899 /* sequence number origin group */
901 gtk_radio_button_new_with_label (NULL, "initial sequence number");
902 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
903 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
904 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
905 seq_orig_box = gtk_vbox_new (TRUE, 0);
906 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
907 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
908 seq_orig_frame = gtk_frame_new ("Sequence number origin");
909 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
910 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
912 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
913 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
915 gtk_signal_connect (GTK_OBJECT (time_orig_conn), "toggled",
916 (GtkSignalFunc )callback_time_origin, g);
917 gtk_signal_connect (GTK_OBJECT (seq_orig_isn), "toggled",
918 (GtkSignalFunc )callback_seq_origin, g);
920 box = gtk_vbox_new (FALSE, 0);
921 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
922 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
923 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
924 gtk_widget_show (box);
925 label = gtk_label_new ("Origin");
926 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
929 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
931 GtkWidget *cross_frame, *label;
933 cross_frame = control_panel_create_cross_group (g);
934 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
935 label = gtk_label_new ("Cross");
936 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
939 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
941 GtkWidget *frame, *label;
943 frame = control_panel_create_graph_type_group (g);
944 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
945 label = gtk_label_new ("Graph type");
946 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
949 static void callback_close (GtkWidget *widget _U_, gpointer data)
951 struct graph *g = (struct graph * )data;
953 if (!(g->flags & GRAPH_DESTROYED)) {
954 g->flags |= GRAPH_DESTROYED;
955 graph_destroy ((struct graph * )data);
959 static void callback_create_help (GtkWidget *widget _U_, gpointer data)
961 struct graph *g = (struct graph * )data;
962 GtkWidget *toplevel, *box, *text, *scroll, *close;
964 toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
965 gtk_window_set_title(GTK_WINDOW(toplevel), "Help for TCP graphing");
966 gtk_widget_set_usize (toplevel, 500, 400);
967 gtk_signal_connect (GTK_OBJECT (toplevel), "realize",
968 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
970 box = gtk_vbox_new (FALSE, 0);
971 gtk_container_add (GTK_CONTAINER (toplevel), box);
972 scroll = gtk_scrolled_window_new (NULL, NULL);
973 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
974 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
975 gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
976 text = gtk_text_new (NULL, NULL);
977 gtk_text_set_editable (GTK_TEXT (text), FALSE);
978 gtk_text_set_line_wrap (GTK_TEXT (text), FALSE);
979 gtk_text_set_word_wrap (GTK_TEXT (text), FALSE);
980 gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1);
981 gtk_container_add (GTK_CONTAINER (scroll), text);
982 close = gtk_button_new_with_label ("Close");
983 gtk_box_pack_start (GTK_BOX (box), close, FALSE, FALSE, 0);
984 gtk_signal_connect (GTK_OBJECT (close), "clicked",
985 (GtkSignalFunc )callback_close_help, toplevel);
987 gtk_widget_show_all (toplevel);
990 static void callback_close_help (GtkWidget *widget _U_, gpointer data)
992 gtk_widget_destroy ((GtkWidget * )data);
995 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
997 toggle_time_origin ((struct graph * )data);
1000 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1002 toggle_seq_origin ((struct graph * )data);
1005 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1007 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1008 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1009 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1010 GtkWidget *zoom_v_step_label, *zoom_v_step;
1011 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1012 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1013 GtkWidget *zoom_h_entry, *zoom_v_entry;
1014 GtkWidget *zoom_h_label, *zoom_v_label;
1016 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1017 zoom_out = gtk_radio_button_new_with_label (
1018 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1019 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1020 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1021 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1022 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1024 zoom_separator1 = gtk_hseparator_new ();
1026 zoom_h_entry = gtk_entry_new ();
1027 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1028 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1029 zoom_h_label = gtk_label_new ("Horizontal:");
1031 zoom_v_entry = gtk_entry_new ();
1032 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1033 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1034 zoom_v_label = gtk_label_new ("Vertical:");
1036 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1037 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1039 zoom_table = gtk_table_new (2, 2, FALSE);
1040 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1041 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1042 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1043 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1044 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1045 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1046 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1047 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1049 zoom_separator2 = gtk_hseparator_new ();
1051 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
1052 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1053 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1054 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1056 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
1057 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1058 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1059 zoom_v_step_label = gtk_label_new ("Vertical step:");
1061 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1062 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1064 zoom_same_toggle = gtk_check_button_new_with_label ("Keep them the same");
1065 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1066 gtk_object_set_data (GTK_OBJECT (zoom_same_toggle), "flag",
1067 (gpointer )ZOOM_STEPS_SAME);
1068 gtk_object_set_data (GTK_OBJECT (zoom_ratio_toggle), "flag",
1069 (gpointer )ZOOM_STEPS_KEEP_RATIO);
1070 gtk_signal_connect (GTK_OBJECT (zoom_same_toggle), "clicked",
1071 (GtkSignalFunc )callback_zoom_flags, g);
1072 gtk_signal_connect (GTK_OBJECT (zoom_ratio_toggle), "clicked",
1073 (GtkSignalFunc )callback_zoom_flags, g);
1075 zoom_step_table = gtk_table_new (4, 2, FALSE);
1076 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1077 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1078 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1079 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1080 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1081 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1082 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1083 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1084 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1085 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1086 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1087 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1089 zoom_box = gtk_vbox_new (FALSE, 0);
1090 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1091 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1092 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1093 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1094 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1095 zoom_frame = gtk_frame_new ("Zoom");
1096 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1098 gtk_object_set_data (GTK_OBJECT (zoom_h_step), "direction", (gpointer )0);
1099 gtk_object_set_data (GTK_OBJECT (zoom_v_step), "direction", (gpointer )1);
1101 gtk_signal_connect (GTK_OBJECT (zoom_in), "toggled",
1102 (GtkSignalFunc )callback_zoom_inout, g);
1103 gtk_signal_connect (GTK_OBJECT (zoom_h_step), "changed",
1104 (GtkSignalFunc )callback_zoom_step, g);
1105 gtk_signal_connect (GTK_OBJECT (zoom_v_step), "changed",
1106 (GtkSignalFunc )callback_zoom_step, g);
1108 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1109 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1113 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1115 struct graph *g = (struct graph * )data;
1117 if (GTK_TOGGLE_BUTTON (toggle)->active)
1118 g->zoom.flags &= ~ZOOM_OUT;
1120 g->zoom.flags |= ZOOM_OUT;
1123 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1125 struct graph *g = (struct graph * )data;
1128 double *zoom_this, *zoom_other;
1129 GtkSpinButton *widget_this, *widget_other;
1132 direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
1133 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1136 zoom_this = &g->zoom.step_y;
1137 zoom_other = &g->zoom.step_x;
1138 widget_this = g->zoom.widget.v_step;
1139 widget_other = g->zoom.widget.h_step;
1141 zoom_this = &g->zoom.step_x;
1142 zoom_other = &g->zoom.step_y;
1143 widget_this = g->zoom.widget.h_step;
1144 widget_other = g->zoom.widget.v_step;
1147 old_this = *zoom_this;
1149 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1150 *zoom_other = value;
1151 gtk_spin_button_set_value (widget_other, *zoom_other);
1152 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1153 double old_other = *zoom_other;
1154 *zoom_other *= value / old_this;
1155 if (*zoom_other < 1.0) {
1157 *zoom_this = old_this * 1.0 / old_other;
1158 gtk_spin_button_set_value (widget_this, *zoom_this);
1159 } else if (*zoom_other > 5.0) {
1161 *zoom_this = old_this * 5.0 / old_other;
1162 gtk_spin_button_set_value (widget_this, *zoom_this);
1164 gtk_spin_button_set_value (widget_other, *zoom_other);
1168 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1170 struct graph *g = (struct graph * )data;
1171 int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
1173 if (GTK_TOGGLE_BUTTON (toggle)->active)
1174 g->zoom.flags |= flag;
1176 g->zoom.flags &= ~flag;
1179 static void update_zoom_spins (struct graph *g)
1183 snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
1184 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1185 snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
1186 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1189 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1191 GtkWidget *mag_width_label, *mag_width;
1192 GtkWidget *mag_height_label, *mag_height;
1193 GtkWidget *mag_x_label, *mag_x;
1194 GtkWidget *mag_y_label, *mag_y;
1195 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1196 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1197 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1198 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1199 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1200 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1201 GtkWidget *mag_box, *mag_frame;
1203 mag_width_label = gtk_label_new ("Width:");
1204 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1205 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1207 mag_height_label = gtk_label_new ("Height:");
1208 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1209 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1211 mag_x_label = gtk_label_new ("X:");
1212 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1213 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1215 mag_y_label = gtk_label_new ("Y:");
1216 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1217 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1219 mag_wh_table = gtk_table_new (4, 2, FALSE);
1220 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1221 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1222 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1223 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1224 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1225 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1226 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1227 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1228 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1229 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1230 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1231 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1232 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1233 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1234 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1235 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1237 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1238 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
1239 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1241 mag_v_zoom_label = gtk_label_new ("Vertical:");
1242 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
1243 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1245 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1246 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1248 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1249 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1250 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1251 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1252 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1253 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1254 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1255 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1256 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1257 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1258 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1259 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1260 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1262 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1263 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1264 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1266 mag_box = gtk_vbox_new (FALSE, 0);
1267 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1268 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1269 mag_frame = gtk_frame_new ("Magnify");
1270 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1272 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1273 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1274 gtk_object_set_data (GTK_OBJECT (mag_h_zoom), "direction", (gpointer )0);
1275 gtk_object_set_data (GTK_OBJECT (mag_v_zoom), "direction", (gpointer )1);
1276 gtk_object_set_data (GTK_OBJECT (mag_zoom_same), "flag",
1277 (gpointer )MAGZOOMS_SAME);
1278 gtk_object_set_data (GTK_OBJECT (mag_zoom_ratio), "flag",
1279 (gpointer )MAGZOOMS_SAME_RATIO);
1281 gtk_signal_connect (GTK_OBJECT (mag_width), "changed",
1282 (GtkSignalFunc )callback_mag_width, g);
1283 gtk_signal_connect (GTK_OBJECT (mag_height), "changed",
1284 (GtkSignalFunc )callback_mag_height, g);
1285 gtk_signal_connect (GTK_OBJECT (mag_x), "changed",
1286 (GtkSignalFunc )callback_mag_x, g);
1287 gtk_signal_connect (GTK_OBJECT (mag_y), "changed",
1288 (GtkSignalFunc )callback_mag_y, g);
1289 gtk_signal_connect (GTK_OBJECT (mag_h_zoom), "changed",
1290 (GtkSignalFunc )callback_mag_zoom, g);
1291 gtk_signal_connect (GTK_OBJECT (mag_v_zoom), "changed",
1292 (GtkSignalFunc )callback_mag_zoom, g);
1293 gtk_signal_connect (GTK_OBJECT (mag_zoom_same), "clicked",
1294 (GtkSignalFunc )callback_mag_flags, g);
1295 gtk_signal_connect (GTK_OBJECT (mag_zoom_ratio), "clicked",
1296 (GtkSignalFunc )callback_mag_flags, g);
1301 static void callback_mag_width (GtkWidget *spin, gpointer data)
1303 struct graph *g = (struct graph * )data;
1305 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1308 static void callback_mag_height (GtkWidget *spin, gpointer data)
1310 struct graph *g = (struct graph * )data;
1312 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1315 static void callback_mag_x (GtkWidget *spin, gpointer data)
1317 struct graph *g = (struct graph * )data;
1319 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1322 static void callback_mag_y (GtkWidget *spin, gpointer data)
1324 struct graph *g = (struct graph * )data;
1326 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1329 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1331 struct graph *g = (struct graph * )data;
1334 double *zoom_this, *zoom_other;
1335 GtkSpinButton *widget_this, *widget_other;
1338 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1339 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1340 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1343 direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
1344 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1347 zoom_this = &g->magnify.zoom.y;
1348 zoom_other = &g->magnify.zoom.x;
1349 widget_this = g->magnify.widget.v_zoom;
1350 widget_other = g->magnify.widget.h_zoom;
1352 zoom_this = &g->magnify.zoom.x;
1353 zoom_other = &g->magnify.zoom.y;
1354 widget_this = g->magnify.widget.h_zoom;
1355 widget_other = g->magnify.widget.v_zoom;
1358 old_this = *zoom_this;
1360 if (g->magnify.flags & MAGZOOMS_SAME) {
1361 *zoom_other = value;
1362 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1363 gtk_spin_button_set_value (widget_other, *zoom_other);
1364 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1365 double old_other = *zoom_other;
1366 *zoom_other *= value / old_this;
1367 if (*zoom_other < 1.0) {
1369 *zoom_this = old_this * 1.0 / old_other;
1370 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1371 gtk_spin_button_set_value (widget_this, *zoom_this);
1372 } else if (*zoom_other > 25.0) {
1374 *zoom_this = old_this * 25.0 / old_other;
1375 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1376 gtk_spin_button_set_value (widget_this, *zoom_this);
1378 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1379 gtk_spin_button_set_value (widget_other, *zoom_other);
1383 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1385 struct graph *g = (struct graph * )data;
1386 int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
1388 if (GTK_TOGGLE_BUTTON (toggle)->active)
1389 g->magnify.flags |= flag;
1391 g->magnify.flags &= ~flag;
1394 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1396 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1397 GtkWidget *zoom_lock_frame;
1399 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1400 zoom_lock_h = gtk_radio_button_new_with_label (
1401 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1403 zoom_lock_v = gtk_radio_button_new_with_label (
1404 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1406 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1407 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1408 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0);
1409 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1410 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1411 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1412 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1414 gtk_signal_connect (GTK_OBJECT (zoom_lock_h), "toggled",
1415 (GtkSignalFunc )callback_zoomlock_h, g);
1416 gtk_signal_connect (GTK_OBJECT (zoom_lock_v), "toggled",
1417 (GtkSignalFunc )callback_zoomlock_v, g);
1419 return zoom_lock_frame;
1422 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1424 struct graph *g = (struct graph * )data;
1426 if (GTK_TOGGLE_BUTTON (toggle)->active)
1427 g->zoom.flags |= ZOOM_HLOCK;
1429 g->zoom.flags &= ~ZOOM_HLOCK;
1432 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1434 struct graph *g = (struct graph * )data;
1436 if (GTK_TOGGLE_BUTTON (toggle)->active)
1437 g->zoom.flags |= ZOOM_VLOCK;
1439 g->zoom.flags &= ~ZOOM_VLOCK;
1442 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1444 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1446 label = gtk_label_new ("Crosshairs:");
1447 off = gtk_radio_button_new_with_label (NULL, "off");
1448 on = gtk_radio_button_new_with_label (
1449 gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on");
1450 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1451 box = gtk_hbox_new (FALSE, 0);
1452 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1453 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1454 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1455 vbox = gtk_vbox_new (FALSE, 0);
1456 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1457 /* frame = gtk_frame_new ("Cross:"); */
1458 frame = gtk_frame_new (NULL);
1459 gtk_container_add (GTK_CONTAINER (frame), vbox);
1461 gtk_signal_connect (GTK_OBJECT (on), "toggled",
1462 (GtkSignalFunc )callback_cross_on_off, g);
1464 g->cross.on_toggle = (GtkToggleButton * )on;
1465 g->cross.off_toggle = (GtkToggleButton * )off;
1470 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1472 struct graph *g = (struct graph * )data;
1474 if (GTK_TOGGLE_BUTTON (toggle)->active) {
1476 g->cross.draw = TRUE;
1477 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
1478 cross_draw (g, x, y);
1480 g->cross.draw = FALSE;
1485 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1487 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1488 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1489 GtkWidget *graph_frame;
1491 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1492 graph_tseqttrace = gtk_radio_button_new_with_label (
1493 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1494 "Time/Sequence (tcptrace-style)");
1495 graph_tseqstevens = gtk_radio_button_new_with_label (
1496 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1497 "Time/Sequence (Stevens'-style)");
1498 graph_rtt = gtk_radio_button_new_with_label (
1499 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1502 case GRAPH_TSEQ_STEVENS:
1503 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1505 case GRAPH_TSEQ_TCPTRACE:
1506 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1508 case GRAPH_THROUGHPUT:
1509 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1512 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1515 graph_init = gtk_check_button_new_with_label ("Init on change");
1516 graph_sep = gtk_hseparator_new ();
1517 graph_box = gtk_vbox_new (FALSE, 0);
1518 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1519 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1520 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1521 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1522 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1523 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1524 graph_frame = gtk_frame_new ("Graph type:");
1525 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1527 gtk_object_set_data (GTK_OBJECT (graph_tseqstevens), "new-graph-type",
1529 gtk_object_set_data (GTK_OBJECT (graph_tseqttrace), "new-graph-type",
1531 gtk_object_set_data (GTK_OBJECT (graph_tput), "new-graph-type",
1533 gtk_object_set_data (GTK_OBJECT (graph_rtt), "new-graph-type",
1536 gtk_signal_connect (GTK_OBJECT (graph_tseqttrace), "toggled",
1537 (GtkSignalFunc )callback_graph_type, g);
1538 gtk_signal_connect (GTK_OBJECT (graph_tseqstevens), "toggled",
1539 (GtkSignalFunc )callback_graph_type, g);
1540 gtk_signal_connect (GTK_OBJECT (graph_tput), "toggled",
1541 (GtkSignalFunc )callback_graph_type, g);
1542 gtk_signal_connect (GTK_OBJECT (graph_rtt), "toggled",
1543 (GtkSignalFunc )callback_graph_type, g);
1544 gtk_signal_connect (GTK_OBJECT (graph_init), "toggled",
1545 (GtkSignalFunc )callback_graph_init_on_typechg, g);
1550 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1552 int old_type, new_type;
1553 struct graph *g = (struct graph * )data;
1555 new_type = (int )gtk_object_get_data (GTK_OBJECT (toggle),"new-graph-type");
1557 if (!GTK_TOGGLE_BUTTON (toggle)->active)
1563 graph_element_lists_free (g);
1564 graph_element_lists_initialize (g);
1566 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1567 /* throughput graph uses differently constructed segment list so we
1568 * need to recreate it */
1569 graph_segment_list_free (g);
1570 graph_segment_list_get (g);
1573 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1574 g->geom.width = g->wp.width;
1575 g->geom.height = g->wp.height;
1576 g->geom.x = g->wp.x;
1577 g->geom.y = g->wp.y;
1579 g->x_axis->min = g->y_axis->min = 0;
1580 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1581 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1582 graph_init_sequence (g);
1585 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1587 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1590 static struct graph *graph_new (void)
1594 g = (struct graph * )calloc (1, sizeof (struct graph));
1595 graph_element_lists_initialize (g);
1597 g->x_axis = (struct axis * )calloc (1, sizeof (struct axis));
1598 g->y_axis = (struct axis * )calloc (1, sizeof (struct axis));
1600 g->x_axis->flags = 0;
1601 g->x_axis->flags |= AXIS_ORIENTATION;
1602 g->x_axis->s.x = g->x_axis->s.y = 0;
1603 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1604 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1605 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1607 g->y_axis->flags = 0;
1608 g->y_axis->flags &= ~AXIS_ORIENTATION;
1609 g->y_axis->p.x = g->y_axis->p.y = 0;
1610 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1612 g->y_axis->s.y = TITLEBAR_HEIGHT;
1613 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1618 static void graph_initialize_values (struct graph *g)
1620 g->geom.width = g->wp.width = 750;
1621 g->geom.height = g->wp.height = 550;
1622 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1623 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1625 /* g->zoom.x = g->zoom.y = 1.0; */
1626 g->zoom.step_x = g->zoom.step_y = 1.2;
1628 g->cross.draw = g->cross.erase_needed = 0;
1629 g->grab.grabbed = 0;
1630 g->magnify.active = 0;
1631 g->magnify.offset.x = g->magnify.offset.y = 0;
1632 g->magnify.width = g->magnify.height = 250;
1633 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1634 g->magnify.flags = 0;
1637 static void graph_put (struct graph *graph)
1641 for (g=graphs; g->next; g=g->next);
1647 static void graph_init_sequence (struct graph *g)
1649 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1651 graph_type_dependent_initialize (g);
1652 g->zoom.initial.x = g->zoom.x;
1653 g->zoom.initial.y = g->zoom.y;
1654 graph_element_lists_make (g);
1655 g->x_axis->s.width = g->wp.width;
1656 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1657 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1658 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1659 g->y_axis->s.height = g->wp.height;
1660 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1661 graph_pixmaps_create (g);
1662 axis_pixmaps_create (g->y_axis);
1663 axis_pixmaps_create (g->x_axis);
1664 graph_title_pixmap_create (g);
1665 graph_title_pixmap_draw (g);
1666 graph_title_pixmap_display (g);
1668 axis_display (g->y_axis);
1669 axis_display (g->x_axis);
1672 static void graph_type_dependent_initialize (struct graph *g)
1675 case GRAPH_TSEQ_STEVENS:
1676 case GRAPH_TSEQ_TCPTRACE:
1677 tseq_stevens_initialize (g);
1679 case GRAPH_THROUGHPUT:
1680 tput_initialize (g);
1690 static void graph_destroy (struct graph *g)
1693 struct graph *p=NULL;
1694 /* struct graph *tmp; */
1696 debug(DBS_FENTRY) puts ("graph_destroy()");
1698 for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next)
1702 axis_destroy (g->x_axis);
1703 axis_destroy (g->y_axis);
1704 /* gtk_widget_destroy (g->drawing_area); */
1705 gtk_widget_destroy (g->gui.control_panel);
1706 gtk_widget_destroy (g->toplevel);
1707 /* gtk_widget_destroy (g->text); */
1708 gdk_gc_unref (g->fg_gc);
1709 gdk_gc_unref (g->bg_gc);
1710 gdk_font_unref (g->font);
1711 gdk_pixmap_unref (g->pixmap[0]);
1712 gdk_pixmap_unref (g->pixmap[1]);
1716 graph_segment_list_free (g);
1717 graph_element_lists_free (g);
1719 for (tmp=graphs; tmp; tmp=tmp->next)
1720 printf ("%p next: %p\n", tmp, tmp->next);
1721 printf ("p=%p, g=%p, p->next=%p, g->next=%p\n",
1722 p, g, p ? p->next : NULL, g->next);
1730 for (tmp=graphs; tmp; tmp=tmp->next)
1731 printf ("%p next: %p\n", tmp, tmp->next);
1735 /* here we collect all the external data we will ever need */
1736 static void graph_segment_list_get (struct graph *g)
1739 union wtap_pseudo_header pseudo_header;
1740 char pd[WTAP_MAX_PACKET_SIZE];
1741 struct segment *segment=NULL, *last=NULL;
1742 struct segment current;
1746 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1747 get_headers (cfile.current_frame, cfile.pd, ¤t);
1748 if (g->type == GRAPH_THROUGHPUT)
1749 condition = COMPARE_CURR_DIR;
1751 condition = COMPARE_ANY_DIR;
1753 for (ptr=cfile.plist; ptr; ptr=ptr->next) {
1754 /* XXX - do something with "err" */
1755 wtap_seek_read (cfile.wth, ptr->file_off, &pseudo_header,
1756 pd, ptr->cap_len, &err);
1758 segment = (struct segment * )malloc (sizeof (struct segment));
1760 perror ("malloc failed");
1761 if (!get_headers (ptr, pd, segment))
1762 continue; /* not TCP over IP over Ethernet II */
1763 if (compare_headers (¤t, segment, condition)) {
1764 segment->next = NULL;
1765 segment->num = ptr->num;
1766 segment->rel_secs = ptr->rel_secs;
1767 segment->rel_usecs = ptr->rel_usecs;
1768 segment->abs_secs = ptr->abs_secs;
1769 segment->abs_usecs = ptr->abs_usecs;
1770 segment->data = ntohs (segment->iphdr.tot_len) -
1771 4*IHL(&(segment->iphdr)) - 4*DOFF(segment->tcphdr);
1773 last->next = segment;
1775 g->segments = segment;
1778 if (ptr==cfile.current_frame)
1779 g->current = segment;
1785 static int get_headers (frame_data *fd, char *pd, struct segment *hdrs)
1787 struct ether_header *e;
1788 struct ppp_header *p;
1793 * XXX - on Alpha, even fetching one-byte fields from structures
1794 * pointed to by unaligned pointers may be risky, as, unless
1795 * the BWX instructions are being used, a one-byte load is done
1796 * by loading the word containing the byte and then extracting
1799 * This means that the references to "p->ppp_type" and
1800 * "((struct iphdr *)ip)->protocol" may turn into a load of
1801 * an unaligned word.
1803 switch (fd->lnk_t) {
1805 case WTAP_ENCAP_ETHERNET:
1807 e = (struct ether_header *)pd;
1808 if (pntohs (&e->ether_type) != ETHERTYPE_IP)
1809 return FALSE; /* not IP */
1813 case WTAP_ENCAP_PPP:
1815 p = (struct ppp_header *)pd;
1816 if (p->ppp_type != PPPTYPE_IP)
1817 return FALSE; /* not IP */
1821 case WTAP_ENCAP_RAW_IP:
1827 /* Those are the only encapsulation types we handle */
1830 if (((struct iphdr *)ip)->protocol != IPPROTO_TCP) {
1831 /* printf ("transport protocol not TCP: %#1x\n", ip->protocol); */
1834 tcp = (struct tcphdr *)((guint8 *)ip + 4*IHL((struct iphdr *)ip));
1836 memcpy(&hdrs->iphdr, ip, sizeof (struct iphdr));
1837 memcpy(&hdrs->tcphdr, tcp, sizeof (struct tcphdr));
1841 static int compare_headers (struct segment *h1, struct segment *h2, int dir)
1843 if (dir == COMPARE_CURR_DIR)
1844 return h1->iphdr.saddr == h2->iphdr.saddr &&
1845 h1->iphdr.daddr == h2->iphdr.daddr &&
1846 h1->tcphdr.source == h2->tcphdr.source &&
1847 h1->tcphdr.dest == h2->tcphdr.dest;
1849 return (h1->iphdr.saddr == h2->iphdr.saddr &&
1850 h1->iphdr.daddr == h2->iphdr.daddr &&
1851 h1->tcphdr.source == h2->tcphdr.source &&
1852 h1->tcphdr.dest == h2->tcphdr.dest) ||
1853 (h1->iphdr.saddr == h2->iphdr.daddr &&
1854 h1->iphdr.daddr == h2->iphdr.saddr &&
1855 h1->tcphdr.source == h2->tcphdr.dest &&
1856 h1->tcphdr.dest == h2->tcphdr.source);
1859 static void graph_segment_list_free (struct graph *g)
1861 struct segment *segment;
1863 while (g->segments) {
1864 segment = g->segments->next;
1866 g->segments = segment;
1871 static void graph_element_lists_initialize (struct graph *g)
1873 g->elists = (struct element_list *)calloc (1, sizeof (struct element_list));
1876 static void graph_element_lists_make (struct graph *g)
1878 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1881 case GRAPH_TSEQ_STEVENS:
1882 tseq_stevens_make_elmtlist (g);
1884 case GRAPH_TSEQ_TCPTRACE:
1885 tseq_tcptrace_make_elmtlist (g);
1887 case GRAPH_THROUGHPUT:
1888 tput_make_elmtlist (g);
1891 rtt_make_elmtlist (g);
1894 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1899 static void graph_element_lists_free (struct graph *g)
1901 struct element_list *list, *next_list;
1904 for (list=g->elists; list; list=list->next)
1905 free (list->elements);
1906 while (g->elists->next) {
1907 list = g->elists->next->next;
1908 free (g->elists->next);
1909 g->elists->next = list;
1913 for (list=g->elists; list; list=next_list) {
1914 free (list->elements);
1915 next_list = list->next;
1918 g->elists = NULL; /* just to make debugging easier */
1921 static void graph_title_pixmap_create (struct graph *g)
1923 if (g->title_pixmap)
1924 gdk_pixmap_unref (g->title_pixmap);
1926 g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
1927 g->x_axis->p.width, g->wp.y, -1);
1930 static void graph_title_pixmap_draw (struct graph *g)
1934 gdk_draw_rectangle (g->title_pixmap, g->bg_gc, TRUE, 0, 0,
1935 g->x_axis->p.width, g->wp.y);
1936 for (i=0; g->title[i]; i++) {
1938 w = gdk_string_width (g->font, g->title[i]);
1939 h = gdk_string_height (g->font, g->title[i]);
1940 gdk_draw_string (g->title_pixmap, g->font, g->fg_gc,
1941 g->wp.width/2 - w/2, 20+h + i*(h+3), g->title[i]);
1945 static void graph_title_pixmap_display (struct graph *g)
1947 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
1948 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
1951 static void graph_pixmaps_create (struct graph *g)
1953 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
1956 gdk_pixmap_unref (g->pixmap[0]);
1958 gdk_pixmap_unref (g->pixmap[1]);
1960 g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
1961 g->wp.width, g->wp.height, -1);
1962 g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
1963 g->wp.width, g->wp.height, -1);
1968 static void graph_display (struct graph *g)
1970 graph_pixmap_draw (g);
1971 graph_pixmaps_switch (g);
1972 graph_pixmap_display (g);
1975 static void graph_pixmap_display (struct graph *g)
1977 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
1978 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
1979 g->wp.width, g->wp.height);
1982 static void graph_pixmaps_switch (struct graph *g)
1984 g->displayed = 1 ^ g->displayed;
1987 static void graph_pixmap_draw (struct graph *g)
1989 struct element_list *list;
1993 debug(DBS_FENTRY) puts ("graph_display()");
1994 not_disp = 1 ^ g->displayed;
1996 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
1997 0, 0, g->wp.width, g->wp.height);
1999 for (list=g->elists; list; list=list->next)
2000 for (e=list->elements; e->type != ELMT_NONE; e++) {
2005 draw_element_line (g, e);
2008 draw_element_arc (g, e);
2016 static void draw_element_line (struct graph *g, struct element *e)
2020 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2021 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2022 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2023 x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2024 x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2025 y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2026 y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2037 if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
2038 (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
2039 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2043 if (x2 > g->wp.width-1)
2047 if (y2 > g->wp.height-1)
2048 y2 = g->wp.height-1;
2051 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
2052 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
2055 static void draw_element_arc (struct graph *g, struct element *e)
2059 x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2060 x2 = e->p.arc.dim.width;
2061 y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2062 y2 = e->p.arc.dim.height;
2063 if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
2065 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
2066 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
2067 y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
2070 static void axis_pixmaps_create (struct axis *axis)
2072 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2073 if (axis->pixmap[0])
2074 gdk_pixmap_unref (axis->pixmap[0]);
2075 if (axis->pixmap[1])
2076 gdk_pixmap_unref (axis->pixmap[1]);
2078 axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
2079 axis->p.width, axis->p.height, -1);
2080 axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
2081 axis->p.width, axis->p.height, -1);
2083 axis->displayed = 0;
2086 static void axis_destroy (struct axis *axis)
2088 gdk_pixmap_unref (axis->pixmap[0]);
2089 gdk_pixmap_unref (axis->pixmap[1]);
2093 static void axis_display (struct axis *axis)
2095 if (axis->flags & AXIS_ORIENTATION)
2096 h_axis_pixmap_draw (axis);
2098 v_axis_pixmap_draw (axis);
2099 axis_pixmaps_switch (axis);
2100 axis_pixmap_display (axis);
2103 static void v_axis_pixmap_draw (struct axis *axis)
2105 struct graph *g = axis->g;
2108 int not_disp, rdigits, offset, imin, imax;
2109 double bottom, top, j, fl, corr;
2111 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2112 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2113 (double )g->geom.height * g->bounds.height;
2114 bottom += axis->min;
2115 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2116 (double )g->geom.height * g->bounds.height;
2118 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2120 j = axis->major - floor (axis->major);
2121 for (rdigits=0; rdigits<=6; rdigits++) {
2128 not_disp = 1 ^ axis->displayed;
2129 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2130 axis->p.width, axis->p.height);
2132 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2133 (axis->p.height-axis->s.height)/2.0, axis->s.width - 1,
2136 offset = g->wp.y + (-g->geom.y);
2137 fl = floor (axis->min / axis->major) * axis->major;
2138 corr = rint ((axis->min - fl) * g->zoom.y);
2141 major_tick = axis->major * g->zoom.y;
2142 imin = (g->geom.height - offset + corr - g->wp.height) / major_tick + 1;
2143 imax = (g->geom.height - offset + corr) / major_tick;
2144 for (i=imin; i <= imax; i++) {
2147 int y = g->geom.height-1 - (int )rint (i * major_tick) -
2148 offset + corr + axis->s.y;
2150 debug(DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->major + fl, y);
2151 if (y < 0 || y > axis->p.height)
2153 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2154 axis->s.width - 15, y, axis->s.width - 1, y);
2155 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2156 w = gdk_string_width (g->font, desc);
2157 h = gdk_string_height (g->font, desc);
2158 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2159 axis->s.width-15-4-w, y + h/2, desc);
2163 double minor_tick = axis->minor * g->zoom.y;
2164 imin = (g->geom.height - offset + corr - g->wp.height)/minor_tick + 1;
2165 imax = (g->geom.height - offset + corr) / minor_tick;
2166 for (i=imin; i <= imax; i++) {
2167 int y = g->geom.height-1 - (int )rint (i*minor_tick) -
2168 offset + corr + axis->s.y;
2170 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2171 if (y > 0 && y < axis->p.height)
2172 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2173 axis->s.width - 8, y, axis->s.width - 1, y);
2176 for (i=0; axis->label[i]; i++) {
2178 w = gdk_string_width (g->font, axis->label[i]);
2179 h = gdk_string_height (g->font, axis->label[i]);
2180 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2181 (axis->p.width - w)/2 , TITLEBAR_HEIGHT-15 - i*(h+3),
2186 static void h_axis_pixmap_draw (struct axis *axis)
2188 struct graph *g = axis->g;
2190 double major_tick, minor_tick;
2191 int not_disp, rdigits, offset, imin, imax;
2192 double left, right, j, fl, corr;
2194 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2195 left = (g->wp.x-g->geom.x) /
2196 (double )g->geom.width * g->bounds.width;
2198 right = (g->wp.x-g->geom.x+g->wp.width) /
2199 (double )g->geom.width * g->bounds.width;
2201 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2203 j = axis->major - floor (axis->major);
2204 for (rdigits=0; rdigits<=6; rdigits++) {
2211 not_disp = 1 ^ axis->displayed;
2212 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2213 axis->p.width, axis->p.height);
2215 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2216 axis->s.width + (axis->p.width-axis->s.width)/2.0, 0);
2217 offset = g->wp.x - g->geom.x;
2219 fl = floor (axis->min / axis->major) * axis->major;
2220 corr = rint ((axis->min - fl) * g->zoom.x);
2223 major_tick = axis->major*g->zoom.x;
2224 imin = (offset + corr) / major_tick + 1;
2225 imax = (offset + corr + axis->s.width) / major_tick;
2226 for (i=imin; i <= imax; i++) {
2229 int x = (int )rint (i * major_tick) - offset - corr;
2231 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2232 if (x < 0 || x > axis->s.width)
2234 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2235 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2236 w = gdk_string_width (g->font, desc);
2237 h = gdk_string_height (g->font, desc);
2238 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2239 x - w/2, 15+h+4, desc);
2241 if (axis->minor > 0) {
2243 minor_tick = axis->minor*g->zoom.x;
2244 imin = (offset + corr) / minor_tick + 1;
2245 imax = (offset + corr + g->wp.width) / minor_tick;
2246 for (i=imin; i <= imax; i++) {
2247 int x = (int )rint (i * minor_tick) - offset - corr;
2248 if (x > 0 && x < axis->s.width)
2249 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2252 for (i=0; axis->label[i]; i++) {
2254 w = gdk_string_width (g->font, axis->label[i]);
2255 h = gdk_string_height (g->font, axis->label[i]);
2256 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2257 axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
2262 static void axis_pixmaps_switch (struct axis *axis)
2264 axis->displayed = 1 ^ axis->displayed;
2267 static void axis_pixmap_display (struct axis *axis)
2269 gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
2270 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2271 axis->p.width, axis->p.height);
2274 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2276 int i, j, ii, jj, ms;
2277 double zoom, x, steps[3]={ 0.1, 0.5 };
2278 int dim, check_needed, diminished;
2279 double majthresh[2]={2.0, 3.0};
2281 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2282 debug(DBS_AXES_TICKS)
2283 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2285 zoom = axis_zoom_get (axis, dir);
2287 for (i=-9; i<=12; i++) {
2288 if (x / pow (10, i) < 1)
2292 ms = (int )(x / pow (10, i));
2302 axis->major = steps[j] * pow (10, i);
2304 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2305 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2307 /* let's compute minor ticks */
2310 axis_ticks_down (&ii, &jj);
2311 axis->minor = steps[jj] * pow (10, ii);
2312 /* we don't want minors if they would be less than 10 pixels apart */
2313 if (axis->minor*zoom < 10) {
2314 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2315 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2319 check_needed = TRUE;
2321 while (check_needed) {
2322 check_needed = FALSE;
2323 dim = get_label_dim (axis, dir, xmax);
2324 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2325 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2326 axis->major, axis->minor, axis->major*zoom/dim,
2327 axis->minor*zoom/dim);
2329 /* corrections: if majors are less than majthresh[dir] times label
2330 * dimension apart, we need to use bigger ones */
2331 if (axis->major*zoom / dim < majthresh[dir]) {
2332 axis_ticks_up (&ii, &jj);
2333 axis->minor = axis->major;
2334 axis_ticks_up (&i, &j);
2335 axis->major = steps[j] * pow (10, i);
2336 check_needed = TRUE;
2337 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2340 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2341 * we could promote them to majors as well */
2342 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2343 axis_ticks_down (&i, &j);
2344 axis->major = axis->minor;
2345 axis_ticks_down (&ii, &jj);
2346 axis->minor = steps[jj] * pow (10, ii);
2347 check_needed = TRUE;
2350 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2353 if (axis->minor*zoom < 10) {
2354 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2355 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2361 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2362 "axis->minor == %.1f\n", axis->major, axis->minor);
2365 static void axis_ticks_up (int *i, int *j)
2374 static void axis_ticks_down (int *i, int *j)
2383 static int get_label_dim (struct axis *axis, int dir, double label)
2389 /* First, let's compute how many digits to the right of radix
2390 * we need to print */
2391 y = axis->major - floor (axis->major);
2392 for (rdigits=0; rdigits<=6; rdigits++) {
2398 snprintf (str, 32, "%.*f", rdigits, label);
2400 case AXIS_HORIZONTAL:
2401 dim = gdk_string_width (axis->g->font, str);
2404 dim = gdk_string_height (axis->g->font, str);
2407 puts ("initialize axis: an axis must be either horizontal or vertical");
2414 static double axis_zoom_get (struct axis *axis, int dir)
2417 case AXIS_HORIZONTAL:
2418 return axis->g->zoom.x;
2421 return axis->g->zoom.y;
2429 static void graph_select_segment (struct graph *g, int x, int y)
2431 struct element_list *list;
2434 debug(DBS_FENTRY) puts ("graph_select_segment()");
2437 y = g->geom.height-1 - (y - g->geom.y);
2439 for (list=g->elists; list; list=list->next)
2440 for (e=list->elements; e->type != ELMT_NONE; e++) {
2445 if (line_detect_collision (e, x, y)) {
2446 int row = e->parent->num - 1;
2447 update_packet_list (row);
2451 if (arc_detect_collision (e, x, y)) {
2452 int row = e->parent->num - 1;
2453 update_packet_list (row);
2462 static int line_detect_collision (struct element *e, int x, int y)
2466 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2467 x1 = (int )rint (e->p.line.dim.x1);
2468 x2 = (int )rint (e->p.line.dim.x2);
2470 x1 = (int )rint (e->p.line.dim.x2);
2471 x2 = (int )rint (e->p.line.dim.x1);
2473 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2474 y1 = (int )rint (e->p.line.dim.y1);
2475 y2 = (int )rint (e->p.line.dim.y2);
2477 y1 = (int )rint (e->p.line.dim.y2);
2478 y2 = (int )rint (e->p.line.dim.y1);
2481 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2483 if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
2489 static int arc_detect_collision (struct element *e, int x, int y)
2493 x1 = (int )rint (e->p.arc.dim.x);
2494 x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2495 y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2496 y2 = (int )rint (e->p.arc.dim.y);
2498 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2500 if (x1<=x && x<=x2 && y1<=y && y<=y2)
2506 static void update_packet_list (int row)
2508 select_packet (&cfile, row);
2509 if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) !=
2510 GTK_VISIBILITY_FULL)
2511 gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.5, 0.5);
2512 GTK_CLIST(packet_list)->focus_row = row;
2513 gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
2516 static void cross_xor (struct graph *g, int x, int y)
2518 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2519 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2520 gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
2521 y, g->wp.x + g->wp.width, y);
2522 gdk_draw_line (g->drawing_area->window, xor_gc, x,
2523 g->wp.y, x, g->wp.y + g->wp.height);
2527 static void cross_draw (struct graph *g, int x, int y)
2529 cross_xor (g, x, y);
2532 g->cross.erase_needed = 1;
2535 static void cross_erase (struct graph *g)
2537 cross_xor (g, g->cross.x, g->cross.y);
2538 g->cross.erase_needed = 0;
2541 static void magnify_create (struct graph *g, int x, int y)
2544 struct element_list *list, *new_list;
2545 struct ipoint pos, offsetpos;
2548 mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph));
2549 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2551 mg->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
2552 gtk_signal_connect (GTK_OBJECT (mg->toplevel), "realize",
2553 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
2554 mg->drawing_area = mg->toplevel;
2555 gtk_widget_set_usize (mg->toplevel, g->magnify.width, g->magnify.height);
2556 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2557 /* | GDK_ENTER_NOTIFY_MASK */
2558 /* | GDK_ALL_EVENTS_MASK */
2563 mg->wp.width = g->magnify.width;
2564 mg->wp.height = g->magnify.height;
2565 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2566 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2567 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2568 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2570 /* in order to keep original element lists intact we need our own */
2571 graph_element_lists_initialize (mg);
2572 list = g->elists->next;
2573 new_list = mg->elists;
2574 for ( ; list; list=list->next) {
2576 (struct element_list * )malloc (sizeof (struct element_list));
2577 new_list = new_list->next;
2578 new_list->next = NULL;
2579 new_list->elements = NULL;
2581 graph_element_lists_make (mg);
2583 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2584 g->magnify.x = pos.x + x - g->magnify.width/2;
2585 g->magnify.y = pos.y + y - g->magnify.height/2;
2586 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2587 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2588 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2589 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2590 gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
2591 magnify_get_geom (g, x, y);
2593 gtk_widget_show (mg->drawing_area);
2595 /* we need to wait for the first expose event before we start drawing */
2596 while (!gdk_events_pending ());
2598 e = gdk_event_get ();
2600 if (e->any.type == GDK_EXPOSE) {
2608 mg->pixmap[0] = mg->pixmap[1] = NULL;
2609 graph_pixmaps_create (mg);
2611 g->magnify.active = 1;
2614 static void magnify_move (struct graph *g, int x, int y)
2616 struct ipoint pos, offsetpos;
2618 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2619 g->magnify.x = pos.x + x - g->magnify.width/2;
2620 g->magnify.y = pos.y + y - g->magnify.height/2;
2621 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2622 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2623 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2624 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2625 magnify_get_geom (g, x, y);
2626 gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
2631 static void magnify_destroy (struct graph *g)
2633 struct element_list *list;
2634 struct graph *mg = g->magnify.g;
2636 gtk_widget_destroy (GTK_WIDGET (mg->drawing_area));
2637 gdk_pixmap_unref (mg->pixmap[0]);
2638 gdk_pixmap_unref (mg->pixmap[1]);
2639 for (list=mg->elists; list; list=list->next)
2640 free (list->elements);
2641 while (mg->elists->next) {
2642 list = mg->elists->next->next;
2643 free (mg->elists->next);
2644 mg->elists->next = list;
2646 free (g->magnify.g);
2647 g->magnify.active = 0;
2650 static void magnify_get_geom (struct graph *g, int x, int y)
2654 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
2656 g->magnify.g->geom.x = g->geom.x;
2657 g->magnify.g->geom.y = g->geom.y;
2659 g->magnify.g->geom.x -=
2660 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2661 ((x-g->geom.x)/(double )g->geom.width));
2662 g->magnify.g->geom.y -=
2663 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2664 ((y-g->geom.y)/(double )g->geom.height));
2666 /* we have coords of origin of graph relative to origin of g->toplevel.
2667 * now we need them to relate to origin of magnify window */
2668 g->magnify.g->geom.x -= (g->magnify.x - posx);
2669 g->magnify.g->geom.y -= (g->magnify.y - posy);
2672 static void magnify_draw (struct graph *g)
2674 int not_disp = 1 ^ g->magnify.g->displayed;
2676 graph_pixmap_draw (g->magnify.g);
2677 /* graph pixmap is almost ready, just add border */
2678 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2679 g->magnify.width - 1, 0);
2680 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2681 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2682 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2683 0, g->magnify.height - 1);
2684 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2685 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2687 graph_pixmaps_switch (g->magnify.g);
2688 graph_pixmap_display (g->magnify.g);
2692 static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
2698 int cur_g_width, cur_g_height;
2699 int cur_wp_width, cur_wp_height;
2701 debug(DBS_FENTRY) puts ("configure_event()");
2703 for (g=graphs; g; g=g->next)
2704 if (g->drawing_area == widget)
2707 cur_wp_width = g->wp.width;
2708 cur_wp_height = g->wp.height;
2709 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2710 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2711 g->x_axis->s.width = g->wp.width;
2712 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2713 g->y_axis->p.height = g->wp.height + g->wp.y;
2714 g->y_axis->s.height = g->wp.height;
2715 g->x_axis->p.y = g->y_axis->p.height;
2716 zoom.x = (double )g->wp.width / cur_wp_width;
2717 zoom.y = (double )g->wp.height / cur_wp_height;
2718 cur_g_width = g->geom.width;
2719 cur_g_height = g->geom.height;
2720 g->geom.width = (int )rint (g->geom.width * zoom.x);
2721 g->geom.height = (int )rint (g->geom.height * zoom.y);
2722 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2723 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2724 /* g->zoom.initial.x = g->zoom.x; */
2725 /* g->zoom.initial.y = g->zoom.y; */
2727 g->geom.x = g->wp.x - (double )g->geom.width/cur_g_width *
2728 (g->wp.x - g->geom.x);
2729 g->geom.y = g->wp.y - (double )g->geom.height/cur_g_height *
2730 (g->wp.y - g->geom.y);
2732 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2733 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2734 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2735 g->zoom.x, g->zoom.y);
2738 update_zoom_spins (g);
2739 graph_element_lists_make (g);
2740 graph_pixmaps_create (g);
2741 graph_title_pixmap_create (g);
2742 axis_pixmaps_create (g->y_axis);
2743 axis_pixmaps_create (g->x_axis);
2744 /* we don't do actual drawing here; we leave it to expose handler */
2745 graph_pixmap_draw (g);
2746 graph_pixmaps_switch (g);
2747 graph_title_pixmap_draw (g);
2748 h_axis_pixmap_draw (g->x_axis);
2749 axis_pixmaps_switch (g->x_axis);
2750 v_axis_pixmap_draw (g->y_axis);
2751 axis_pixmaps_switch (g->y_axis);
2755 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
2759 debug(DBS_FENTRY) puts ("expose_event()");
2764 for (g=graphs; g; g=g->next)
2765 if (g->drawing_area == widget)
2768 /* lower left corner */
2769 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
2770 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2772 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
2773 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2775 graph_pixmap_display (g);
2776 graph_title_pixmap_display (g);
2777 axis_pixmap_display (g->x_axis);
2778 axis_pixmap_display (g->y_axis);
2783 static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
2787 debug(DBS_FENTRY) puts ("button_press_event()");
2789 for (g=graphs; g; g=g->next)
2790 if (g->drawing_area == widget)
2793 if (event->button == 3) {
2794 if (event->state & GDK_CONTROL_MASK)
2795 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
2797 g->grab.x = (int )rint (event->x) - g->geom.x;
2798 g->grab.y = (int )rint (event->y) - g->geom.y;
2799 g->grab.grabbed = TRUE;
2802 /* Windows mouse control: */
2803 /* [<ctrl>-left] - select packet */
2804 /* [left] - zoom in */
2805 /* [<shift>-left] - zoom out */
2806 } else if (event->button == 1) {
2807 if (event->state & GDK_CONTROL_MASK) {
2808 graph_select_segment (g, (int)event->x, (int)event->y);
2811 } else if (event->button == 2) {
2813 int cur_width = g->geom.width, cur_height = g->geom.height;
2814 struct { double x, y; } factor;
2816 if (g->zoom.flags & ZOOM_OUT) {
2817 if (g->zoom.flags & ZOOM_HLOCK)
2820 factor.x = 1 / g->zoom.step_x;
2821 if (g->zoom.flags & ZOOM_VLOCK)
2824 factor.y = 1 / g->zoom.step_y;
2826 if (g->zoom.flags & ZOOM_HLOCK)
2829 factor.x = g->zoom.step_x;
2830 if (g->zoom.flags & ZOOM_VLOCK)
2833 factor.y = g->zoom.step_y;
2836 g->geom.width = (int )rint (g->geom.width * factor.x);
2837 g->geom.height = (int )rint (g->geom.height * factor.y);
2838 if (g->geom.width < g->wp.width)
2839 g->geom.width = g->wp.width;
2840 if (g->geom.height < g->wp.height)
2841 g->geom.height = g->wp.height;
2842 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2843 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2845 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2846 ((event->x-g->geom.x)/(double )cur_width));
2847 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2848 ((event->y-g->geom.y)/(double )cur_height));
2850 if (g->geom.x > g->wp.x)
2851 g->geom.x = g->wp.x;
2852 if (g->geom.y > g->wp.y)
2853 g->geom.y = g->wp.y;
2854 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2855 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2856 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2857 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2859 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2860 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2861 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2862 g->wp.height, g->zoom.x, g->zoom.y);
2864 graph_element_lists_make (g);
2866 axis_display (g->y_axis);
2867 axis_display (g->x_axis);
2868 update_zoom_spins (g);
2870 cross_draw (g, event->x, event->y);
2872 } else if (event->button == 1) {
2873 graph_select_segment (g, (int )event->x, (int )event->y);
2881 static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
2885 GdkModifierType state;
2887 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
2889 for (g=graphs; g; g=g->next)
2890 if (g->drawing_area == widget)
2894 gdk_window_get_pointer (event->window, &x, &y, &state);
2898 state = event->state;
2901 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
2902 * is pressed while pointer is in motion, we will receive one more motion
2903 * notify *before* we get the button press. This last motion notify works
2904 * with stale grab coordinates */
2905 if (state & GDK_BUTTON3_MASK) {
2906 if (g->grab.grabbed) {
2907 g->geom.x = x-g->grab.x;
2908 g->geom.y = y-g->grab.y;
2910 if (g->geom.x > g->wp.x)
2911 g->geom.x = g->wp.x;
2912 if (g->geom.y > g->wp.y)
2913 g->geom.y = g->wp.y;
2914 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2915 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2916 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2917 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2919 axis_display (g->y_axis);
2920 axis_display (g->x_axis);
2922 cross_draw (g, x, y);
2923 } else if (g->magnify.active)
2924 magnify_move (g, x, y);
2925 } else if (state & GDK_BUTTON1_MASK) {
2926 graph_select_segment (g, x, y);
2927 if (g->cross.erase_needed)
2930 cross_draw (g, x, y);
2932 if (g->cross.erase_needed)
2935 cross_draw (g, x, y);
2941 static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
2945 debug(DBS_FENTRY) puts ("button_release_event()");
2947 for (g=graphs; g; g=g->next)
2948 if (g->drawing_area == widget)
2951 if (event->button == 3)
2952 g->grab.grabbed = FALSE;
2954 if (g->magnify.active)
2955 magnify_destroy (g);
2959 static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
2963 debug(DBS_FENTRY) puts ("key_press_event()");
2965 for (g=graphs; g; g=g->next)
2966 if (g->toplevel == widget)
2969 if (event->keyval == 32 /*space*/) {
2972 if (g->cross.draw) {
2974 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
2976 } else if (g->cross.erase_needed) {
2980 /* toggle buttons emit their "toggled" signals so don't bother doing
2981 * any real work here, it will be done in signal handlers */
2983 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
2985 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
2986 } else if (event->keyval == 't')
2987 toggle_time_origin (g);
2988 else if (event->keyval == 's')
2989 toggle_seq_origin (g);
2990 else if (event->keyval == GDK_Shift_L) {
2991 /* g->zoom.flags |= ZOOM_OUT; */
2992 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
2997 static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
3001 debug(DBS_FENTRY) puts ("key_release_event()");
3003 for (g=graphs; g; g=g->next)
3004 if (g->toplevel == widget)
3007 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3008 /* g->zoom.flags &= ~ZOOM_OUT; */
3009 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3014 static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3018 for (g=graphs; g; g=g->next)
3019 if (g->drawing_area == widget)
3022 if (g->cross.erase_needed)
3028 static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3032 for (g=graphs; g; g=g->next)
3033 if (g->drawing_area == widget)
3036 /* graph_pixmap_display (g); */
3037 if (g->cross.draw) {
3039 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3040 cross_draw (g, x, y);
3045 static void toggle_time_origin (struct graph *g)
3048 case GRAPH_TSEQ_STEVENS:
3049 tseq_stevens_toggle_time_origin (g);
3051 case GRAPH_TSEQ_TCPTRACE:
3052 tseq_tcptrace_toggle_time_origin (g);
3054 case GRAPH_THROUGHPUT:
3055 tput_toggle_time_origin (g);
3060 axis_display (g->x_axis);
3063 static void toggle_seq_origin (struct graph *g)
3066 case GRAPH_TSEQ_STEVENS:
3067 tseq_stevens_toggle_seq_origin (g);
3068 axis_display (g->y_axis);
3070 case GRAPH_TSEQ_TCPTRACE:
3071 tseq_tcptrace_toggle_seq_origin (g);
3072 axis_display (g->y_axis);
3075 rtt_toggle_seq_origin (g);
3076 axis_display (g->x_axis);
3083 static int get_num_dsegs (struct graph *g)
3086 struct segment *tmp;
3088 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3089 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3096 static int get_num_acks (struct graph *g)
3099 struct segment *tmp;
3101 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3102 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3110 * Stevens-style time-sequence grapH
3113 static void tseq_stevens_read_config (struct graph *g)
3115 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3117 g->s.tseq_stevens.seq_width = 4;
3118 g->s.tseq_stevens.seq_height = 4;
3119 g->s.tseq_stevens.flags = 0;
3121 g->title = (char ** )malloc (2 * sizeof (char *));
3122 g->title[0] = "Time/Sequence Graph";
3124 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3125 g->y_axis->label[0] = "number[B]";
3126 g->y_axis->label[1] = "Sequence";
3127 g->y_axis->label[2] = NULL;
3128 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3129 g->x_axis->label[0] = "Time[s]";
3130 g->x_axis->label[1] = NULL;
3133 static void tseq_stevens_initialize (struct graph *g)
3135 debug(DBS_FENTRY) puts ("tseq_stevens_initialize()");
3136 tseq_stevens_get_bounds (g);
3142 case GRAPH_TSEQ_STEVENS:
3143 tseq_stevens_read_config(g);
3145 case GRAPH_TSEQ_TCPTRACE:
3146 tseq_tcptrace_read_config(g);
3151 static void tseq_stevens_get_bounds (struct graph *g)
3153 struct segment *tmp, *last, *first;
3154 double t0, tmax, y0, ymax;
3156 for (first=g->segments; first->next; first=first->next) {
3157 if (compare_headers (g->current, first, COMPARE_CURR_DIR))
3162 for (tmp=g->segments; tmp; tmp=tmp->next) {
3163 unsigned int highest_byte_num;
3165 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3166 highest_byte_num = ntohl (tmp->tcphdr.seq) + tmp->data;
3168 highest_byte_num = ntohl (tmp->tcphdr.ack_seq);
3169 if (highest_byte_num > ymax)
3170 ymax = highest_byte_num;
3173 puts ("tseq_stevens_get_bounds: segment list corrupted!");
3177 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3178 tmax = last->rel_secs + last->rel_usecs / 1000000.0;
3179 y0 = ntohl (first->tcphdr.seq);
3183 g->bounds.width = tmax - t0;
3184 g->bounds.height = ymax - y0;
3185 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3186 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3189 static void tseq_stevens_make_elmtlist (struct graph *g)
3191 struct segment *tmp;
3192 struct element *elements, *e;
3193 double x0 = g->bounds.x0, y0 = g->bounds.y0;
3195 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3196 if (g->elists->elements == NULL) {
3197 int n = 1 + get_num_dsegs (g);
3198 e = elements = (struct element * )malloc (n*sizeof (struct element));
3200 e = elements = g->elists->elements;
3202 for (tmp=g->segments; tmp; tmp=tmp->next) {
3205 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3208 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
3209 seqno = g->zoom.y * (ntohl (tmp->tcphdr.seq) - y0);
3214 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3215 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3216 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3217 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3218 e->p.arc.filled = TRUE;
3219 e->p.arc.angle1 = 0;
3220 e->p.arc.angle2 = 23040;
3223 e->type = ELMT_NONE;
3224 g->elists->elements = elements;
3227 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3229 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3231 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3232 g->y_axis->min = g->bounds.y0;
3233 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3237 static void tseq_stevens_toggle_time_origin (struct graph *g)
3239 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3241 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3242 g->x_axis->min = g->bounds.x0;
3243 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3248 * tcptrace-style time-sequence graph
3251 static void tseq_tcptrace_read_config (struct graph *g)
3253 GdkColormap *colormap;
3256 g->s.tseq_tcptrace.flags = 0;
3257 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
3258 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
3259 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
3260 colormap = gdk_window_get_colormap (g->drawing_area->window);
3261 gdk_color_parse ("black", &color);
3262 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3263 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3264 gdk_color_parse ("LightSlateGray", &color);
3265 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3266 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3267 gdk_color_parse ("LightGray", &color);
3268 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3269 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3271 g->elists->next = (struct element_list * )
3272 malloc (sizeof (struct element_list));
3273 g->elists->next->next = NULL;
3274 g->elists->next->elements = NULL;
3276 g->title = (char ** )malloc (2 * sizeof (char *));
3277 g->title[0] = "Time/Sequence Graph";
3279 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3280 g->y_axis->label[0] = "number[B]";
3281 g->y_axis->label[1] = "Sequence";
3282 g->y_axis->label[2] = NULL;
3283 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3284 g->x_axis->label[0] = "Time[s]";
3285 g->x_axis->label[1] = NULL;
3288 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3290 struct segment *tmp;
3291 struct element *elements0, *e0; /* list of elmts with prio 0 */
3292 struct element *elements1, *e1; /* list of elmts with prio 1 */
3294 double p_t; /* ackno, window and time of previous segment */
3295 double p_ackno, p_win;
3298 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3300 if (g->elists->elements == NULL) {
3301 int n = 1 + 4*get_num_acks(g);
3302 e0 = elements0 = (struct element * )malloc (n*sizeof (struct element));
3304 e0 = elements0 = g->elists->elements;
3306 if (g->elists->next->elements == NULL ) {
3307 int n = 1 + 3*get_num_dsegs(g);
3308 e1 = elements1 = (struct element * )malloc (n*sizeof (struct element));
3310 e1 = elements1 = g->elists->next->elements;
3314 /* initialize "previous" values */
3315 for (tmp=g->segments; tmp; tmp=tmp->next)
3316 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3319 p_ackno = (unsigned int )(g->zoom.y * (ntohl (tmp->tcphdr.ack_seq) - y0));
3322 p_win = g->zoom.y * ntohs (tmp->tcphdr.window);
3323 p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0;
3324 for (tmp=g->segments; tmp; tmp=tmp->next) {
3325 double secs, seqno, data;
3328 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3331 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3332 /* forward direction -> we need seqno and amount of data */
3335 seqno = ntohl (tmp->tcphdr.seq);
3336 if (TCP_SYN (tmp->tcphdr))
3341 y1 = g->zoom.y * (seqno - y0);
3342 y2 = g->zoom.y * (seqno - y0 + data);
3343 e1->type = ELMT_LINE;
3345 e1->gc = g->s.tseq_tcptrace.gc_seq;
3346 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3347 e1->p.line.dim.y1 = y1;
3348 e1->p.line.dim.y2 = y2;
3350 e1->type = ELMT_LINE;
3352 e1->gc = g->s.tseq_tcptrace.gc_seq;
3353 e1->p.line.dim.x1 = x - 1;
3354 e1->p.line.dim.x2 = x + 1;
3355 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
3357 e1->type = ELMT_LINE;
3359 e1->gc = g->s.tseq_tcptrace.gc_seq;
3360 e1->p.line.dim.x1 = x + 1;
3361 e1->p.line.dim.x2 = x - 1;
3362 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
3366 if (TCP_SYN (tmp->tcphdr) && ! TCP_ACK (tmp->tcphdr))
3367 /* SYN's have ACK==0 and are useless here */
3369 /* backward direction -> we need ackno and window */
3370 ackno = (ntohl (tmp->tcphdr.ack_seq) - y0) * g->zoom.y;
3371 win = ntohs (tmp->tcphdr.window) * g->zoom.y;
3374 e0->type = ELMT_LINE;
3376 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3377 e0->p.line.dim.x1 = p_t;
3378 e0->p.line.dim.y1 = p_ackno;
3379 e0->p.line.dim.x2 = x;
3380 e0->p.line.dim.y2 = p_ackno;
3382 e0->type = ELMT_LINE;
3384 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3385 e0->p.line.dim.x1 = x;
3386 e0->p.line.dim.y1 = p_ackno;
3387 e0->p.line.dim.x2 = x;
3388 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3391 e0->type = ELMT_LINE;
3393 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3394 e0->p.line.dim.x1 = p_t;
3395 e0->p.line.dim.y1 = p_win + p_ackno;
3396 e0->p.line.dim.x2 = x;
3397 e0->p.line.dim.y2 = p_win + p_ackno;
3399 e0->type = ELMT_LINE;
3401 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3402 e0->p.line.dim.x1 = x;
3403 e0->p.line.dim.y1 = p_win + p_ackno;
3404 e0->p.line.dim.x2 = x;
3405 e0->p.line.dim.y2 = win + ackno;
3413 e0->type = ELMT_NONE;
3414 e1->type = ELMT_NONE;
3415 g->elists->elements = elements0;
3416 g->elists->next->elements = elements1;
3419 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3421 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3423 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3424 g->y_axis->min = g->bounds.y0;
3425 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3429 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3431 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3433 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3434 g->x_axis->min = g->bounds.x0;
3435 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3443 static void tput_make_elmtlist (struct graph *g)
3445 struct segment *tmp, *oldest;
3446 struct element *elements, *e;
3450 if (g->elists->elements == NULL) {
3451 int n = 1 + get_num_dsegs (g);
3452 e = elements = (struct element * )malloc (n*sizeof (struct element));
3454 e = elements = g->elists->elements;
3456 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3457 double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3458 dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3459 if (i>g->s.tput.nsegs) {
3460 sum -= oldest->data;
3461 oldest=oldest->next;
3465 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3470 e->p.arc.dim.width = g->s.tput.width;
3471 e->p.arc.dim.height = g->s.tput.height;
3472 e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
3473 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3474 e->p.arc.filled = TRUE;
3475 e->p.arc.angle1 = 0;
3476 e->p.arc.angle2 = 23040;
3479 e->type = ELMT_NONE;
3480 g->elists->elements = elements;
3483 /* Purpose of <graph_type>_initialize functions:
3484 * - find maximum and minimum for both axes
3485 * - call setup routine for style struct */
3486 static void tput_initialize (struct graph *g)
3488 struct segment *tmp, *oldest, *last;
3490 double dtime, tput, tputmax=0;
3491 double t0, tmax, y0, ymax;
3493 debug(DBS_FENTRY) puts ("tput_initialize()");
3495 tput_read_config(g);
3497 for (last=g->segments; last->next; last=last->next);
3498 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3499 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3500 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3501 if (i>g->s.tput.nsegs) {
3502 sum -= oldest->data;
3503 oldest=oldest->next;
3507 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3512 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3513 tmax = last->rel_secs + last->rel_usecs / 1000000.0;
3519 g->bounds.width = tmax - t0;
3520 g->bounds.height = ymax - y0;
3521 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3522 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3525 static void tput_read_config (struct graph *g)
3527 debug(DBS_FENTRY) puts ("tput_read_config()");
3529 g->s.tput.width = 4;
3530 g->s.tput.height = 4;
3531 g->s.tput.nsegs = 20;
3533 g->title = (char ** )malloc (2 * sizeof (char *));
3534 g->title[0] = "Throughput Graph";
3536 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3537 g->y_axis->label[0] = "[B/s]";
3538 g->y_axis->label[1] = "Throughput";
3539 g->y_axis->label[2] = NULL;
3540 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3541 g->x_axis->label[0] = "Time[s]";
3542 g->x_axis->label[1] = NULL;
3543 g->s.tput.flags = 0;
3546 static void tput_toggle_time_origin (struct graph *g)
3548 g->s.tput.flags ^= TIME_ORIGIN;
3550 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3551 g->x_axis->min = g->bounds.x0;
3552 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3558 static void rtt_read_config (struct graph *g)
3560 debug(DBS_FENTRY) puts ("rtt_read_config()");
3563 g->s.rtt.height = 4;
3566 g->title = (char ** )malloc (2 * sizeof (char *));
3567 g->title[0] = "Round Trip Time Graph";
3569 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3570 g->y_axis->label[0] = "RTT [s]";
3571 g->y_axis->label[1] = NULL;
3572 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3573 g->x_axis->label[0] = "Sequence Number[B]";
3574 g->x_axis->label[1] = NULL;
3577 static void rtt_initialize (struct graph *g)
3579 struct segment *tmp, *first=NULL;
3580 struct unack *unack = NULL, *u;
3582 double x0, xmax=0, y0, ymax;
3584 debug(DBS_FENTRY) puts ("rtt_initialize()");
3586 rtt_read_config (g);
3588 for (tmp=g->segments; tmp; tmp=tmp->next) {
3589 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3590 unsigned int seqno = ntohl (tmp->tcphdr.seq);
3595 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3596 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3597 u = rtt_get_new_unack (time, seqno);
3599 rtt_put_unack_on_list (&unack, u);
3602 if (seqno + tmp->data > xmax)
3603 xmax = seqno + tmp->data;
3605 unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
3606 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3609 for (u=unack; u; u=v)
3610 if (ackno > u->seqno) {
3611 double rtt = time - u->time;
3615 rtt_delete_unack_from_list (&unack, u);
3621 x0 = ntohl (first->tcphdr.seq);
3627 g->bounds.width = xmax - x0;
3628 g->bounds.height = ymax - y0;
3629 g->zoom.x = g->geom.width / g->bounds.width;
3630 g->zoom.y = g->geom.height / g->bounds.height;
3633 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
3637 for (u=list; u; u=u->next)
3638 if (u->seqno == seqno)
3644 static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
3648 u = (struct unack * )malloc (sizeof (struct unack));
3657 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
3659 struct unack *u, *list = *l;
3661 for (u=list; u; u=u->next)
3671 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
3673 struct unack *u, *list = *l;
3682 for (u=list; u; u=u->next)
3683 if (u->next == dead) {
3684 u->next = u->next->next;
3690 static void rtt_make_elmtlist (struct graph *g)
3692 struct segment *tmp;
3693 struct unack *unack = NULL, *u;
3694 struct element *elements, *e;
3696 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
3698 if (g->elists->elements == NULL) {
3699 int n = 1 + get_num_dsegs (g);
3700 e = elements = (struct element * )malloc (n*sizeof (struct element));
3702 e = elements = g->elists->elements;
3704 for (tmp=g->segments; tmp; tmp=tmp->next) {
3705 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3706 unsigned int seqno = ntohl (tmp->tcphdr.seq);
3708 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3709 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3710 u = rtt_get_new_unack (time, seqno);
3712 rtt_put_unack_on_list (&unack, u);
3715 unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
3716 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3719 for (u=unack; u; u=v)
3720 if (ackno > u->seqno) {
3721 double rtt = time - u->time;
3726 e->p.arc.dim.width = g->s.rtt.width;
3727 e->p.arc.dim.height = g->s.rtt.height;
3728 e->p.arc.dim.x = g->zoom.x * (u->seqno - g->bounds.x0)
3729 - g->s.rtt.width/2.0;
3730 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
3731 e->p.arc.filled = TRUE;
3732 e->p.arc.angle1 = 0;
3733 e->p.arc.angle2 = 23040;
3737 rtt_delete_unack_from_list (&unack, u);
3742 e->type = ELMT_NONE;
3743 g->elists->elements = elements;
3746 static void rtt_toggle_seq_origin (struct graph *g)
3748 g->s.rtt.flags ^= SEQ_ORIGIN;
3750 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3751 g->x_axis->min = g->bounds.x0;
3757 /* replacement of Unix rint() for Windows */
3758 static int rint (double x)
3763 buf = _fcvt(x, 0, &dec, &sig);