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.18 2002/04/29 08:20:18 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 * );
392 static void create_text_widget (struct graph * );
393 static void display_text (struct graph * );
395 static void create_drawing_area (struct graph * );
396 static void control_panel_create (struct graph * );
397 static GtkWidget *control_panel_create_zoom_group (struct graph * );
398 static GtkWidget *control_panel_create_magnify_group (struct graph * );
399 static GtkWidget *control_panel_create_cross_group (struct graph * );
400 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
401 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
402 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
403 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
404 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
405 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
406 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
407 static void callback_toplevel_destroy (GtkWidget * , gpointer );
408 static void callback_close (GtkWidget * , gpointer );
409 static void callback_time_origin (GtkWidget * , gpointer );
410 static void callback_seq_origin (GtkWidget * , gpointer );
411 static void callback_zoomlock_h (GtkWidget * , gpointer );
412 static void callback_zoomlock_v (GtkWidget * , gpointer );
413 static void callback_zoom_inout (GtkWidget * , gpointer );
414 static void callback_zoom_step (GtkWidget * , gpointer );
415 static void callback_zoom_flags (GtkWidget * , gpointer );
416 static void callback_cross_on_off (GtkWidget * , gpointer );
417 static void callback_mag_width (GtkWidget * , gpointer );
418 static void callback_mag_height (GtkWidget * , gpointer );
419 static void callback_mag_x (GtkWidget * , gpointer );
420 static void callback_mag_y (GtkWidget * , gpointer );
421 static void callback_mag_zoom (GtkWidget * , gpointer );
422 static void callback_mag_flags (GtkWidget * , gpointer );
423 static void callback_graph_type (GtkWidget * , gpointer );
424 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
425 static void callback_create_help (GtkWidget * , gpointer );
426 static void callback_close_help (GtkWidget * , gpointer );
427 static void update_zoom_spins (struct graph * );
428 static int get_headers (frame_data *, char * , struct segment * );
429 static int compare_headers (struct segment * , struct segment * , int );
430 static int get_num_dsegs (struct graph * );
431 static int get_num_acks (struct graph * );
432 static void graph_type_dependent_initialize (struct graph * );
433 static void graph_put (struct graph * );
434 static struct graph *graph_new (void);
435 static void graph_destroy (struct graph * );
436 static void graph_initialize_values (struct graph * );
437 static void graph_init_sequence (struct graph * );
438 static void draw_element_line (struct graph * , struct element * );
439 static void draw_element_arc (struct graph * , struct element * );
440 static void graph_display (struct graph * );
441 static void graph_pixmaps_create (struct graph * );
442 static void graph_pixmaps_switch (struct graph * );
443 static void graph_pixmap_draw (struct graph * );
444 static void graph_pixmap_display (struct graph * );
445 static void graph_element_lists_make (struct graph * );
446 static void graph_element_lists_free (struct graph * );
447 static void graph_element_lists_initialize (struct graph * );
448 static void graph_title_pixmap_create (struct graph * );
449 static void graph_title_pixmap_draw (struct graph * );
450 static void graph_title_pixmap_display (struct graph * );
451 static void graph_segment_list_get (struct graph * );
452 static void graph_segment_list_free (struct graph * );
453 static void graph_select_segment (struct graph * , int , int );
454 static int line_detect_collision (struct element * , int , int );
455 static int arc_detect_collision (struct element * , int , int );
456 static void update_packet_list (int );
457 static void axis_pixmaps_create (struct axis * );
458 static void axis_pixmaps_switch (struct axis * );
459 static void axis_display (struct axis * );
460 static void v_axis_pixmap_draw (struct axis * );
461 static void h_axis_pixmap_draw (struct axis * );
462 static void axis_pixmap_display (struct axis * );
463 static void axis_compute_ticks (struct axis * , double , double , int );
464 static double axis_zoom_get (struct axis * , int );
465 static void axis_ticks_up (int * , int * );
466 static void axis_ticks_down (int * , int * );
467 static void axis_destroy (struct axis * );
468 static int get_label_dim (struct axis * , int , double );
469 static void toggle_time_origin (struct graph * );
470 static void toggle_seq_origin (struct graph * );
471 static void cross_xor (struct graph * , int , int );
472 static void cross_draw (struct graph * , int , int );
473 static void cross_erase (struct graph * );
474 static void magnify_create (struct graph * , int , int );
475 static void magnify_move (struct graph * , int , int );
476 static void magnify_destroy (struct graph * );
477 static void magnify_draw (struct graph * );
478 static void magnify_get_geom (struct graph * , int , int );
479 static gint configure_event (GtkWidget * , GdkEventConfigure * );
480 static gint expose_event (GtkWidget * , GdkEventExpose * );
481 static gint button_press_event (GtkWidget * , GdkEventButton * );
482 static gint button_release_event (GtkWidget * , GdkEventButton * );
483 static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
484 static gint key_press_event (GtkWidget * , GdkEventKey * );
485 static gint key_release_event (GtkWidget * , GdkEventKey * );
486 static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
487 static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
488 static void tseq_stevens_initialize (struct graph * );
489 static void tseq_stevens_get_bounds (struct graph * );
490 static void tseq_stevens_read_config (struct graph * );
491 static void tseq_stevens_make_elmtlist (struct graph * );
492 static void tseq_stevens_toggle_seq_origin (struct graph * );
493 static void tseq_stevens_toggle_time_origin (struct graph * );
494 static void tseq_tcptrace_read_config (struct graph * );
495 static void tseq_tcptrace_make_elmtlist (struct graph * );
496 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
497 static void tseq_tcptrace_toggle_time_origin (struct graph * );
498 static void tput_initialize (struct graph * );
499 static void tput_read_config (struct graph * );
500 static void tput_make_elmtlist (struct graph * );
501 static void tput_toggle_time_origin (struct graph * );
502 static void rtt_read_config (struct graph * );
503 static void rtt_initialize (struct graph * );
504 static int rtt_is_retrans (struct unack * , unsigned int );
505 static struct unack *rtt_get_new_unack (double , unsigned int );
506 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
507 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
508 static void rtt_make_elmtlist (struct graph * );
509 static void rtt_toggle_seq_origin (struct graph * );
511 static int rint (double ); /* compiler template for Windows */
514 static char helptext[] =
516 "Here's what you can do:\n\
517 - Left Mouse Button selects segment in ethereal's packet list\n\
518 - Middle Mouse Button zooms in\n\
519 - <shift>-Middle Button zooms out\n\
520 - Right Mouse Button moves the graph (if zoomed in)\n\
521 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
522 - Space toggles crosshairs\n\
523 - 's' toggles relative/absolute sequence numbers\n\
524 - 't' toggles time origin\n\
527 "Here's what you can do:\n\
528 - <ctrl>-Left Mouse Button selects segment in ethereal's packet list\n\
529 - Left Mouse Button zooms in\n\
530 - <shift>-Left Mouse Button zooms out\n\
531 - Right Mouse Button moves the graph (if zoomed in)\n\
532 - <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
534 - Space bar toggles crosshairs\n\
535 - 's' - Toggles relative/absolute sequence numbers\n\
536 - 't' - Toggles time origin\n\
540 void tcp_graph_cb (GtkWidget *w _U_, gpointer data _U_, guint graph_type)
542 struct segment current;
545 debug(DBS_FENTRY) puts ("tcp_graph_cb()");
547 if (! (g = graph_new()))
551 graph_initialize_values (g);
554 g->type = graph_type;
555 if (!get_headers (cfile.current_frame, cfile.pd, ¤t)) {
556 /* currently selected packet is neither TCP over IP over Ethernet II/PPP
557 * nor TCP over IP alone - should display some
558 * kind of warning dialog */
559 simple_dialog(ESD_TYPE_WARN, NULL,
560 "Selected packet is not a TCP segment");
564 graph_segment_list_get(g);
566 /* display_text(g); */
567 graph_init_sequence(g);
570 static void create_gui (struct graph *g)
572 debug(DBS_FENTRY) puts ("create_gui()");
573 /* create_text_widget(g); */
574 control_panel_create (g);
575 create_drawing_area(g);
579 static void create_text_widget (struct graph *g)
581 GtkWidget *streamwindow, *txt_scrollw, *box;
583 debug(DBS_FENTRY) puts ("create_text_widget()");
584 streamwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
585 gtk_widget_set_name (streamwindow, "Packet chain");
586 gtk_widget_set_usize (GTK_WIDGET (streamwindow), TXT_WIDTH, TXT_HEIGHT);
587 gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
588 gtk_signal_connect (GTK_OBJECT (streamwindow), "realize",
589 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
591 box = gtk_vbox_new (FALSE, 0);
592 gtk_container_add (GTK_CONTAINER (streamwindow), box);
593 gtk_widget_show (box);
595 txt_scrollw = scrolled_window_new (NULL, NULL);
596 gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
597 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
598 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
599 gtk_widget_show (txt_scrollw);
601 g->text = gtk_text_new (NULL, NULL);
602 gtk_text_set_editable (GTK_TEXT (g->text), FALSE);
603 gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
604 gtk_widget_show (g->text);
605 gtk_widget_show (streamwindow);
607 static void display_text (struct graph *g)
611 double first_time, prev_time;
612 unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
615 debug(DBS_FENTRY) puts ("display_text()");
616 gdk_color_parse ("SlateGray", &color);
617 gtk_text_freeze (GTK_TEXT (g->text));
618 snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
619 "pkt num", "time", "delta first", "delta prev",
620 "seqno", "delta first", "delta prev", "data (B)");
621 gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL,
622 (const char *)line, -1);
624 first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
625 prev_time = first_time;
626 /* we have to find Initial Sequence Number for both ends of connection */
627 for (ptr=g->segments; ptr; ptr=ptr->next) {
628 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
629 isn_this = ntohl (ptr->tcphdr.seq);
633 for (ptr=g->segments; ptr; ptr=ptr->next) {
634 if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
635 isn_opposite = ntohl (ptr->tcphdr.seq);
639 seq_this_prev = isn_this;
640 seq_opposite_prev = isn_opposite;
641 for (ptr=g->segments; ptr; ptr=ptr->next) {
642 double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
643 unsigned int seq = ntohl (ptr->tcphdr.seq);
644 int seq_delta_isn, seq_delta_prev;
646 if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
647 seq_delta_isn = seq - isn_this;
648 seq_delta_prev = seq - seq_this_prev;
652 seq_delta_isn = seq - isn_opposite;
653 seq_delta_prev = seq - seq_opposite_prev;
654 seq_opposite_prev = seq;
657 snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
658 ptr->num, time, time-first_time, time-prev_time,
659 seq, seq_delta_isn, seq_delta_prev,
660 ntohs (ptr->iphdr.tot_len) - 4*IHL(&(ptr->iphdr)) -
661 4*DOFF(ptr->tcphdr));
662 gtk_text_insert (GTK_TEXT (g->text), g->font, c, NULL,
663 (const char * )line, -1);
666 gtk_text_thaw (GTK_TEXT (g->text));
670 static void create_drawing_area (struct graph *g)
672 GdkColormap *colormap;
674 #define WINDOW_TITLE_LENGTH 64
675 char window_title[WINDOW_TITLE_LENGTH];
677 debug(DBS_FENTRY) puts ("create_drawing_area()");
679 g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
681 g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
682 "-*-*-m-*-iso8859-2");
684 g->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
685 gtk_widget_set_name (g->toplevel, "Test Graph");
686 gtk_signal_connect (GTK_OBJECT (g->toplevel), "realize",
687 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
689 /* Create the drawing area */
690 g->drawing_area = gtk_drawing_area_new ();
691 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
692 gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
693 g->wp.width + g->wp.x + RMARGIN_WIDTH,
694 g->wp.height + g->wp.y + g->x_axis->s.height);
695 gtk_widget_show (g->drawing_area);
697 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "expose_event",
698 (GtkSignalFunc )expose_event, NULL);
699 /* this has to be done later, after the widget has been shown */
701 gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
702 (GtkSignalFunc )configure_event, NULL);
704 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "motion_notify_event",
705 (GtkSignalFunc )motion_notify_event, NULL);
706 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_press_event",
707 (GtkSignalFunc )button_press_event, NULL);
708 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_release_event",
709 (GtkSignalFunc )button_release_event, NULL);
710 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "leave_notify_event",
711 (GtkSignalFunc )leave_notify_event, NULL);
712 gtk_signal_connect (GTK_OBJECT (g->drawing_area), "enter_notify_event",
713 (GtkSignalFunc )enter_notify_event, NULL);
714 gtk_signal_connect (GTK_OBJECT (g->toplevel), "destroy",
715 (GtkSignalFunc )callback_toplevel_destroy, g);
716 /* why doesn't drawing area send key_press_signals? */
717 gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_press_event",
718 (GtkSignalFunc )key_press_event, NULL);
719 gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_release_event",
720 (GtkSignalFunc )key_release_event, NULL);
721 gtk_widget_set_events (g->toplevel,GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
723 gtk_widget_set_events (g->drawing_area, GDK_EXPOSURE_MASK
724 | GDK_LEAVE_NOTIFY_MASK
725 | GDK_ENTER_NOTIFY_MASK
726 | GDK_BUTTON_PRESS_MASK
727 | GDK_BUTTON_RELEASE_MASK
728 | GDK_POINTER_MOTION_MASK
729 | GDK_POINTER_MOTION_HINT_MASK);
732 frame = gtk_frame_new (NULL);
733 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
734 gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
736 box = gtk_hbox_new (FALSE, 0);
737 gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
738 gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
739 gtk_container_add (GTK_CONTAINER (g->toplevel), box);
740 gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
741 gtk_widget_show (frame);
742 gtk_widget_show (box);
745 gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
746 gtk_widget_show (g->toplevel);
747 snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d - Ethereal",
749 gtk_window_set_title (GTK_WINDOW (g->toplevel), window_title);
751 /* in case we didn't get what we asked for */
752 g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
753 g->wp.x - RMARGIN_WIDTH;
754 g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
755 g->wp.y - g->x_axis->s.height;
757 g->font = g->drawing_area->style->font;
758 gdk_font_ref (g->font);
760 colormap = gdk_window_get_colormap (g->drawing_area->window);
762 xor_gc = gdk_gc_new (g->drawing_area->window);
763 gdk_gc_set_function (xor_gc, GDK_XOR);
764 gdk_color_parse ("gray15", &color);
765 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
766 gdk_gc_set_foreground (xor_gc, &color);
768 g->fg_gc = gdk_gc_new (g->drawing_area->window);
769 g->bg_gc = gdk_gc_new (g->drawing_area->window);
770 gdk_color_parse ("white", &color);
771 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
772 gdk_gc_set_foreground (g->bg_gc, &color);
774 /* this is probably quite an ugly way to get rid of the first configure
776 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
777 * event which is handled during the next return to gtk_main which is
778 * probably the gdk_gc_new() call. configure handler calls
779 * graph_element_lists_make() which is not good because the graph struct is
780 * not fully set up yet - namely we're not sure about actual geometry
781 * and we don't have the GC's at all. so we just postpone installation
782 * of configure handler until we're ready to deal with it.
784 * !!! NEMÌLO BY TO BÝT NA KONCI graph_init_sequence()? !!!
787 gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
788 (GtkSignalFunc )configure_event, NULL);
790 /* puts ("exiting create_drawing_area()"); */
793 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
795 struct graph *g = (struct graph * )data;
797 if (!(g->flags & GRAPH_DESTROYED)) {
798 g->flags |= GRAPH_DESTROYED;
799 graph_destroy ((struct graph * )data);
803 static void control_panel_create (struct graph *g)
805 GtkWidget *toplevel, *notebook;
807 GtkWidget *help, *close, *button_box;
808 #define WINDOW_TITLE_LENGTH 64
809 char window_title[WINDOW_TITLE_LENGTH];
811 debug(DBS_FENTRY) puts ("control_panel_create()");
813 notebook = gtk_notebook_new ();
814 control_panel_add_zoom_page (g, notebook);
815 control_panel_add_magnify_page (g, notebook);
816 control_panel_add_origin_page (g, notebook);
817 control_panel_add_cross_page (g, notebook);
818 control_panel_add_graph_type_page (g, notebook);
820 /* bottom buttons group */
821 help = gtk_button_new_with_label ("Help");
822 close = gtk_button_new_with_label ("Close");
823 button_box = gtk_hbox_new (TRUE, 0);
824 gtk_box_pack_start (GTK_BOX (button_box), help, TRUE, TRUE, 0);
825 gtk_box_pack_start (GTK_BOX (button_box), close, TRUE, TRUE, 0);
827 toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
828 gtk_signal_connect (GTK_OBJECT (toplevel), "realize",
829 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
831 table = gtk_table_new (2, 1, FALSE);
832 gtk_container_add (GTK_CONTAINER (toplevel), table);
834 gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
835 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
836 gtk_table_attach (GTK_TABLE (table), button_box, 0, 1, 1, 2,
837 GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
839 gtk_signal_connect (GTK_OBJECT (close), "clicked",
840 (GtkSignalFunc )callback_close, g);
841 gtk_signal_connect (GTK_OBJECT (help), "clicked",
842 (GtkSignalFunc )callback_create_help, g);
844 /* gtk_widget_show_all (table); */
845 /* g->gui.control_panel = table; */
846 gtk_widget_show_all (toplevel);
847 snprintf (window_title, WINDOW_TITLE_LENGTH,
848 "Graph %d - Control - Ethereal", refnum);
849 gtk_window_set_title (GTK_WINDOW (toplevel), window_title);
850 g->gui.control_panel = toplevel;
853 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
855 GtkWidget *zoom_frame;
856 GtkWidget *zoom_lock_frame;
860 zoom_frame = control_panel_create_zoom_group (g);
861 gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
862 zoom_lock_frame = control_panel_create_zoomlock_group (g);
863 gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
864 box = gtk_vbox_new (FALSE, 0);
865 gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
866 gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
867 gtk_widget_show (box);
868 label = gtk_label_new ("Zoom");
869 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
872 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
874 GtkWidget *mag_frame, *label;
876 mag_frame = control_panel_create_magnify_group (g);
877 gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
878 label = gtk_label_new ("Magnify");
879 gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
882 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
884 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
885 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
886 GtkWidget *box, *label;
888 /* time origin box */
890 gtk_radio_button_new_with_label (NULL, "beginning of capture");
891 time_orig_conn = gtk_radio_button_new_with_label (
892 gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)),
893 "beginning of this TCP connection");
894 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
895 time_orig_box = gtk_vbox_new (TRUE, 0);
896 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
897 gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
898 time_orig_frame = gtk_frame_new ("Time origin");
899 gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
900 gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
902 /* sequence number origin group */
904 gtk_radio_button_new_with_label (NULL, "initial sequence number");
905 seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
906 GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
908 seq_orig_box = gtk_vbox_new (TRUE, 0);
909 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
910 gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
911 seq_orig_frame = gtk_frame_new ("Sequence number origin");
912 gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
913 gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
915 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
916 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
918 gtk_signal_connect (GTK_OBJECT (time_orig_conn), "toggled",
919 (GtkSignalFunc )callback_time_origin, g);
920 gtk_signal_connect (GTK_OBJECT (seq_orig_isn), "toggled",
921 (GtkSignalFunc )callback_seq_origin, g);
923 box = gtk_vbox_new (FALSE, 0);
924 gtk_container_set_border_width (GTK_CONTAINER (box), 5);
925 gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
926 gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
927 gtk_widget_show (box);
928 label = gtk_label_new ("Origin");
929 gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
932 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
934 GtkWidget *cross_frame, *label;
936 cross_frame = control_panel_create_cross_group (g);
937 gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
938 label = gtk_label_new ("Cross");
939 gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
942 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
944 GtkWidget *frame, *label;
946 frame = control_panel_create_graph_type_group (g);
947 gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
948 label = gtk_label_new ("Graph type");
949 gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
952 static void callback_close (GtkWidget *widget _U_, gpointer data)
954 struct graph *g = (struct graph * )data;
956 if (!(g->flags & GRAPH_DESTROYED)) {
957 g->flags |= GRAPH_DESTROYED;
958 graph_destroy ((struct graph * )data);
962 static void callback_create_help (GtkWidget *widget _U_, gpointer data)
964 struct graph *g = (struct graph * )data;
965 GtkWidget *toplevel, *box, *text, *scroll, *close;
967 toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
968 gtk_window_set_title(GTK_WINDOW(toplevel), "Help for TCP graphing");
969 gtk_widget_set_usize (toplevel, 500, 400);
970 gtk_signal_connect (GTK_OBJECT (toplevel), "realize",
971 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
973 box = gtk_vbox_new (FALSE, 0);
974 gtk_container_add (GTK_CONTAINER (toplevel), box);
975 scroll = gtk_scrolled_window_new (NULL, NULL);
976 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
977 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
978 gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
979 text = gtk_text_new (NULL, NULL);
980 gtk_text_set_editable (GTK_TEXT (text), FALSE);
981 gtk_text_set_line_wrap (GTK_TEXT (text), FALSE);
982 gtk_text_set_word_wrap (GTK_TEXT (text), FALSE);
983 gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1);
984 gtk_container_add (GTK_CONTAINER (scroll), text);
985 close = gtk_button_new_with_label ("Close");
986 gtk_box_pack_start (GTK_BOX (box), close, FALSE, FALSE, 0);
987 gtk_signal_connect (GTK_OBJECT (close), "clicked",
988 (GtkSignalFunc )callback_close_help, toplevel);
990 gtk_widget_show_all (toplevel);
993 static void callback_close_help (GtkWidget *widget _U_, gpointer data)
995 gtk_widget_destroy ((GtkWidget * )data);
998 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1000 toggle_time_origin ((struct graph * )data);
1003 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1005 toggle_seq_origin ((struct graph * )data);
1008 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1010 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1011 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1012 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1013 GtkWidget *zoom_v_step_label, *zoom_v_step;
1014 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1015 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1016 GtkWidget *zoom_h_entry, *zoom_v_entry;
1017 GtkWidget *zoom_h_label, *zoom_v_label;
1019 zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1020 zoom_out = gtk_radio_button_new_with_label (
1021 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1022 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1023 zoom_inout_box = gtk_hbox_new (FALSE, 0);
1024 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1025 gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1027 zoom_separator1 = gtk_hseparator_new ();
1029 zoom_h_entry = gtk_entry_new ();
1030 gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1031 gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1032 zoom_h_label = gtk_label_new ("Horizontal:");
1034 zoom_v_entry = gtk_entry_new ();
1035 gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1036 gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1037 zoom_v_label = gtk_label_new ("Vertical:");
1039 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1040 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1042 zoom_table = gtk_table_new (2, 2, FALSE);
1043 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1044 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1045 gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1046 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1047 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1048 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1049 gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1050 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1052 zoom_separator2 = gtk_hseparator_new ();
1054 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
1055 zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1056 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1057 zoom_h_step_label = gtk_label_new ("Horizontal step:");
1059 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
1060 zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1061 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1062 zoom_v_step_label = gtk_label_new ("Vertical step:");
1064 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1065 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1067 zoom_same_toggle = gtk_check_button_new_with_label ("Keep them the same");
1068 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1069 gtk_object_set_data (GTK_OBJECT (zoom_same_toggle), "flag",
1070 (gpointer )ZOOM_STEPS_SAME);
1071 gtk_object_set_data (GTK_OBJECT (zoom_ratio_toggle), "flag",
1072 (gpointer )ZOOM_STEPS_KEEP_RATIO);
1073 gtk_signal_connect (GTK_OBJECT (zoom_same_toggle), "clicked",
1074 (GtkSignalFunc )callback_zoom_flags, g);
1075 gtk_signal_connect (GTK_OBJECT (zoom_ratio_toggle), "clicked",
1076 (GtkSignalFunc )callback_zoom_flags, g);
1078 zoom_step_table = gtk_table_new (4, 2, FALSE);
1079 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1080 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1081 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1082 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1083 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1084 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1085 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1086 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1087 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1088 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1089 gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1090 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1092 zoom_box = gtk_vbox_new (FALSE, 0);
1093 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1094 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1095 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1096 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1097 gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1098 zoom_frame = gtk_frame_new ("Zoom");
1099 gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1101 gtk_object_set_data (GTK_OBJECT (zoom_h_step), "direction", (gpointer )0);
1102 gtk_object_set_data (GTK_OBJECT (zoom_v_step), "direction", (gpointer )1);
1104 gtk_signal_connect (GTK_OBJECT (zoom_in), "toggled",
1105 (GtkSignalFunc )callback_zoom_inout, g);
1106 gtk_signal_connect (GTK_OBJECT (zoom_h_step), "changed",
1107 (GtkSignalFunc )callback_zoom_step, g);
1108 gtk_signal_connect (GTK_OBJECT (zoom_v_step), "changed",
1109 (GtkSignalFunc )callback_zoom_step, g);
1111 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1112 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1116 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1118 struct graph *g = (struct graph * )data;
1120 if (GTK_TOGGLE_BUTTON (toggle)->active)
1121 g->zoom.flags &= ~ZOOM_OUT;
1123 g->zoom.flags |= ZOOM_OUT;
1126 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1128 struct graph *g = (struct graph * )data;
1131 double *zoom_this, *zoom_other;
1132 GtkSpinButton *widget_this, *widget_other;
1135 direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
1136 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1139 zoom_this = &g->zoom.step_y;
1140 zoom_other = &g->zoom.step_x;
1141 widget_this = g->zoom.widget.v_step;
1142 widget_other = g->zoom.widget.h_step;
1144 zoom_this = &g->zoom.step_x;
1145 zoom_other = &g->zoom.step_y;
1146 widget_this = g->zoom.widget.h_step;
1147 widget_other = g->zoom.widget.v_step;
1150 old_this = *zoom_this;
1152 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1153 *zoom_other = value;
1154 gtk_spin_button_set_value (widget_other, *zoom_other);
1155 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1156 double old_other = *zoom_other;
1157 *zoom_other *= value / old_this;
1158 if (*zoom_other < 1.0) {
1160 *zoom_this = old_this * 1.0 / old_other;
1161 gtk_spin_button_set_value (widget_this, *zoom_this);
1162 } else if (*zoom_other > 5.0) {
1164 *zoom_this = old_this * 5.0 / old_other;
1165 gtk_spin_button_set_value (widget_this, *zoom_this);
1167 gtk_spin_button_set_value (widget_other, *zoom_other);
1171 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1173 struct graph *g = (struct graph * )data;
1174 int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
1176 if (GTK_TOGGLE_BUTTON (toggle)->active)
1177 g->zoom.flags |= flag;
1179 g->zoom.flags &= ~flag;
1182 static void update_zoom_spins (struct graph *g)
1186 snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
1187 gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1188 snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
1189 gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1192 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1194 GtkWidget *mag_width_label, *mag_width;
1195 GtkWidget *mag_height_label, *mag_height;
1196 GtkWidget *mag_x_label, *mag_x;
1197 GtkWidget *mag_y_label, *mag_y;
1198 GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1199 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1200 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1201 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1202 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1203 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1204 GtkWidget *mag_box, *mag_frame;
1206 mag_width_label = gtk_label_new ("Width:");
1207 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1208 mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1210 mag_height_label = gtk_label_new ("Height:");
1211 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1212 mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1214 mag_x_label = gtk_label_new ("X:");
1215 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1216 mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1218 mag_y_label = gtk_label_new ("Y:");
1219 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1220 mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1222 mag_wh_table = gtk_table_new (4, 2, FALSE);
1223 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1224 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1225 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1226 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1227 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1228 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1229 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1230 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1231 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1232 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1233 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1234 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1235 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1236 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1237 gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1238 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1240 mag_h_zoom_label = gtk_label_new ("Horizontal:");
1241 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
1242 mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1244 mag_v_zoom_label = gtk_label_new ("Vertical:");
1245 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
1246 mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1248 mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1249 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1251 mag_zoom_table = gtk_table_new (4, 2, FALSE);
1252 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1253 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1254 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1255 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1256 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1257 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1258 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1259 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1260 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1261 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1262 gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1263 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1265 mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1266 gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1267 gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1269 mag_box = gtk_vbox_new (FALSE, 0);
1270 gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1271 gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1272 mag_frame = gtk_frame_new ("Magnify");
1273 gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1275 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1276 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1277 gtk_object_set_data (GTK_OBJECT (mag_h_zoom), "direction", (gpointer )0);
1278 gtk_object_set_data (GTK_OBJECT (mag_v_zoom), "direction", (gpointer )1);
1279 gtk_object_set_data (GTK_OBJECT (mag_zoom_same), "flag",
1280 (gpointer )MAGZOOMS_SAME);
1281 gtk_object_set_data (GTK_OBJECT (mag_zoom_ratio), "flag",
1282 (gpointer )MAGZOOMS_SAME_RATIO);
1284 gtk_signal_connect (GTK_OBJECT (mag_width), "changed",
1285 (GtkSignalFunc )callback_mag_width, g);
1286 gtk_signal_connect (GTK_OBJECT (mag_height), "changed",
1287 (GtkSignalFunc )callback_mag_height, g);
1288 gtk_signal_connect (GTK_OBJECT (mag_x), "changed",
1289 (GtkSignalFunc )callback_mag_x, g);
1290 gtk_signal_connect (GTK_OBJECT (mag_y), "changed",
1291 (GtkSignalFunc )callback_mag_y, g);
1292 gtk_signal_connect (GTK_OBJECT (mag_h_zoom), "changed",
1293 (GtkSignalFunc )callback_mag_zoom, g);
1294 gtk_signal_connect (GTK_OBJECT (mag_v_zoom), "changed",
1295 (GtkSignalFunc )callback_mag_zoom, g);
1296 gtk_signal_connect (GTK_OBJECT (mag_zoom_same), "clicked",
1297 (GtkSignalFunc )callback_mag_flags, g);
1298 gtk_signal_connect (GTK_OBJECT (mag_zoom_ratio), "clicked",
1299 (GtkSignalFunc )callback_mag_flags, g);
1304 static void callback_mag_width (GtkWidget *spin, gpointer data)
1306 struct graph *g = (struct graph * )data;
1308 g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1311 static void callback_mag_height (GtkWidget *spin, gpointer data)
1313 struct graph *g = (struct graph * )data;
1315 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1318 static void callback_mag_x (GtkWidget *spin, gpointer data)
1320 struct graph *g = (struct graph * )data;
1322 g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1325 static void callback_mag_y (GtkWidget *spin, gpointer data)
1327 struct graph *g = (struct graph * )data;
1329 g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1332 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1334 struct graph *g = (struct graph * )data;
1337 double *zoom_this, *zoom_other;
1338 GtkSpinButton *widget_this, *widget_other;
1341 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1342 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1343 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1346 direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
1347 value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
1350 zoom_this = &g->magnify.zoom.y;
1351 zoom_other = &g->magnify.zoom.x;
1352 widget_this = g->magnify.widget.v_zoom;
1353 widget_other = g->magnify.widget.h_zoom;
1355 zoom_this = &g->magnify.zoom.x;
1356 zoom_other = &g->magnify.zoom.y;
1357 widget_this = g->magnify.widget.h_zoom;
1358 widget_other = g->magnify.widget.v_zoom;
1361 old_this = *zoom_this;
1363 if (g->magnify.flags & MAGZOOMS_SAME) {
1364 *zoom_other = value;
1365 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1366 gtk_spin_button_set_value (widget_other, *zoom_other);
1367 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1368 double old_other = *zoom_other;
1369 *zoom_other *= value / old_this;
1370 if (*zoom_other < 1.0) {
1372 *zoom_this = old_this * 1.0 / old_other;
1373 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1374 gtk_spin_button_set_value (widget_this, *zoom_this);
1375 } else if (*zoom_other > 25.0) {
1377 *zoom_this = old_this * 25.0 / old_other;
1378 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1379 gtk_spin_button_set_value (widget_this, *zoom_this);
1381 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1382 gtk_spin_button_set_value (widget_other, *zoom_other);
1386 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1388 struct graph *g = (struct graph * )data;
1389 int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
1391 if (GTK_TOGGLE_BUTTON (toggle)->active)
1392 g->magnify.flags |= flag;
1394 g->magnify.flags &= ~flag;
1397 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1399 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1400 GtkWidget *zoom_lock_frame;
1402 zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1403 zoom_lock_h = gtk_radio_button_new_with_label (
1404 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1406 zoom_lock_v = gtk_radio_button_new_with_label (
1407 gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1409 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1410 zoom_lock_box = gtk_hbox_new (FALSE, 0);
1411 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0);
1412 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1413 gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1414 zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1415 gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1417 gtk_signal_connect (GTK_OBJECT (zoom_lock_h), "toggled",
1418 (GtkSignalFunc )callback_zoomlock_h, g);
1419 gtk_signal_connect (GTK_OBJECT (zoom_lock_v), "toggled",
1420 (GtkSignalFunc )callback_zoomlock_v, g);
1422 return zoom_lock_frame;
1425 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1427 struct graph *g = (struct graph * )data;
1429 if (GTK_TOGGLE_BUTTON (toggle)->active)
1430 g->zoom.flags |= ZOOM_HLOCK;
1432 g->zoom.flags &= ~ZOOM_HLOCK;
1435 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1437 struct graph *g = (struct graph * )data;
1439 if (GTK_TOGGLE_BUTTON (toggle)->active)
1440 g->zoom.flags |= ZOOM_VLOCK;
1442 g->zoom.flags &= ~ZOOM_VLOCK;
1445 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1447 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1449 label = gtk_label_new ("Crosshairs:");
1450 off = gtk_radio_button_new_with_label (NULL, "off");
1451 on = gtk_radio_button_new_with_label (
1452 gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on");
1453 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1454 box = gtk_hbox_new (FALSE, 0);
1455 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1456 gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1457 gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1458 vbox = gtk_vbox_new (FALSE, 0);
1459 gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1460 /* frame = gtk_frame_new ("Cross:"); */
1461 frame = gtk_frame_new (NULL);
1462 gtk_container_add (GTK_CONTAINER (frame), vbox);
1464 gtk_signal_connect (GTK_OBJECT (on), "toggled",
1465 (GtkSignalFunc )callback_cross_on_off, g);
1467 g->cross.on_toggle = (GtkToggleButton * )on;
1468 g->cross.off_toggle = (GtkToggleButton * )off;
1473 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1475 struct graph *g = (struct graph * )data;
1477 if (GTK_TOGGLE_BUTTON (toggle)->active) {
1479 g->cross.draw = TRUE;
1480 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
1481 cross_draw (g, x, y);
1483 g->cross.draw = FALSE;
1488 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1490 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1491 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1492 GtkWidget *graph_frame;
1494 graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1495 graph_tseqttrace = gtk_radio_button_new_with_label (
1496 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1497 "Time/Sequence (tcptrace-style)");
1498 graph_tseqstevens = gtk_radio_button_new_with_label (
1499 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1500 "Time/Sequence (Stevens'-style)");
1501 graph_rtt = gtk_radio_button_new_with_label (
1502 gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
1505 case GRAPH_TSEQ_STEVENS:
1506 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1508 case GRAPH_TSEQ_TCPTRACE:
1509 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1511 case GRAPH_THROUGHPUT:
1512 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1515 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1518 graph_init = gtk_check_button_new_with_label ("Init on change");
1519 graph_sep = gtk_hseparator_new ();
1520 graph_box = gtk_vbox_new (FALSE, 0);
1521 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1522 gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1523 gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1524 gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1525 gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1526 gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1527 graph_frame = gtk_frame_new ("Graph type:");
1528 gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1530 gtk_object_set_data (GTK_OBJECT (graph_tseqstevens), "new-graph-type",
1532 gtk_object_set_data (GTK_OBJECT (graph_tseqttrace), "new-graph-type",
1534 gtk_object_set_data (GTK_OBJECT (graph_tput), "new-graph-type",
1536 gtk_object_set_data (GTK_OBJECT (graph_rtt), "new-graph-type",
1539 gtk_signal_connect (GTK_OBJECT (graph_tseqttrace), "toggled",
1540 (GtkSignalFunc )callback_graph_type, g);
1541 gtk_signal_connect (GTK_OBJECT (graph_tseqstevens), "toggled",
1542 (GtkSignalFunc )callback_graph_type, g);
1543 gtk_signal_connect (GTK_OBJECT (graph_tput), "toggled",
1544 (GtkSignalFunc )callback_graph_type, g);
1545 gtk_signal_connect (GTK_OBJECT (graph_rtt), "toggled",
1546 (GtkSignalFunc )callback_graph_type, g);
1547 gtk_signal_connect (GTK_OBJECT (graph_init), "toggled",
1548 (GtkSignalFunc )callback_graph_init_on_typechg, g);
1553 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1555 int old_type, new_type;
1556 struct graph *g = (struct graph * )data;
1558 new_type = (int )gtk_object_get_data (GTK_OBJECT (toggle),"new-graph-type");
1560 if (!GTK_TOGGLE_BUTTON (toggle)->active)
1566 graph_element_lists_free (g);
1567 graph_element_lists_initialize (g);
1569 if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1570 /* throughput graph uses differently constructed segment list so we
1571 * need to recreate it */
1572 graph_segment_list_free (g);
1573 graph_segment_list_get (g);
1576 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1577 g->geom.width = g->wp.width;
1578 g->geom.height = g->wp.height;
1579 g->geom.x = g->wp.x;
1580 g->geom.y = g->wp.y;
1582 g->x_axis->min = g->y_axis->min = 0;
1583 gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1584 gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1585 graph_init_sequence (g);
1588 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1590 ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1593 static struct graph *graph_new (void)
1597 g = (struct graph * )calloc (1, sizeof (struct graph));
1598 graph_element_lists_initialize (g);
1600 g->x_axis = (struct axis * )calloc (1, sizeof (struct axis));
1601 g->y_axis = (struct axis * )calloc (1, sizeof (struct axis));
1603 g->x_axis->flags = 0;
1604 g->x_axis->flags |= AXIS_ORIENTATION;
1605 g->x_axis->s.x = g->x_axis->s.y = 0;
1606 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1607 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1608 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1610 g->y_axis->flags = 0;
1611 g->y_axis->flags &= ~AXIS_ORIENTATION;
1612 g->y_axis->p.x = g->y_axis->p.y = 0;
1613 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1615 g->y_axis->s.y = TITLEBAR_HEIGHT;
1616 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1621 static void graph_initialize_values (struct graph *g)
1623 g->geom.width = g->wp.width = 750;
1624 g->geom.height = g->wp.height = 550;
1625 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1626 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1628 /* g->zoom.x = g->zoom.y = 1.0; */
1629 g->zoom.step_x = g->zoom.step_y = 1.2;
1631 g->cross.draw = g->cross.erase_needed = 0;
1632 g->grab.grabbed = 0;
1633 g->magnify.active = 0;
1634 g->magnify.offset.x = g->magnify.offset.y = 0;
1635 g->magnify.width = g->magnify.height = 250;
1636 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1637 g->magnify.flags = 0;
1640 static void graph_put (struct graph *graph)
1644 for (g=graphs; g->next; g=g->next);
1650 static void graph_init_sequence (struct graph *g)
1652 debug(DBS_FENTRY) puts ("graph_init_sequence()");
1654 graph_type_dependent_initialize (g);
1655 g->zoom.initial.x = g->zoom.x;
1656 g->zoom.initial.y = g->zoom.y;
1657 graph_element_lists_make (g);
1658 g->x_axis->s.width = g->wp.width;
1659 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1660 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1661 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1662 g->y_axis->s.height = g->wp.height;
1663 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1664 graph_pixmaps_create (g);
1665 axis_pixmaps_create (g->y_axis);
1666 axis_pixmaps_create (g->x_axis);
1667 graph_title_pixmap_create (g);
1668 graph_title_pixmap_draw (g);
1669 graph_title_pixmap_display (g);
1671 axis_display (g->y_axis);
1672 axis_display (g->x_axis);
1675 static void graph_type_dependent_initialize (struct graph *g)
1678 case GRAPH_TSEQ_STEVENS:
1679 case GRAPH_TSEQ_TCPTRACE:
1680 tseq_stevens_initialize (g);
1682 case GRAPH_THROUGHPUT:
1683 tput_initialize (g);
1693 static void graph_destroy (struct graph *g)
1696 struct graph *p=NULL;
1697 /* struct graph *tmp; */
1699 debug(DBS_FENTRY) puts ("graph_destroy()");
1701 for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next)
1705 axis_destroy (g->x_axis);
1706 axis_destroy (g->y_axis);
1707 /* gtk_widget_destroy (g->drawing_area); */
1708 gtk_widget_destroy (g->gui.control_panel);
1709 gtk_widget_destroy (g->toplevel);
1710 /* gtk_widget_destroy (g->text); */
1711 gdk_gc_unref (g->fg_gc);
1712 gdk_gc_unref (g->bg_gc);
1713 gdk_font_unref (g->font);
1714 gdk_pixmap_unref (g->pixmap[0]);
1715 gdk_pixmap_unref (g->pixmap[1]);
1719 graph_segment_list_free (g);
1720 graph_element_lists_free (g);
1722 for (tmp=graphs; tmp; tmp=tmp->next)
1723 printf ("%p next: %p\n", tmp, tmp->next);
1724 printf ("p=%p, g=%p, p->next=%p, g->next=%p\n",
1725 p, g, p ? p->next : NULL, g->next);
1733 for (tmp=graphs; tmp; tmp=tmp->next)
1734 printf ("%p next: %p\n", tmp, tmp->next);
1738 /* here we collect all the external data we will ever need */
1739 static void graph_segment_list_get (struct graph *g)
1742 union wtap_pseudo_header pseudo_header;
1743 char pd[WTAP_MAX_PACKET_SIZE];
1744 struct segment *segment=NULL, *last=NULL;
1745 struct segment current;
1749 debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1750 get_headers (cfile.current_frame, cfile.pd, ¤t);
1751 if (g->type == GRAPH_THROUGHPUT)
1752 condition = COMPARE_CURR_DIR;
1754 condition = COMPARE_ANY_DIR;
1756 for (ptr=cfile.plist; ptr; ptr=ptr->next) {
1757 /* XXX - do something with "err" */
1758 wtap_seek_read (cfile.wth, ptr->file_off, &pseudo_header,
1759 pd, ptr->cap_len, &err);
1761 segment = (struct segment * )malloc (sizeof (struct segment));
1763 perror ("malloc failed");
1764 if (!get_headers (ptr, pd, segment))
1765 continue; /* not TCP over IP over Ethernet II */
1766 if (compare_headers (¤t, segment, condition)) {
1767 segment->next = NULL;
1768 segment->num = ptr->num;
1769 segment->rel_secs = ptr->rel_secs;
1770 segment->rel_usecs = ptr->rel_usecs;
1771 segment->abs_secs = ptr->abs_secs;
1772 segment->abs_usecs = ptr->abs_usecs;
1773 segment->data = ntohs (segment->iphdr.tot_len) -
1774 4*IHL(&(segment->iphdr)) - 4*DOFF(segment->tcphdr);
1776 last->next = segment;
1778 g->segments = segment;
1781 if (ptr==cfile.current_frame)
1782 g->current = segment;
1788 static int get_headers (frame_data *fd, char *pd, struct segment *hdrs)
1790 struct ether_header *e;
1791 struct ppp_header *p;
1796 * XXX - on Alpha, even fetching one-byte fields from structures
1797 * pointed to by unaligned pointers may be risky, as, unless
1798 * the BWX instructions are being used, a one-byte load is done
1799 * by loading the word containing the byte and then extracting
1802 * This means that the references to "p->ppp_type" and
1803 * "((struct iphdr *)ip)->protocol" may turn into a load of
1804 * an unaligned word.
1806 switch (fd->lnk_t) {
1808 case WTAP_ENCAP_ETHERNET:
1810 e = (struct ether_header *)pd;
1811 if (pntohs (&e->ether_type) != ETHERTYPE_IP)
1812 return FALSE; /* not IP */
1816 case WTAP_ENCAP_PPP:
1818 p = (struct ppp_header *)pd;
1819 if (p->ppp_type != PPPTYPE_IP)
1820 return FALSE; /* not IP */
1824 case WTAP_ENCAP_RAW_IP:
1830 /* Those are the only encapsulation types we handle */
1833 if (((struct iphdr *)ip)->protocol != IPPROTO_TCP) {
1834 /* printf ("transport protocol not TCP: %#1x\n", ip->protocol); */
1837 tcp = (struct tcphdr *)((guint8 *)ip + 4*IHL((struct iphdr *)ip));
1839 memcpy(&hdrs->iphdr, ip, sizeof (struct iphdr));
1840 memcpy(&hdrs->tcphdr, tcp, sizeof (struct tcphdr));
1844 static int compare_headers (struct segment *h1, struct segment *h2, int dir)
1846 if (dir == COMPARE_CURR_DIR)
1847 return h1->iphdr.saddr == h2->iphdr.saddr &&
1848 h1->iphdr.daddr == h2->iphdr.daddr &&
1849 h1->tcphdr.source == h2->tcphdr.source &&
1850 h1->tcphdr.dest == h2->tcphdr.dest;
1852 return (h1->iphdr.saddr == h2->iphdr.saddr &&
1853 h1->iphdr.daddr == h2->iphdr.daddr &&
1854 h1->tcphdr.source == h2->tcphdr.source &&
1855 h1->tcphdr.dest == h2->tcphdr.dest) ||
1856 (h1->iphdr.saddr == h2->iphdr.daddr &&
1857 h1->iphdr.daddr == h2->iphdr.saddr &&
1858 h1->tcphdr.source == h2->tcphdr.dest &&
1859 h1->tcphdr.dest == h2->tcphdr.source);
1862 static void graph_segment_list_free (struct graph *g)
1864 struct segment *segment;
1866 while (g->segments) {
1867 segment = g->segments->next;
1869 g->segments = segment;
1874 static void graph_element_lists_initialize (struct graph *g)
1876 g->elists = (struct element_list *)calloc (1, sizeof (struct element_list));
1879 static void graph_element_lists_make (struct graph *g)
1881 debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1884 case GRAPH_TSEQ_STEVENS:
1885 tseq_stevens_make_elmtlist (g);
1887 case GRAPH_TSEQ_TCPTRACE:
1888 tseq_tcptrace_make_elmtlist (g);
1890 case GRAPH_THROUGHPUT:
1891 tput_make_elmtlist (g);
1894 rtt_make_elmtlist (g);
1897 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
1902 static void graph_element_lists_free (struct graph *g)
1904 struct element_list *list, *next_list;
1907 for (list=g->elists; list; list=list->next)
1908 free (list->elements);
1909 while (g->elists->next) {
1910 list = g->elists->next->next;
1911 free (g->elists->next);
1912 g->elists->next = list;
1916 for (list=g->elists; list; list=next_list) {
1917 free (list->elements);
1918 next_list = list->next;
1921 g->elists = NULL; /* just to make debugging easier */
1924 static void graph_title_pixmap_create (struct graph *g)
1926 if (g->title_pixmap)
1927 gdk_pixmap_unref (g->title_pixmap);
1929 g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
1930 g->x_axis->p.width, g->wp.y, -1);
1933 static void graph_title_pixmap_draw (struct graph *g)
1937 gdk_draw_rectangle (g->title_pixmap, g->bg_gc, TRUE, 0, 0,
1938 g->x_axis->p.width, g->wp.y);
1939 for (i=0; g->title[i]; i++) {
1941 w = gdk_string_width (g->font, g->title[i]);
1942 h = gdk_string_height (g->font, g->title[i]);
1943 gdk_draw_string (g->title_pixmap, g->font, g->fg_gc,
1944 g->wp.width/2 - w/2, 20+h + i*(h+3), g->title[i]);
1948 static void graph_title_pixmap_display (struct graph *g)
1950 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
1951 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
1954 static void graph_pixmaps_create (struct graph *g)
1956 debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
1959 gdk_pixmap_unref (g->pixmap[0]);
1961 gdk_pixmap_unref (g->pixmap[1]);
1963 g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
1964 g->wp.width, g->wp.height, -1);
1965 g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
1966 g->wp.width, g->wp.height, -1);
1971 static void graph_display (struct graph *g)
1973 graph_pixmap_draw (g);
1974 graph_pixmaps_switch (g);
1975 graph_pixmap_display (g);
1978 static void graph_pixmap_display (struct graph *g)
1980 gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
1981 g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
1982 g->wp.width, g->wp.height);
1985 static void graph_pixmaps_switch (struct graph *g)
1987 g->displayed = 1 ^ g->displayed;
1990 static void graph_pixmap_draw (struct graph *g)
1992 struct element_list *list;
1996 debug(DBS_FENTRY) puts ("graph_display()");
1997 not_disp = 1 ^ g->displayed;
1999 gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
2000 0, 0, g->wp.width, g->wp.height);
2002 for (list=g->elists; list; list=list->next)
2003 for (e=list->elements; e->type != ELMT_NONE; e++) {
2008 draw_element_line (g, e);
2011 draw_element_arc (g, e);
2019 static void draw_element_line (struct graph *g, struct element *e)
2023 debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2024 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2025 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2026 x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2027 x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2028 y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2029 y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2040 if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
2041 (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
2042 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2046 if (x2 > g->wp.width-1)
2050 if (y2 > g->wp.height-1)
2051 y2 = g->wp.height-1;
2054 debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
2055 gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
2058 static void draw_element_arc (struct graph *g, struct element *e)
2062 x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
2063 x2 = e->p.arc.dim.width;
2064 y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
2065 y2 = e->p.arc.dim.height;
2066 if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
2068 debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
2069 gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
2070 y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
2073 static void axis_pixmaps_create (struct axis *axis)
2075 debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2076 if (axis->pixmap[0])
2077 gdk_pixmap_unref (axis->pixmap[0]);
2078 if (axis->pixmap[1])
2079 gdk_pixmap_unref (axis->pixmap[1]);
2081 axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
2082 axis->p.width, axis->p.height, -1);
2083 axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
2084 axis->p.width, axis->p.height, -1);
2086 axis->displayed = 0;
2089 static void axis_destroy (struct axis *axis)
2091 gdk_pixmap_unref (axis->pixmap[0]);
2092 gdk_pixmap_unref (axis->pixmap[1]);
2096 static void axis_display (struct axis *axis)
2098 if (axis->flags & AXIS_ORIENTATION)
2099 h_axis_pixmap_draw (axis);
2101 v_axis_pixmap_draw (axis);
2102 axis_pixmaps_switch (axis);
2103 axis_pixmap_display (axis);
2106 static void v_axis_pixmap_draw (struct axis *axis)
2108 struct graph *g = axis->g;
2111 int not_disp, rdigits, offset, imin, imax;
2112 double bottom, top, j, fl, corr;
2114 debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2115 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2116 (double )g->geom.height * g->bounds.height;
2117 bottom += axis->min;
2118 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2119 (double )g->geom.height * g->bounds.height;
2121 axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2123 j = axis->major - floor (axis->major);
2124 for (rdigits=0; rdigits<=6; rdigits++) {
2131 not_disp = 1 ^ axis->displayed;
2132 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2133 axis->p.width, axis->p.height);
2135 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
2136 (axis->p.height-axis->s.height)/2.0, axis->s.width - 1,
2139 offset = g->wp.y + (-g->geom.y);
2140 fl = floor (axis->min / axis->major) * axis->major;
2141 corr = rint ((axis->min - fl) * g->zoom.y);
2144 major_tick = axis->major * g->zoom.y;
2145 imin = (g->geom.height - offset + corr - g->wp.height) / major_tick + 1;
2146 imax = (g->geom.height - offset + corr) / major_tick;
2147 for (i=imin; i <= imax; i++) {
2150 int y = g->geom.height-1 - (int )rint (i * major_tick) -
2151 offset + corr + axis->s.y;
2153 debug(DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->major + fl, y);
2154 if (y < 0 || y > axis->p.height)
2156 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2157 axis->s.width - 15, y, axis->s.width - 1, y);
2158 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2159 w = gdk_string_width (g->font, desc);
2160 h = gdk_string_height (g->font, desc);
2161 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2162 axis->s.width-15-4-w, y + h/2, desc);
2166 double minor_tick = axis->minor * g->zoom.y;
2167 imin = (g->geom.height - offset + corr - g->wp.height)/minor_tick + 1;
2168 imax = (g->geom.height - offset + corr) / minor_tick;
2169 for (i=imin; i <= imax; i++) {
2170 int y = g->geom.height-1 - (int )rint (i*minor_tick) -
2171 offset + corr + axis->s.y;
2173 debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2174 if (y > 0 && y < axis->p.height)
2175 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
2176 axis->s.width - 8, y, axis->s.width - 1, y);
2179 for (i=0; axis->label[i]; i++) {
2181 w = gdk_string_width (g->font, axis->label[i]);
2182 h = gdk_string_height (g->font, axis->label[i]);
2183 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2184 (axis->p.width - w)/2 , TITLEBAR_HEIGHT-15 - i*(h+3),
2189 static void h_axis_pixmap_draw (struct axis *axis)
2191 struct graph *g = axis->g;
2193 double major_tick, minor_tick;
2194 int not_disp, rdigits, offset, imin, imax;
2195 double left, right, j, fl, corr;
2197 debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2198 left = (g->wp.x-g->geom.x) /
2199 (double )g->geom.width * g->bounds.width;
2201 right = (g->wp.x-g->geom.x+g->wp.width) /
2202 (double )g->geom.width * g->bounds.width;
2204 axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2206 j = axis->major - floor (axis->major);
2207 for (rdigits=0; rdigits<=6; rdigits++) {
2214 not_disp = 1 ^ axis->displayed;
2215 gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
2216 axis->p.width, axis->p.height);
2218 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
2219 axis->s.width + (axis->p.width-axis->s.width)/2.0, 0);
2220 offset = g->wp.x - g->geom.x;
2222 fl = floor (axis->min / axis->major) * axis->major;
2223 corr = rint ((axis->min - fl) * g->zoom.x);
2226 major_tick = axis->major*g->zoom.x;
2227 imin = (offset + corr) / major_tick + 1;
2228 imax = (offset + corr + axis->s.width) / major_tick;
2229 for (i=imin; i <= imax; i++) {
2232 int x = (int )rint (i * major_tick) - offset - corr;
2234 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2235 if (x < 0 || x > axis->s.width)
2237 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
2238 snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
2239 w = gdk_string_width (g->font, desc);
2240 h = gdk_string_height (g->font, desc);
2241 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2242 x - w/2, 15+h+4, desc);
2244 if (axis->minor > 0) {
2246 minor_tick = axis->minor*g->zoom.x;
2247 imin = (offset + corr) / minor_tick + 1;
2248 imax = (offset + corr + g->wp.width) / minor_tick;
2249 for (i=imin; i <= imax; i++) {
2250 int x = (int )rint (i * minor_tick) - offset - corr;
2251 if (x > 0 && x < axis->s.width)
2252 gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
2255 for (i=0; axis->label[i]; i++) {
2257 w = gdk_string_width (g->font, axis->label[i]);
2258 h = gdk_string_height (g->font, axis->label[i]);
2259 gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
2260 axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
2265 static void axis_pixmaps_switch (struct axis *axis)
2267 axis->displayed = 1 ^ axis->displayed;
2270 static void axis_pixmap_display (struct axis *axis)
2272 gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
2273 axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
2274 axis->p.width, axis->p.height);
2277 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2279 int i, j, ii, jj, ms;
2280 double zoom, x, steps[3]={ 0.1, 0.5 };
2281 int dim, check_needed, diminished;
2282 double majthresh[2]={2.0, 3.0};
2284 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2285 debug(DBS_AXES_TICKS)
2286 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2288 zoom = axis_zoom_get (axis, dir);
2290 for (i=-9; i<=12; i++) {
2291 if (x / pow (10, i) < 1)
2295 ms = (int )(x / pow (10, i));
2305 axis->major = steps[j] * pow (10, i);
2307 debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2308 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2310 /* let's compute minor ticks */
2313 axis_ticks_down (&ii, &jj);
2314 axis->minor = steps[jj] * pow (10, ii);
2315 /* we don't want minors if they would be less than 10 pixels apart */
2316 if (axis->minor*zoom < 10) {
2317 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2318 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2322 check_needed = TRUE;
2324 while (check_needed) {
2325 check_needed = FALSE;
2326 dim = get_label_dim (axis, dir, xmax);
2327 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2328 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2329 axis->major, axis->minor, axis->major*zoom/dim,
2330 axis->minor*zoom/dim);
2332 /* corrections: if majors are less than majthresh[dir] times label
2333 * dimension apart, we need to use bigger ones */
2334 if (axis->major*zoom / dim < majthresh[dir]) {
2335 axis_ticks_up (&ii, &jj);
2336 axis->minor = axis->major;
2337 axis_ticks_up (&i, &j);
2338 axis->major = steps[j] * pow (10, i);
2339 check_needed = TRUE;
2340 debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2343 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2344 * we could promote them to majors as well */
2345 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2346 axis_ticks_down (&i, &j);
2347 axis->major = axis->minor;
2348 axis_ticks_down (&ii, &jj);
2349 axis->minor = steps[jj] * pow (10, ii);
2350 check_needed = TRUE;
2353 debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2356 if (axis->minor*zoom < 10) {
2357 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2358 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2364 debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2365 "axis->minor == %.1f\n", axis->major, axis->minor);
2368 static void axis_ticks_up (int *i, int *j)
2377 static void axis_ticks_down (int *i, int *j)
2386 static int get_label_dim (struct axis *axis, int dir, double label)
2392 /* First, let's compute how many digits to the right of radix
2393 * we need to print */
2394 y = axis->major - floor (axis->major);
2395 for (rdigits=0; rdigits<=6; rdigits++) {
2401 snprintf (str, 32, "%.*f", rdigits, label);
2403 case AXIS_HORIZONTAL:
2404 dim = gdk_string_width (axis->g->font, str);
2407 dim = gdk_string_height (axis->g->font, str);
2410 puts ("initialize axis: an axis must be either horizontal or vertical");
2417 static double axis_zoom_get (struct axis *axis, int dir)
2420 case AXIS_HORIZONTAL:
2421 return axis->g->zoom.x;
2424 return axis->g->zoom.y;
2432 static void graph_select_segment (struct graph *g, int x, int y)
2434 struct element_list *list;
2437 debug(DBS_FENTRY) puts ("graph_select_segment()");
2440 y = g->geom.height-1 - (y - g->geom.y);
2442 for (list=g->elists; list; list=list->next)
2443 for (e=list->elements; e->type != ELMT_NONE; e++) {
2448 if (line_detect_collision (e, x, y)) {
2449 int row = e->parent->num - 1;
2450 update_packet_list (row);
2454 if (arc_detect_collision (e, x, y)) {
2455 int row = e->parent->num - 1;
2456 update_packet_list (row);
2465 static int line_detect_collision (struct element *e, int x, int y)
2469 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2470 x1 = (int )rint (e->p.line.dim.x1);
2471 x2 = (int )rint (e->p.line.dim.x2);
2473 x1 = (int )rint (e->p.line.dim.x2);
2474 x2 = (int )rint (e->p.line.dim.x1);
2476 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2477 y1 = (int )rint (e->p.line.dim.y1);
2478 y2 = (int )rint (e->p.line.dim.y2);
2480 y1 = (int )rint (e->p.line.dim.y2);
2481 y2 = (int )rint (e->p.line.dim.y1);
2484 printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2486 if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
2492 static int arc_detect_collision (struct element *e, int x, int y)
2496 x1 = (int )rint (e->p.arc.dim.x);
2497 x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
2498 y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
2499 y2 = (int )rint (e->p.arc.dim.y);
2501 printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
2503 if (x1<=x && x<=x2 && y1<=y && y<=y2)
2509 static void update_packet_list (int row)
2511 select_packet (&cfile, row);
2512 if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) !=
2513 GTK_VISIBILITY_FULL)
2514 gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.5, 0.5);
2515 GTK_CLIST(packet_list)->focus_row = row;
2516 gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
2519 static void cross_xor (struct graph *g, int x, int y)
2521 if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2522 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2523 gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
2524 y, g->wp.x + g->wp.width, y);
2525 gdk_draw_line (g->drawing_area->window, xor_gc, x,
2526 g->wp.y, x, g->wp.y + g->wp.height);
2530 static void cross_draw (struct graph *g, int x, int y)
2532 cross_xor (g, x, y);
2535 g->cross.erase_needed = 1;
2538 static void cross_erase (struct graph *g)
2540 cross_xor (g, g->cross.x, g->cross.y);
2541 g->cross.erase_needed = 0;
2544 static void magnify_create (struct graph *g, int x, int y)
2547 struct element_list *list, *new_list;
2548 struct ipoint pos, offsetpos;
2551 mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph));
2552 memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2554 mg->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
2555 gtk_signal_connect (GTK_OBJECT (mg->toplevel), "realize",
2556 GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
2557 mg->drawing_area = mg->toplevel;
2558 gtk_widget_set_usize (mg->toplevel, g->magnify.width, g->magnify.height);
2559 gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2560 /* | GDK_ENTER_NOTIFY_MASK */
2561 /* | GDK_ALL_EVENTS_MASK */
2566 mg->wp.width = g->magnify.width;
2567 mg->wp.height = g->magnify.height;
2568 mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2569 mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2570 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2571 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2573 /* in order to keep original element lists intact we need our own */
2574 graph_element_lists_initialize (mg);
2575 list = g->elists->next;
2576 new_list = mg->elists;
2577 for ( ; list; list=list->next) {
2579 (struct element_list * )malloc (sizeof (struct element_list));
2580 new_list = new_list->next;
2581 new_list->next = NULL;
2582 new_list->elements = NULL;
2584 graph_element_lists_make (mg);
2586 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2587 g->magnify.x = pos.x + x - g->magnify.width/2;
2588 g->magnify.y = pos.y + y - g->magnify.height/2;
2589 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2590 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2591 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2592 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2593 gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
2594 magnify_get_geom (g, x, y);
2596 gtk_widget_show (mg->drawing_area);
2598 /* we need to wait for the first expose event before we start drawing */
2599 while (!gdk_events_pending ());
2601 e = gdk_event_get ();
2603 if (e->any.type == GDK_EXPOSE) {
2611 mg->pixmap[0] = mg->pixmap[1] = NULL;
2612 graph_pixmaps_create (mg);
2614 g->magnify.active = 1;
2617 static void magnify_move (struct graph *g, int x, int y)
2619 struct ipoint pos, offsetpos;
2621 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
2622 g->magnify.x = pos.x + x - g->magnify.width/2;
2623 g->magnify.y = pos.y + y - g->magnify.height/2;
2624 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2625 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2626 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2627 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2628 magnify_get_geom (g, x, y);
2629 gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
2634 static void magnify_destroy (struct graph *g)
2636 struct element_list *list;
2637 struct graph *mg = g->magnify.g;
2639 gtk_widget_destroy (GTK_WIDGET (mg->drawing_area));
2640 gdk_pixmap_unref (mg->pixmap[0]);
2641 gdk_pixmap_unref (mg->pixmap[1]);
2642 for (list=mg->elists; list; list=list->next)
2643 free (list->elements);
2644 while (mg->elists->next) {
2645 list = mg->elists->next->next;
2646 free (mg->elists->next);
2647 mg->elists->next = list;
2649 free (g->magnify.g);
2650 g->magnify.active = 0;
2653 static void magnify_get_geom (struct graph *g, int x, int y)
2657 gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
2659 g->magnify.g->geom.x = g->geom.x;
2660 g->magnify.g->geom.y = g->geom.y;
2662 g->magnify.g->geom.x -=
2663 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
2664 ((x-g->geom.x)/(double )g->geom.width));
2665 g->magnify.g->geom.y -=
2666 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
2667 ((y-g->geom.y)/(double )g->geom.height));
2669 /* we have coords of origin of graph relative to origin of g->toplevel.
2670 * now we need them to relate to origin of magnify window */
2671 g->magnify.g->geom.x -= (g->magnify.x - posx);
2672 g->magnify.g->geom.y -= (g->magnify.y - posy);
2675 static void magnify_draw (struct graph *g)
2677 int not_disp = 1 ^ g->magnify.g->displayed;
2679 graph_pixmap_draw (g->magnify.g);
2680 /* graph pixmap is almost ready, just add border */
2681 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2682 g->magnify.width - 1, 0);
2683 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
2684 g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
2685 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
2686 0, g->magnify.height - 1);
2687 gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
2688 g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
2690 graph_pixmaps_switch (g->magnify.g);
2691 graph_pixmap_display (g->magnify.g);
2695 static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
2701 int cur_g_width, cur_g_height;
2702 int cur_wp_width, cur_wp_height;
2704 debug(DBS_FENTRY) puts ("configure_event()");
2706 for (g=graphs; g; g=g->next)
2707 if (g->drawing_area == widget)
2710 cur_wp_width = g->wp.width;
2711 cur_wp_height = g->wp.height;
2712 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2713 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2714 g->x_axis->s.width = g->wp.width;
2715 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2716 g->y_axis->p.height = g->wp.height + g->wp.y;
2717 g->y_axis->s.height = g->wp.height;
2718 g->x_axis->p.y = g->y_axis->p.height;
2719 zoom.x = (double )g->wp.width / cur_wp_width;
2720 zoom.y = (double )g->wp.height / cur_wp_height;
2721 cur_g_width = g->geom.width;
2722 cur_g_height = g->geom.height;
2723 g->geom.width = (int )rint (g->geom.width * zoom.x);
2724 g->geom.height = (int )rint (g->geom.height * zoom.y);
2725 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2726 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2727 /* g->zoom.initial.x = g->zoom.x; */
2728 /* g->zoom.initial.y = g->zoom.y; */
2730 g->geom.x = g->wp.x - (double )g->geom.width/cur_g_width *
2731 (g->wp.x - g->geom.x);
2732 g->geom.y = g->wp.y - (double )g->geom.height/cur_g_height *
2733 (g->wp.y - g->geom.y);
2735 printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
2736 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2737 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2738 g->zoom.x, g->zoom.y);
2741 update_zoom_spins (g);
2742 graph_element_lists_make (g);
2743 graph_pixmaps_create (g);
2744 graph_title_pixmap_create (g);
2745 axis_pixmaps_create (g->y_axis);
2746 axis_pixmaps_create (g->x_axis);
2747 /* we don't do actual drawing here; we leave it to expose handler */
2748 graph_pixmap_draw (g);
2749 graph_pixmaps_switch (g);
2750 graph_title_pixmap_draw (g);
2751 h_axis_pixmap_draw (g->x_axis);
2752 axis_pixmaps_switch (g->x_axis);
2753 v_axis_pixmap_draw (g->y_axis);
2754 axis_pixmaps_switch (g->y_axis);
2758 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
2762 debug(DBS_FENTRY) puts ("expose_event()");
2767 for (g=graphs; g; g=g->next)
2768 if (g->drawing_area == widget)
2771 /* lower left corner */
2772 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
2773 g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2775 gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
2776 g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2778 graph_pixmap_display (g);
2779 graph_title_pixmap_display (g);
2780 axis_pixmap_display (g->x_axis);
2781 axis_pixmap_display (g->y_axis);
2786 static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
2790 debug(DBS_FENTRY) puts ("button_press_event()");
2792 for (g=graphs; g; g=g->next)
2793 if (g->drawing_area == widget)
2796 if (event->button == 3) {
2797 if (event->state & GDK_CONTROL_MASK)
2798 magnify_create (g, (int )rint (event->x), (int )rint (event->y));
2800 g->grab.x = (int )rint (event->x) - g->geom.x;
2801 g->grab.y = (int )rint (event->y) - g->geom.y;
2802 g->grab.grabbed = TRUE;
2805 /* Windows mouse control: */
2806 /* [<ctrl>-left] - select packet */
2807 /* [left] - zoom in */
2808 /* [<shift>-left] - zoom out */
2809 } else if (event->button == 1) {
2810 if (event->state & GDK_CONTROL_MASK) {
2811 graph_select_segment (g, (int)event->x, (int)event->y);
2814 } else if (event->button == 2) {
2816 int cur_width = g->geom.width, cur_height = g->geom.height;
2817 struct { double x, y; } factor;
2819 if (g->zoom.flags & ZOOM_OUT) {
2820 if (g->zoom.flags & ZOOM_HLOCK)
2823 factor.x = 1 / g->zoom.step_x;
2824 if (g->zoom.flags & ZOOM_VLOCK)
2827 factor.y = 1 / g->zoom.step_y;
2829 if (g->zoom.flags & ZOOM_HLOCK)
2832 factor.x = g->zoom.step_x;
2833 if (g->zoom.flags & ZOOM_VLOCK)
2836 factor.y = g->zoom.step_y;
2839 g->geom.width = (int )rint (g->geom.width * factor.x);
2840 g->geom.height = (int )rint (g->geom.height * factor.y);
2841 if (g->geom.width < g->wp.width)
2842 g->geom.width = g->wp.width;
2843 if (g->geom.height < g->wp.height)
2844 g->geom.height = g->wp.height;
2845 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2846 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2848 g->geom.x -= (int )rint ((g->geom.width - cur_width) *
2849 ((event->x-g->geom.x)/(double )cur_width));
2850 g->geom.y -= (int )rint ((g->geom.height - cur_height) *
2851 ((event->y-g->geom.y)/(double )cur_height));
2853 if (g->geom.x > g->wp.x)
2854 g->geom.x = g->wp.x;
2855 if (g->geom.y > g->wp.y)
2856 g->geom.y = g->wp.y;
2857 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2858 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2859 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2860 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2862 printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2863 "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
2864 g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
2865 g->wp.height, g->zoom.x, g->zoom.y);
2867 graph_element_lists_make (g);
2869 axis_display (g->y_axis);
2870 axis_display (g->x_axis);
2871 update_zoom_spins (g);
2873 cross_draw (g, event->x, event->y);
2875 } else if (event->button == 1) {
2876 graph_select_segment (g, (int )event->x, (int )event->y);
2884 static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
2888 GdkModifierType state;
2890 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
2892 for (g=graphs; g; g=g->next)
2893 if (g->drawing_area == widget)
2897 gdk_window_get_pointer (event->window, &x, &y, &state);
2901 state = event->state;
2904 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
2905 * is pressed while pointer is in motion, we will receive one more motion
2906 * notify *before* we get the button press. This last motion notify works
2907 * with stale grab coordinates */
2908 if (state & GDK_BUTTON3_MASK) {
2909 if (g->grab.grabbed) {
2910 g->geom.x = x-g->grab.x;
2911 g->geom.y = y-g->grab.y;
2913 if (g->geom.x > g->wp.x)
2914 g->geom.x = g->wp.x;
2915 if (g->geom.y > g->wp.y)
2916 g->geom.y = g->wp.y;
2917 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2918 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2919 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2920 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
2922 axis_display (g->y_axis);
2923 axis_display (g->x_axis);
2925 cross_draw (g, x, y);
2926 } else if (g->magnify.active)
2927 magnify_move (g, x, y);
2928 } else if (state & GDK_BUTTON1_MASK) {
2929 graph_select_segment (g, x, y);
2930 if (g->cross.erase_needed)
2933 cross_draw (g, x, y);
2935 if (g->cross.erase_needed)
2938 cross_draw (g, x, y);
2944 static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
2948 debug(DBS_FENTRY) puts ("button_release_event()");
2950 for (g=graphs; g; g=g->next)
2951 if (g->drawing_area == widget)
2954 if (event->button == 3)
2955 g->grab.grabbed = FALSE;
2957 if (g->magnify.active)
2958 magnify_destroy (g);
2962 static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
2966 debug(DBS_FENTRY) puts ("key_press_event()");
2968 for (g=graphs; g; g=g->next)
2969 if (g->toplevel == widget)
2972 if (event->keyval == 32 /*space*/) {
2975 if (g->cross.draw) {
2977 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
2979 } else if (g->cross.erase_needed) {
2983 /* toggle buttons emit their "toggled" signals so don't bother doing
2984 * any real work here, it will be done in signal handlers */
2986 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
2988 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
2989 } else if (event->keyval == 't')
2990 toggle_time_origin (g);
2991 else if (event->keyval == 's')
2992 toggle_seq_origin (g);
2993 else if (event->keyval == GDK_Shift_L) {
2994 /* g->zoom.flags |= ZOOM_OUT; */
2995 gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3000 static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
3004 debug(DBS_FENTRY) puts ("key_release_event()");
3006 for (g=graphs; g; g=g->next)
3007 if (g->toplevel == widget)
3010 if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3011 /* g->zoom.flags &= ~ZOOM_OUT; */
3012 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3017 static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3021 for (g=graphs; g; g=g->next)
3022 if (g->drawing_area == widget)
3025 if (g->cross.erase_needed)
3031 static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
3035 for (g=graphs; g; g=g->next)
3036 if (g->drawing_area == widget)
3039 /* graph_pixmap_display (g); */
3040 if (g->cross.draw) {
3042 gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
3043 cross_draw (g, x, y);
3048 static void toggle_time_origin (struct graph *g)
3051 case GRAPH_TSEQ_STEVENS:
3052 tseq_stevens_toggle_time_origin (g);
3054 case GRAPH_TSEQ_TCPTRACE:
3055 tseq_tcptrace_toggle_time_origin (g);
3057 case GRAPH_THROUGHPUT:
3058 tput_toggle_time_origin (g);
3063 axis_display (g->x_axis);
3066 static void toggle_seq_origin (struct graph *g)
3069 case GRAPH_TSEQ_STEVENS:
3070 tseq_stevens_toggle_seq_origin (g);
3071 axis_display (g->y_axis);
3073 case GRAPH_TSEQ_TCPTRACE:
3074 tseq_tcptrace_toggle_seq_origin (g);
3075 axis_display (g->y_axis);
3078 rtt_toggle_seq_origin (g);
3079 axis_display (g->x_axis);
3086 static int get_num_dsegs (struct graph *g)
3089 struct segment *tmp;
3091 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3092 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3099 static int get_num_acks (struct graph *g)
3102 struct segment *tmp;
3104 for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3105 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3113 * Stevens-style time-sequence grapH
3116 static void tseq_stevens_read_config (struct graph *g)
3118 debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3120 g->s.tseq_stevens.seq_width = 4;
3121 g->s.tseq_stevens.seq_height = 4;
3122 g->s.tseq_stevens.flags = 0;
3124 g->title = (char ** )malloc (2 * sizeof (char *));
3125 g->title[0] = "Time/Sequence Graph";
3127 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3128 g->y_axis->label[0] = "number[B]";
3129 g->y_axis->label[1] = "Sequence";
3130 g->y_axis->label[2] = NULL;
3131 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3132 g->x_axis->label[0] = "Time[s]";
3133 g->x_axis->label[1] = NULL;
3136 static void tseq_stevens_initialize (struct graph *g)
3138 debug(DBS_FENTRY) puts ("tseq_stevens_initialize()");
3139 tseq_stevens_get_bounds (g);
3145 case GRAPH_TSEQ_STEVENS:
3146 tseq_stevens_read_config(g);
3148 case GRAPH_TSEQ_TCPTRACE:
3149 tseq_tcptrace_read_config(g);
3154 static void tseq_stevens_get_bounds (struct graph *g)
3156 struct segment *tmp, *last, *first;
3157 double t0, tmax, y0, ymax;
3159 for (first=g->segments; first->next; first=first->next) {
3160 if (compare_headers (g->current, first, COMPARE_CURR_DIR))
3165 for (tmp=g->segments; tmp; tmp=tmp->next) {
3166 unsigned int highest_byte_num;
3168 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3169 highest_byte_num = ntohl (tmp->tcphdr.seq) + tmp->data;
3171 highest_byte_num = ntohl (tmp->tcphdr.ack_seq);
3172 if (highest_byte_num > ymax)
3173 ymax = highest_byte_num;
3176 puts ("tseq_stevens_get_bounds: segment list corrupted!");
3180 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3181 tmax = last->rel_secs + last->rel_usecs / 1000000.0;
3182 y0 = ntohl (first->tcphdr.seq);
3186 g->bounds.width = tmax - t0;
3187 g->bounds.height = ymax - y0;
3188 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3189 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3192 static void tseq_stevens_make_elmtlist (struct graph *g)
3194 struct segment *tmp;
3195 struct element *elements, *e;
3196 double x0 = g->bounds.x0, y0 = g->bounds.y0;
3198 debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3199 if (g->elists->elements == NULL) {
3200 int n = 1 + get_num_dsegs (g);
3201 e = elements = (struct element * )malloc (n*sizeof (struct element));
3203 e = elements = g->elists->elements;
3205 for (tmp=g->segments; tmp; tmp=tmp->next) {
3208 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3211 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
3212 seqno = g->zoom.y * (ntohl (tmp->tcphdr.seq) - y0);
3217 e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
3218 e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
3219 e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3220 e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3221 e->p.arc.filled = TRUE;
3222 e->p.arc.angle1 = 0;
3223 e->p.arc.angle2 = 23040;
3226 e->type = ELMT_NONE;
3227 g->elists->elements = elements;
3230 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3232 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3234 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3235 g->y_axis->min = g->bounds.y0;
3236 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3240 static void tseq_stevens_toggle_time_origin (struct graph *g)
3242 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3244 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3245 g->x_axis->min = g->bounds.x0;
3246 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3251 * tcptrace-style time-sequence graph
3254 static void tseq_tcptrace_read_config (struct graph *g)
3256 GdkColormap *colormap;
3259 g->s.tseq_tcptrace.flags = 0;
3260 g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
3261 g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
3262 g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
3263 colormap = gdk_window_get_colormap (g->drawing_area->window);
3264 gdk_color_parse ("black", &color);
3265 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3266 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
3267 gdk_color_parse ("LightSlateGray", &color);
3268 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3269 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
3270 gdk_color_parse ("LightGray", &color);
3271 gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
3272 gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
3274 g->elists->next = (struct element_list * )
3275 malloc (sizeof (struct element_list));
3276 g->elists->next->next = NULL;
3277 g->elists->next->elements = NULL;
3279 g->title = (char ** )malloc (2 * sizeof (char *));
3280 g->title[0] = "Time/Sequence Graph";
3282 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3283 g->y_axis->label[0] = "number[B]";
3284 g->y_axis->label[1] = "Sequence";
3285 g->y_axis->label[2] = NULL;
3286 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3287 g->x_axis->label[0] = "Time[s]";
3288 g->x_axis->label[1] = NULL;
3291 static void tseq_tcptrace_make_elmtlist (struct graph *g)
3293 struct segment *tmp;
3294 struct element *elements0, *e0; /* list of elmts with prio 0 */
3295 struct element *elements1, *e1; /* list of elmts with prio 1 */
3297 double p_t; /* ackno, window and time of previous segment */
3298 double p_ackno, p_win;
3301 debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
3303 if (g->elists->elements == NULL) {
3304 int n = 1 + 4*get_num_acks(g);
3305 e0 = elements0 = (struct element * )malloc (n*sizeof (struct element));
3307 e0 = elements0 = g->elists->elements;
3309 if (g->elists->next->elements == NULL ) {
3310 int n = 1 + 3*get_num_dsegs(g);
3311 e1 = elements1 = (struct element * )malloc (n*sizeof (struct element));
3313 e1 = elements1 = g->elists->next->elements;
3317 /* initialize "previous" values */
3318 for (tmp=g->segments; tmp; tmp=tmp->next)
3319 if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
3322 p_ackno = (unsigned int )(g->zoom.y * (ntohl (tmp->tcphdr.ack_seq) - y0));
3325 p_win = g->zoom.y * ntohs (tmp->tcphdr.window);
3326 p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0;
3327 for (tmp=g->segments; tmp; tmp=tmp->next) {
3328 double secs, seqno, data;
3331 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3334 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3335 /* forward direction -> we need seqno and amount of data */
3338 seqno = ntohl (tmp->tcphdr.seq);
3339 if (TCP_SYN (tmp->tcphdr))
3344 y1 = g->zoom.y * (seqno - y0);
3345 y2 = g->zoom.y * (seqno - y0 + data);
3346 e1->type = ELMT_LINE;
3348 e1->gc = g->s.tseq_tcptrace.gc_seq;
3349 e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
3350 e1->p.line.dim.y1 = y1;
3351 e1->p.line.dim.y2 = y2;
3353 e1->type = ELMT_LINE;
3355 e1->gc = g->s.tseq_tcptrace.gc_seq;
3356 e1->p.line.dim.x1 = x - 1;
3357 e1->p.line.dim.x2 = x + 1;
3358 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
3360 e1->type = ELMT_LINE;
3362 e1->gc = g->s.tseq_tcptrace.gc_seq;
3363 e1->p.line.dim.x1 = x + 1;
3364 e1->p.line.dim.x2 = x - 1;
3365 e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
3369 if (TCP_SYN (tmp->tcphdr) && ! TCP_ACK (tmp->tcphdr))
3370 /* SYN's have ACK==0 and are useless here */
3372 /* backward direction -> we need ackno and window */
3373 ackno = (ntohl (tmp->tcphdr.ack_seq) - y0) * g->zoom.y;
3374 win = ntohs (tmp->tcphdr.window) * g->zoom.y;
3377 e0->type = ELMT_LINE;
3379 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3380 e0->p.line.dim.x1 = p_t;
3381 e0->p.line.dim.y1 = p_ackno;
3382 e0->p.line.dim.x2 = x;
3383 e0->p.line.dim.y2 = p_ackno;
3385 e0->type = ELMT_LINE;
3387 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3388 e0->p.line.dim.x1 = x;
3389 e0->p.line.dim.y1 = p_ackno;
3390 e0->p.line.dim.x2 = x;
3391 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
3394 e0->type = ELMT_LINE;
3396 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3397 e0->p.line.dim.x1 = p_t;
3398 e0->p.line.dim.y1 = p_win + p_ackno;
3399 e0->p.line.dim.x2 = x;
3400 e0->p.line.dim.y2 = p_win + p_ackno;
3402 e0->type = ELMT_LINE;
3404 e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
3405 e0->p.line.dim.x1 = x;
3406 e0->p.line.dim.y1 = p_win + p_ackno;
3407 e0->p.line.dim.x2 = x;
3408 e0->p.line.dim.y2 = win + ackno;
3416 e0->type = ELMT_NONE;
3417 e1->type = ELMT_NONE;
3418 g->elists->elements = elements0;
3419 g->elists->next->elements = elements1;
3422 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
3424 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
3426 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3427 g->y_axis->min = g->bounds.y0;
3428 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3432 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
3434 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
3436 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3437 g->x_axis->min = g->bounds.x0;
3438 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3446 static void tput_make_elmtlist (struct graph *g)
3448 struct segment *tmp, *oldest;
3449 struct element *elements, *e;
3453 if (g->elists->elements == NULL) {
3454 int n = 1 + get_num_dsegs (g);
3455 e = elements = (struct element * )malloc (n*sizeof (struct element));
3457 e = elements = g->elists->elements;
3459 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3460 double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
3461 dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3462 if (i>g->s.tput.nsegs) {
3463 sum -= oldest->data;
3464 oldest=oldest->next;
3468 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
3473 e->p.arc.dim.width = g->s.tput.width;
3474 e->p.arc.dim.height = g->s.tput.height;
3475 e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
3476 e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
3477 e->p.arc.filled = TRUE;
3478 e->p.arc.angle1 = 0;
3479 e->p.arc.angle2 = 23040;
3482 e->type = ELMT_NONE;
3483 g->elists->elements = elements;
3486 /* Purpose of <graph_type>_initialize functions:
3487 * - find maximum and minimum for both axes
3488 * - call setup routine for style struct */
3489 static void tput_initialize (struct graph *g)
3491 struct segment *tmp, *oldest, *last;
3493 double dtime, tput, tputmax=0;
3494 double t0, tmax, y0, ymax;
3496 debug(DBS_FENTRY) puts ("tput_initialize()");
3498 tput_read_config(g);
3500 for (last=g->segments; last->next; last=last->next);
3501 for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
3502 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
3503 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
3504 if (i>g->s.tput.nsegs) {
3505 sum -= oldest->data;
3506 oldest=oldest->next;
3510 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
3515 t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
3516 tmax = last->rel_secs + last->rel_usecs / 1000000.0;
3522 g->bounds.width = tmax - t0;
3523 g->bounds.height = ymax - y0;
3524 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3525 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3528 static void tput_read_config (struct graph *g)
3530 debug(DBS_FENTRY) puts ("tput_read_config()");
3532 g->s.tput.width = 4;
3533 g->s.tput.height = 4;
3534 g->s.tput.nsegs = 20;
3536 g->title = (char ** )malloc (2 * sizeof (char *));
3537 g->title[0] = "Throughput Graph";
3539 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3540 g->y_axis->label[0] = "[B/s]";
3541 g->y_axis->label[1] = "Throughput";
3542 g->y_axis->label[2] = NULL;
3543 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3544 g->x_axis->label[0] = "Time[s]";
3545 g->x_axis->label[1] = NULL;
3546 g->s.tput.flags = 0;
3549 static void tput_toggle_time_origin (struct graph *g)
3551 g->s.tput.flags ^= TIME_ORIGIN;
3553 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3554 g->x_axis->min = g->bounds.x0;
3555 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3561 static void rtt_read_config (struct graph *g)
3563 debug(DBS_FENTRY) puts ("rtt_read_config()");
3566 g->s.rtt.height = 4;
3569 g->title = (char ** )malloc (2 * sizeof (char *));
3570 g->title[0] = "Round Trip Time Graph";
3572 g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
3573 g->y_axis->label[0] = "RTT [s]";
3574 g->y_axis->label[1] = NULL;
3575 g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
3576 g->x_axis->label[0] = "Sequence Number[B]";
3577 g->x_axis->label[1] = NULL;
3580 static void rtt_initialize (struct graph *g)
3582 struct segment *tmp, *first=NULL;
3583 struct unack *unack = NULL, *u;
3585 double x0, xmax=0, y0, ymax;
3587 debug(DBS_FENTRY) puts ("rtt_initialize()");
3589 rtt_read_config (g);
3591 for (tmp=g->segments; tmp; tmp=tmp->next) {
3592 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3593 unsigned int seqno = ntohl (tmp->tcphdr.seq);
3598 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3599 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3600 u = rtt_get_new_unack (time, seqno);
3602 rtt_put_unack_on_list (&unack, u);
3605 if (seqno + tmp->data > xmax)
3606 xmax = seqno + tmp->data;
3608 unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
3609 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3612 for (u=unack; u; u=v)
3613 if (ackno > u->seqno) {
3614 double rtt = time - u->time;
3618 rtt_delete_unack_from_list (&unack, u);
3624 x0 = ntohl (first->tcphdr.seq);
3630 g->bounds.width = xmax - x0;
3631 g->bounds.height = ymax - y0;
3632 g->zoom.x = g->geom.width / g->bounds.width;
3633 g->zoom.y = g->geom.height / g->bounds.height;
3636 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
3640 for (u=list; u; u=u->next)
3641 if (u->seqno == seqno)
3647 static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
3651 u = (struct unack * )malloc (sizeof (struct unack));
3660 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
3662 struct unack *u, *list = *l;
3664 for (u=list; u; u=u->next)
3674 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
3676 struct unack *u, *list = *l;
3685 for (u=list; u; u=u->next)
3686 if (u->next == dead) {
3687 u->next = u->next->next;
3693 static void rtt_make_elmtlist (struct graph *g)
3695 struct segment *tmp;
3696 struct unack *unack = NULL, *u;
3697 struct element *elements, *e;
3699 debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
3701 if (g->elists->elements == NULL) {
3702 int n = 1 + get_num_dsegs (g);
3703 e = elements = (struct element * )malloc (n*sizeof (struct element));
3705 e = elements = g->elists->elements;
3707 for (tmp=g->segments; tmp; tmp=tmp->next) {
3708 if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
3709 unsigned int seqno = ntohl (tmp->tcphdr.seq);
3711 if (tmp->data && !rtt_is_retrans (unack, seqno)) {
3712 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3713 u = rtt_get_new_unack (time, seqno);
3715 rtt_put_unack_on_list (&unack, u);
3718 unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
3719 double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3722 for (u=unack; u; u=v)
3723 if (ackno > u->seqno) {
3724 double rtt = time - u->time;
3729 e->p.arc.dim.width = g->s.rtt.width;
3730 e->p.arc.dim.height = g->s.rtt.height;
3731 e->p.arc.dim.x = g->zoom.x * (u->seqno - g->bounds.x0)
3732 - g->s.rtt.width/2.0;
3733 e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
3734 e->p.arc.filled = TRUE;
3735 e->p.arc.angle1 = 0;
3736 e->p.arc.angle2 = 23040;
3740 rtt_delete_unack_from_list (&unack, u);
3745 e->type = ELMT_NONE;
3746 g->elists->elements = elements;
3749 static void rtt_toggle_seq_origin (struct graph *g)
3751 g->s.rtt.flags ^= SEQ_ORIGIN;
3753 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3754 g->x_axis->min = g->bounds.x0;
3760 /* replacement of Unix rint() for Windows */
3761 static int rint (double x)
3766 buf = _fcvt(x, 0, &dec, &sig);