diff options
author | Guy Harris <guy@alum.mit.edu> | 2001-12-08 09:27:51 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2001-12-08 09:27:51 +0000 |
commit | cc97eed136095fff9f8e697d5cce8d116d3d48f1 (patch) | |
tree | d1db295952887fbc9be02cc778c84ca2ab14f62c /gtk | |
parent | 291e50cea0a9f4aeecbeab14bfdeb0974d756552 (diff) |
TCP time-sequence, round-trip time, and throughput graphs, from Pavel
Mores.
svn path=/trunk/; revision=4360
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/Makefile.nmake | 3 | ||||
-rw-r--r-- | gtk/menu.c | 10 | ||||
-rw-r--r-- | gtk/tcp_graph.c | 3764 | ||||
-rw-r--r-- | gtk/tcp_graph.h | 27 |
5 files changed, 3806 insertions, 2 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 51c724cac6..94ba7856b5 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for the GTK interface routines for Ethereal # -# $Id: Makefile.am,v 1.35 2001/03/24 02:23:08 guy Exp $ +# $Id: Makefile.am,v 1.36 2001/12/08 09:27:50 guy Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@zing.org> @@ -89,6 +89,8 @@ libui_a_SOURCES = \ stream_prefs.h \ summary_dlg.c \ summary_dlg.h \ + tcp_graph.c \ + tcp_graph.h \ ui_util.c \ ui_util.h diff --git a/gtk/Makefile.nmake b/gtk/Makefile.nmake index 9a0e3f1b76..6d3d3ef9de 100644 --- a/gtk/Makefile.nmake +++ b/gtk/Makefile.nmake @@ -5,6 +5,8 @@ include ..\config.nmake CFLAGS=-DHAVE_CONFIG_H /I.. /I../epan /I../wiretap \ /I$(GLIB_DIR) /I$(GTK_DIR) /I$(GLIB_DIR)/gmodule \ /I$(GTK_DIR)\gdk /I$(GTK_DIR)\gdk\win32 \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP /I$(PCAP_DIR)\WPCAP\LIBPCAP\bpf \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP\lbl \ /I$(PCAP_DIR)\include $(LOCAL_CFLAGS) CVARSDLL=-DWIN32 -DNULL=0 -D_MT -D_DLL @@ -46,6 +48,7 @@ OBJECTS=capture_dlg.obj \ simple_dialog.obj \ stream_prefs.obj \ summary_dlg.obj \ + tcp_graph.obj \ ui_util.obj diff --git a/gtk/menu.c b/gtk/menu.c index b24a6857bf..65a7f724c2 100644 --- a/gtk/menu.c +++ b/gtk/menu.c @@ -1,7 +1,7 @@ /* menu.c * Menu routines * - * $Id: menu.c,v 1.56 2001/11/21 23:16:26 gram Exp $ + * $Id: menu.c,v 1.57 2001/12/08 09:27:51 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -60,6 +60,7 @@ #include "proto_hier_stats_dlg.h" #include "keys.h" #include "plugins.h" +#include "tcp_graph.h" GtkWidget *popup_menu_object; @@ -151,6 +152,11 @@ static GtkItemFactoryEntry menu_items[] = {"/Tools/_Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL}, {"/Tools/_Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL}, /* {"/Tools/Graph", NULL, NULL, 0, NULL}, future use */ + {"/_Tools/TCP Stream Analysis", NULL, NULL, 0, "<Branch>" }, + {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (Stevens)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 0, NULL}, + {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (tcptrace)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 1, NULL}, + {"/_Tools/TCP Stream Analysis/Throughput Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 2, NULL}, + {"/_Tools/TCP Stream Analysis/RTT Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 3, NULL}, {"/Tools/_Summary", NULL, GTK_MENU_FUNC(summary_open_cb), 0, NULL}, {"/Tools/Protocol Hierarchy Statistics", NULL, GTK_MENU_FUNC(proto_hier_stats_cb), 0, NULL}, {"/_Help", NULL, NULL, 0, "<LastBranch>" }, @@ -403,6 +409,8 @@ set_menus_for_selected_packet(gboolean have_selected_packet) have_selected_packet && decode_as_ok()); set_menu_sensitivity("/Resolve Name", have_selected_packet && !prefs.name_resolve); + set_menu_sensitivity("/Tools/TCP Stream Analysis", + have_selected_packet ? (cfile.edt->pi.ipproto == 6) : FALSE); } /* Enable or disable menu items based on whether a tree row is selected diff --git a/gtk/tcp_graph.c b/gtk/tcp_graph.c new file mode 100644 index 0000000000..2cb5a9312c --- /dev/null +++ b/gtk/tcp_graph.c @@ -0,0 +1,3764 @@ +/* tcp_graph.c + * TCP graph drawing code + * By Pavel Mores <pvl@uh.cz> + * Win32 port: rwh@unifiedtech.com + * + * $Id: tcp_graph.c,v 1.1 2001/12/08 09:27:51 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <stdio.h> +#include <math.h> /* rint() */ + +#include <sys/types.h> /* freebsd requires this */ + +#ifndef WIN32 +# include <netinet/in.h> /* ntohs(), IPPROTO_TCP */ +# include <arpa/inet.h> /* inet_ntoa() */ +#else /* WIN32 */ +# include <stdlib.h> +#endif /* WIN32 */ + +#include "globals.h" /* cfile */ +#include "packet.h" /* frame_data */ +#include "prefs_dlg.h" /* prefs */ +#include "gtkglobals.h" /* set_scrollbar_placement_srollw() and + * remember_scrolled_window() */ +#include "tcp_graph.h" + +/* from <net/ethernet.h> */ +struct ether_header { + guint8 ether_dhost[6]; /* destination eth addr */ + guint8 ether_shost[6]; /* source ether addr */ + guint16 ether_type; /* packet type ID field */ +}; +#define ETHERTYPE_IP 0x0800 + + +#undef BITFIELDS + +/* from <netinet/ip.h> */ +struct iphdr { +#ifdef BITFIELDS + unsigned int ihl:4; + unsigned int version:4; +#else /* ! BITFIELDS */ + guint8 version_ihl; +#endif /* BITFIELDS */ + guint8 tos; + guint16 tot_len; + guint16 id; + guint16 frag_off; + guint8 ttl; + guint8 protocol; + guint16 check; + guint32 saddr; + guint32 daddr; +}; + +#ifndef BITFIELDS + +#define IPHDR_IHL_SHIFT 0 +#define IPHDR_IHL_MASK (0xf << IPHDR_IHL_SHIFT) +#define IHL(iphdrptr) ( ((iphdrptr)->version_ihl & IPHDR_IHL_MASK) >> IPHDR_IHL_SHIFT ) + +#endif /* BITFIELDS */ + +/* from <netinet/tcp.h> */ +struct tcphdr { + guint16 source; + guint16 dest; + guint32 seq; + guint32 ack_seq; +#ifdef BITFIELDS + guint16 res1:4; + guint16 doff:4; + guint16 fin:1; + guint16 syn:1; + guint16 rst:1; + guint16 psh:1; + guint16 ack:1; + guint16 urg:1; + guint16 res2:2; +#else /* ! BITFIELDS */ + guint16 flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#endif /* BITFIELDS */ + guint16 window; + guint16 check; + guint16 urg_ptr; +}; + +#ifndef BITFIELDS + +#define TCP_SYN(tcphdr) ( ntohs ((tcphdr).flags) & TH_SYN ) +#define TCP_ACK(tcphdr) ( ntohs ((tcphdr).flags) & TH_ACK ) +#define TCP_DOFF_SHIFT 12 +#define TCP_DOFF_MASK (0xf << TCP_DOFF_SHIFT) +#define DOFF(tcphdr) ( ( ntohs ((tcphdr).flags) & TCP_DOFF_MASK) >> TCP_DOFF_SHIFT ) + +#endif /* BITFIELDS */ + + +#define TXT_WIDTH 850 +#define TXT_HEIGHT 550 + +/* for compare_headers() */ +/* segment went the same direction as the currently selected one */ +#define COMPARE_CURR_DIR 0 +#define COMPARE_ANY_DIR 1 + +/* initalize_axis() */ +#define AXIS_HORIZONTAL 0 +#define AXIS_VERTICAL 1 + +struct segment { + struct segment *next; + guint32 num; + guint32 rel_secs; + guint32 rel_usecs; + guint32 abs_secs; + guint32 abs_usecs; + struct iphdr iphdr; + struct tcphdr tcphdr; + int data; /* amount of data in this segment */ +}; + +struct rect { + double x, y, width, height; +}; + +struct line { + double x1, y1, x2, y2; +}; + +struct irect { + int x, y, width, height; +}; + +struct ipoint { + int x, y; +}; + +typedef enum { + ELMT_NONE=0, + ELMT_RECT=1, + ELMT_LINE=2, + ELMT_ARC=3 +} ElementType; + +struct rect_params { + struct rect dim; + gint filled; +}; + +struct line_params { + struct line dim; +}; + +struct arc_params { + struct rect dim; + gint filled; + gint angle1, angle2; +}; + +struct element { + ElementType type; + GdkGC *gc; + struct segment *parent; + union { + struct arc_params arc; + struct rect_params rect; + struct line_params line; + } p; +}; + +struct element_list { + struct element_list *next; + struct element *elements; +}; + +struct axis { + struct graph *g; /* which graph we belong to */ + GtkWidget *drawing_area; + GdkPixmap *pixmap[2]; + int displayed; +#define AXIS_ORIENTATION 1 << 0 + int flags; + /* dim and orig (relative to origin of window) of axis' pixmap */ + struct irect p; + /* dim and orig (relative to origin of axis' pixmap) of scale itself */ + struct irect s; + gdouble min, max; + gdouble major, minor; /* major and minor ticks */ + char **label; +}; + +#define HAXIS_INIT_HEIGHT 70 +#define VAXIS_INIT_WIDTH 100 +#define TITLEBAR_HEIGHT 50 +#define RMARGIN_WIDTH 30 + +struct style_tseq_tcptrace { + GdkGC *gc_seq; + GdkGC *gc_ack[2]; + int flags; +}; + +struct style_tseq_stevens { + int seq_width; + int seq_height; + int flags; +}; + +struct style_tput { + int width, height; + int nsegs; + int flags; +}; + +struct style_rtt { + int width, height; + int flags; +}; + +/* style flags */ +#define SEQ_ORIGIN 0x1 +/* show absolute sequence numbers (not differences from isn) */ +#define SEQ_ORIGIN_ZERO 0x1 +#define SEQ_ORIGIN_ISN 0x0 +#define TIME_ORIGIN 0x10 +/* show time from beginning of capture as opposed to time from beginning + * of the connection */ +#define TIME_ORIGIN_CAP 0x10 +#define TIME_ORIGIN_CONN 0x0 + +/* this is used by rtt module only */ +struct unack { + struct unack *next; + double time; + unsigned int seqno; +}; + +struct cross { + int x, y; + int draw; /* indicates whether we should draw cross at all */ + int erase_needed; + GtkToggleButton *on_toggle; + GtkToggleButton *off_toggle; +}; + +struct bounds { + double x0, y0, width, height; +}; + +struct zoom { + double x, y; +}; + +struct zooms { + double x, y; + double step_x, step_y; + struct zoom initial; +#define ZOOM_OUT (1 << 0) +#define ZOOM_HLOCK (1 << 1) +#define ZOOM_VLOCK (1 << 2) +#define ZOOM_STEPS_SAME (1 << 3) +#define ZOOM_STEPS_KEEP_RATIO (1 << 4) + int flags; + /* unfortunately, we need them both because gtk_toggle_button_set_active () + * with second argument FALSE doesn't do anything, somehow */ + struct { + GtkToggleButton *in_toggle; + GtkToggleButton *out_toggle; + GtkEntry *h_zoom; + GtkEntry *v_zoom; + GtkSpinButton *h_step; + GtkSpinButton *v_step; + } widget; +}; + +struct grab { + int grabbed; + int x, y; +}; + +struct magnify { + int active; + int x, y; + struct ipoint offset; + int width, height; + struct zoom zoom; + struct graph *g; +#define MAGZOOMS_SAME (1 << 0) +#define MAGZOOMS_SAME_RATIO (1 << 1) +#define MAGZOOMS_IGNORE (1 << 31) + int flags; + struct { + GtkSpinButton *h_zoom, *v_zoom; + } widget; +}; + +struct graph { + struct graph *next; +#define GRAPH_TSEQ_STEVENS 0 +#define GRAPH_TSEQ_TCPTRACE 1 +#define GRAPH_THROUGHPUT 2 +#define GRAPH_RTT 3 + int type; +#define GRAPH_DESTROYED (1 << 0) +#define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1) + int flags; + GtkWidget *toplevel; /* keypress handler needs this */ + GtkWidget *drawing_area; + GtkWidget *text; /* text widget for seg list - probably temporary */ + GdkFont *font; /* font used for annotations etc. */ + GdkGC *fg_gc; + GdkGC *bg_gc; + GdkPixmap *title_pixmap; + GdkPixmap *pixmap[2]; + int displayed; /* which of both pixmaps is on screen right now */ + struct { + GtkWidget *control_panel; + /* this belongs to style structs of graph types that make use of it */ + GtkToggleButton *time_orig_conn, *seq_orig_isn; + } gui; + char **title; + /* Next 4 attribs describe the graph in natural units, before any scaling. + * For example, if we want to display graph of TCP conversation that + * started 112.309845 s after beginning of the capture and ran until + * 479.093582 s, 237019 B went through the connection (in one direction) + * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845, + * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */ + struct bounds bounds; + /* dimensions and position of the graph, both expressed already in pixels. + * x and y give the position of upper left corner of the graph relative + * to origin of the graph window, size is basically bounds*zoom */ + struct irect geom; + /* viewport (=graph window area which is reserved for graph itself), its + * size and position relative to origin of the graph window */ + struct irect wp; + struct grab grab; + /* If we need to display 237019 sequence numbers (=bytes) onto say 500 + * pixels, we have to scale the graph down by factor of 0.002109. This + * number would be zoom.y. Obviously, both directions have separate zooms.*/ + struct zooms zoom; + struct cross cross; + struct magnify magnify; + struct axis *x_axis, *y_axis; + struct segment *segments; + struct segment *current; + struct element_list *elists; /* element lists */ + union { + struct style_tseq_stevens tseq_stevens; + struct style_tseq_tcptrace tseq_tcptrace; + struct style_tput tput; + struct style_rtt rtt; + } s; +}; + +static struct graph *graphs = NULL; +static GdkGC *xor_gc = NULL; +static int refnum=0; + +#define debug(section) if (debugging & section) +/* print function entry points */ +#define DBS_FENTRY (1 << 0) +#define DBS_AXES_TICKS (1 << 1) +#define DBS_AXES_DRAWING (1 << 2) +#define DBS_GRAPH_DRAWING (1 << 3) +#define DBS_TPUT_ELMTS (1 << 4) +/*int debugging = DBS_FENTRY;*/ +int debugging = 0; +/*int debugging = DBS_AXES_TICKS;*/ +/*int debugging = DBS_AXES_DRAWING;*/ +/*int debugging = DBS_GRAPH_DRAWING;*/ +/*int debugging = DBS_TPUT_ELMTS;*/ + +static void create_gui (struct graph * ); +static void create_text_widget (struct graph * ); +static void display_text (struct graph * ); +static void create_drawing_area (struct graph * ); +static void control_panel_create (struct graph * ); +static GtkWidget *control_panel_create_zoom_group (struct graph * ); +static GtkWidget *control_panel_create_magnify_group (struct graph * ); +static GtkWidget *control_panel_create_cross_group (struct graph * ); +static GtkWidget *control_panel_create_zoomlock_group (struct graph * ); +static GtkWidget *control_panel_create_graph_type_group (struct graph * ); +static void control_panel_add_zoom_page (struct graph * , GtkWidget * ); +static void control_panel_add_magnify_page (struct graph * , GtkWidget * ); +static void control_panel_add_origin_page (struct graph * , GtkWidget * ); +static void control_panel_add_cross_page (struct graph * , GtkWidget * ); +static void control_panel_add_graph_type_page (struct graph * , GtkWidget * ); +static void callback_toplevel_destroy (GtkWidget * , gpointer ); +static void callback_close (GtkWidget * , gpointer ); +static void callback_time_origin (GtkWidget * , gpointer ); +static void callback_seq_origin (GtkWidget * , gpointer ); +static void callback_zoomlock_h (GtkWidget * , gpointer ); +static void callback_zoomlock_v (GtkWidget * , gpointer ); +static void callback_zoom_inout (GtkWidget * , gpointer ); +static void callback_zoom_step (GtkWidget * , gpointer ); +static void callback_zoom_flags (GtkWidget * , gpointer ); +static void callback_cross_on_off (GtkWidget * , gpointer ); +static void callback_mag_width (GtkWidget * , gpointer ); +static void callback_mag_height (GtkWidget * , gpointer ); +static void callback_mag_x (GtkWidget * , gpointer ); +static void callback_mag_y (GtkWidget * , gpointer ); +static void callback_mag_zoom (GtkWidget * , gpointer ); +static void callback_mag_flags (GtkWidget * , gpointer ); +static void callback_graph_type (GtkWidget * , gpointer ); +static void callback_graph_init_on_typechg (GtkWidget * , gpointer ); +static void callback_create_help (GtkWidget * , gpointer ); +static void callback_close_help (GtkWidget * , gpointer ); +static void update_zoom_spins (struct graph * ); +static int get_headers (char * , struct segment * ); +static int compare_headers (struct segment * , struct segment * , int ); +static int get_num_dsegs (struct graph * ); +static int get_num_acks (struct graph * ); +static void graph_type_dependent_initialize (struct graph * ); +static void graph_put (struct graph * ); +static struct graph *graph_new (void); +static void graph_destroy (struct graph * ); +static void graph_initialize_values (struct graph * ); +static void graph_init_sequence (struct graph * ); +static void draw_element_line (struct graph * , struct element * ); +static void draw_element_arc (struct graph * , struct element * ); +static void graph_display (struct graph * ); +static void graph_pixmaps_create (struct graph * ); +static void graph_pixmaps_switch (struct graph * ); +static void graph_pixmap_draw (struct graph * ); +static void graph_pixmap_display (struct graph * ); +static void graph_element_lists_make (struct graph * ); +static void graph_element_lists_free (struct graph * ); +static void graph_element_lists_initialize (struct graph * ); +static void graph_title_pixmap_create (struct graph * ); +static void graph_title_pixmap_draw (struct graph * ); +static void graph_title_pixmap_display (struct graph * ); +static void graph_segment_list_get (struct graph * ); +static void graph_segment_list_free (struct graph * ); +static void graph_select_segment (struct graph * , int , int ); +static int line_detect_collision (struct element * , int , int ); +static int arc_detect_collision (struct element * , int , int ); +static void update_packet_list (int ); +static void axis_pixmaps_create (struct axis * ); +static void axis_pixmaps_switch (struct axis * ); +static void axis_display (struct axis * ); +static void v_axis_pixmap_draw (struct axis * ); +static void h_axis_pixmap_draw (struct axis * ); +static void axis_pixmap_display (struct axis * ); +static void axis_compute_ticks (struct axis * , double , double , int ); +static double axis_zoom_get (struct axis * , int ); +static void axis_ticks_up (int * , int * ); +static void axis_ticks_down (int * , int * ); +static void axis_destroy (struct axis * ); +static int get_label_dim (struct axis * , int , double ); +static void toggle_time_origin (struct graph * ); +static void toggle_seq_origin (struct graph * ); +static void cross_xor (struct graph * , int , int ); +static void cross_draw (struct graph * , int , int ); +static void cross_erase (struct graph * ); +static void magnify_create (struct graph * , int , int ); +static void magnify_move (struct graph * , int , int ); +static void magnify_destroy (struct graph * ); +static void magnify_draw (struct graph * ); +static void magnify_get_geom (struct graph * , int , int ); +static gint configure_event (GtkWidget * , GdkEventConfigure * ); +static gint expose_event (GtkWidget * , GdkEventExpose * ); +static gint button_press_event (GtkWidget * , GdkEventButton * ); +static gint button_release_event (GtkWidget * , GdkEventButton * ); +static gint motion_notify_event (GtkWidget * , GdkEventMotion * ); +static gint key_press_event (GtkWidget * , GdkEventKey * ); +static gint key_release_event (GtkWidget * , GdkEventKey * ); +static gint leave_notify_event (GtkWidget * , GdkEventCrossing * ); +static gint enter_notify_event (GtkWidget * , GdkEventCrossing * ); +static void tseq_stevens_initialize (struct graph * ); +static void tseq_stevens_get_bounds (struct graph * ); +static void tseq_stevens_read_config (struct graph * ); +static void tseq_stevens_make_elmtlist (struct graph * ); +static void tseq_stevens_toggle_seq_origin (struct graph * ); +static void tseq_stevens_toggle_time_origin (struct graph * ); +static void tseq_tcptrace_read_config (struct graph * ); +static void tseq_tcptrace_make_elmtlist (struct graph * ); +static void tseq_tcptrace_toggle_seq_origin (struct graph * ); +static void tseq_tcptrace_toggle_time_origin (struct graph * ); +static void tput_initialize (struct graph * ); +static void tput_read_config (struct graph * ); +static void tput_make_elmtlist (struct graph * ); +static void tput_toggle_time_origin (struct graph * ); +static void rtt_read_config (struct graph * ); +static void rtt_initialize (struct graph * ); +static int rtt_is_retrans (struct unack * , unsigned int ); +static struct unack *rtt_get_new_unack (double , unsigned int ); +static void rtt_put_unack_on_list (struct unack ** , struct unack * ); +static void rtt_delete_unack_from_list (struct unack ** , struct unack * ); +static void rtt_make_elmtlist (struct graph * ); +static void rtt_toggle_seq_origin (struct graph * ); +#ifdef WIN32 +static int rint (double ); /* compiler template for Windows */ +#endif + +static char helptext[] = +#ifndef WIN32 +"Here's what you can do:\n +- Left Mouse Button selects segment in ethereal's packet list\n\ +- Middle Mouse Button zooms in\n\ +- <shift>-Middle Button zooms out\n\ +- Right Mouse Button moves the graph (if zoomed in)\n\ +- <ctrl>-Right Mouse Button displays a portion of graph magnified\n\ +- Space toggles crosshairs\n\ +- 's' toggles relative/absolute sequence numbers\n\ +- 't' toggles time origin\n +"; +#else /* WIN32 */ +"Here's what you can do:\n\ +- <ctrl>-Left Mouse Button selects segment in ethereal's packet list\n\ +- Left Mouse Button zooms in\n\ +- <shift>-Left Mouse Button zooms out\n\ +- Right Mouse Button moves the graph (if zoomed in)\n\ +- <ctrl>-Right Mouse Button displays a portion of graph magnified\n\ +\n\ +- Space bar toggles crosshairs\n\ +- 's' - Toggles relative/absolute sequence numbers\n\ +- 't' - Toggles time origin\n\ +"; +#endif + +void tcp_graph_cb (GtkWidget *w, gpointer data, guint graph_type) +{ + struct segment current; + struct graph *g; + + debug(DBS_FENTRY) puts ("tcp_graph_cb()"); + + if (! (g = graph_new())) + return; + + refnum++; + graph_initialize_values (g); + graph_put (g); + + g->type = graph_type; + if (!get_headers (cfile.pd, ¤t)) { + /* currently selected packet is neither TCP over IP over Ethernet II + * nor TCP over IP alone (= IP over PPP) - should display some + * kind of warning dialog */ + printf ("packet selected is not a TCP segment\n"); + return; + } + + graph_segment_list_get(g); + create_gui(g); + /* display_text(g); */ + graph_init_sequence(g); +} + +static void create_gui (struct graph *g) +{ + debug(DBS_FENTRY) puts ("create_gui()"); + /* create_text_widget(g); */ + control_panel_create (g); + create_drawing_area(g); +} + +static void create_text_widget (struct graph *g) +{ + GtkWidget *streamwindow, *txt_scrollw, *box; + + debug(DBS_FENTRY) puts ("create_text_widget()"); + streamwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (streamwindow, "Packet chain"); + gtk_widget_set_usize (GTK_WIDGET (streamwindow), TXT_WIDTH, TXT_HEIGHT); + gtk_container_border_width (GTK_CONTAINER(streamwindow), 2); + + box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (streamwindow), box); + gtk_widget_show (box); + + txt_scrollw = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + set_scrollbar_placement_scrollw (txt_scrollw, prefs.gui_scrollbar_on_right); + remember_scrolled_window (txt_scrollw); + gtk_widget_show (txt_scrollw); + + g->text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (g->text), FALSE); + gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text); + gtk_widget_show (g->text); + gtk_widget_show (streamwindow); +} + +static void display_text (struct graph *g) +{ + char *line[256]; + struct segment *ptr; + double first_time, prev_time; + unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev; + GdkColor color, *c; + + debug(DBS_FENTRY) puts ("display_text()"); + gdk_color_parse ("SlateGray", &color); + gtk_text_freeze (GTK_TEXT (g->text)); + snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n", + "pkt num", "time", "delta first", "delta prev", + "seqno", "delta first", "delta prev", "data (B)"); + gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL, + (const char *)line, -1); + + first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0; + prev_time = first_time; + /* we have to find Initial Sequence Number for both ends of connection */ + for (ptr=g->segments; ptr; ptr=ptr->next) { + if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) { + isn_this = ntohl (ptr->tcphdr.seq); + break; + } + } + for (ptr=g->segments; ptr; ptr=ptr->next) { + if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) { + isn_opposite = ntohl (ptr->tcphdr.seq); + break; + } + } + seq_this_prev = isn_this; + seq_opposite_prev = isn_opposite; + for (ptr=g->segments; ptr; ptr=ptr->next) { + double time=ptr->rel_secs + ptr->rel_usecs/1000000.0; + unsigned int seq = ntohl (ptr->tcphdr.seq); + int seq_delta_isn, seq_delta_prev; + + if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) { + seq_delta_isn = seq - isn_this; + seq_delta_prev = seq - seq_this_prev; + seq_this_prev = seq; + c = NULL; + } else { + seq_delta_isn = seq - isn_opposite; + seq_delta_prev = seq - seq_opposite_prev; + seq_opposite_prev = seq; + c = &color; + } + snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n", + ptr->num, time, time-first_time, time-prev_time, + seq, seq_delta_isn, seq_delta_prev, +#ifdef BITFIELDS + ntohs (ptr->iphdr.tot_len) - 4*ptr->iphdr.ihl - + 4*ptr->tcphdr.doff); +#else + ntohs (ptr->iphdr.tot_len) - 4*IHL(&(ptr->iphdr)) - + 4*DOFF(ptr->tcphdr)); +#endif /* BITFIELDS */ + gtk_text_insert (GTK_TEXT (g->text), g->font, c, NULL, + (const char * )line, -1); + prev_time = time; + } + gtk_text_thaw (GTK_TEXT (g->text)); +} + +static void create_drawing_area (struct graph *g) +{ + GdkColormap *colormap; + GdkColor color; + GtkWidget *frame, *box; +#define WINDOW_TITLE_LENGTH 64 + char window_title[WINDOW_TITLE_LENGTH]; + + debug(DBS_FENTRY) puts ("create_drawing_area()"); +#if 0 + g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75" + "-c-80-iso8859-2"); + g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120" + "-*-*-m-*-iso8859-2"); +#endif + g->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (g->toplevel, "Test Graph"); + + /* Create the drawing area */ + g->drawing_area = gtk_drawing_area_new (); + g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area; + gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area), + g->wp.width + g->wp.x + RMARGIN_WIDTH, + g->wp.height + g->wp.y + g->x_axis->s.height); + gtk_widget_show (g->drawing_area); + + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "expose_event", + (GtkSignalFunc )expose_event, NULL); + /* this has to be done later, after the widget has been shown */ + /* + gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event", + (GtkSignalFunc )configure_event, NULL); + */ + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "motion_notify_event", + (GtkSignalFunc )motion_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_press_event", + (GtkSignalFunc )button_press_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_release_event", + (GtkSignalFunc )button_release_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "leave_notify_event", + (GtkSignalFunc )leave_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->drawing_area), "enter_notify_event", + (GtkSignalFunc )enter_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->toplevel), "destroy", + (GtkSignalFunc )callback_toplevel_destroy, g); + /* why doesn't drawing area send key_press_signals? */ + gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_press_event", + (GtkSignalFunc )key_press_event, NULL); + gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_release_event", + (GtkSignalFunc )key_release_event, NULL); + gtk_widget_set_events (g->toplevel,GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK); + + gtk_widget_set_events (g->drawing_area, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + +#if 0 + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_add (GTK_CONTAINER (frame), g->drawing_area); + + box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (g->toplevel), box); + gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5); + gtk_widget_show (frame); + gtk_widget_show (box); +#endif + + gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area); + gtk_widget_show (g->toplevel); + snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d - Ethereal", + refnum); + gtk_window_set_title (GTK_WINDOW (g->toplevel), window_title); + + /* in case we didn't get what we asked for */ + g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width - + g->wp.x - RMARGIN_WIDTH; + g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height - + g->wp.y - g->x_axis->s.height; + + g->font = g->drawing_area->style->font; + gdk_font_ref (g->font); + + colormap = gdk_window_get_colormap (g->drawing_area->window); + if (!xor_gc) { + xor_gc = gdk_gc_new (g->drawing_area->window); + gdk_gc_set_function (xor_gc, GDK_XOR); + gdk_color_parse ("gray15", &color); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_set_foreground (xor_gc, &color); + } + g->fg_gc = gdk_gc_new (g->drawing_area->window); + g->bg_gc = gdk_gc_new (g->drawing_area->window); + gdk_color_parse ("white", &color); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_set_foreground (g->bg_gc, &color); + + /* this is probably quite an ugly way to get rid of the first configure + * event + * immediatelly after gtk_widget_show (window) drawing_area gets a configure + * event which is handled during the next return to gtk_main which is + * probably the gdk_gc_new() call. configure handler calls + * graph_element_lists_make() which is not good because the graph struct is + * not fully set up yet - namely we're not sure about actual geometry + * and we don't have the GC's at all. so we just postpone installation + * of configure handler until we're ready to deal with it. + * + * !!! NEMĚLO BY TO BÝT NA KONCI graph_init_sequence()? !!! + * + */ + gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event", + (GtkSignalFunc )configure_event, NULL); + + /* puts ("exiting create_drawing_area()"); */ +} + +static void callback_toplevel_destroy (GtkWidget *widget, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (!(g->flags & GRAPH_DESTROYED)) { + g->flags |= GRAPH_DESTROYED; + graph_destroy ((struct graph * )data); + } +} + +static void control_panel_create (struct graph *g) +{ + GtkWidget *toplevel, *notebook; + GtkWidget *table; + GtkWidget *help, *close, *button_box; +#define WINDOW_TITLE_LENGTH 64 + char window_title[WINDOW_TITLE_LENGTH]; + + debug(DBS_FENTRY) puts ("control_panel_create()"); + + notebook = gtk_notebook_new (); + control_panel_add_zoom_page (g, notebook); + control_panel_add_magnify_page (g, notebook); + control_panel_add_origin_page (g, notebook); + control_panel_add_cross_page (g, notebook); + control_panel_add_graph_type_page (g, notebook); + + /* bottom buttons group */ + help = gtk_button_new_with_label ("Help"); + close = gtk_button_new_with_label ("Close"); + button_box = gtk_hbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (button_box), help, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (button_box), close, TRUE, TRUE, 0); + + toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + table = gtk_table_new (2, 1, FALSE); + gtk_container_add (GTK_CONTAINER (toplevel), table); + + gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + gtk_table_attach (GTK_TABLE (table), button_box, 0, 1, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + gtk_signal_connect (GTK_OBJECT (close), "clicked", + (GtkSignalFunc )callback_close, g); + gtk_signal_connect (GTK_OBJECT (help), "clicked", + (GtkSignalFunc )callback_create_help, g); + + /* gtk_widget_show_all (table); */ + /* g->gui.control_panel = table; */ + gtk_widget_show_all (toplevel); + snprintf (window_title, WINDOW_TITLE_LENGTH, + "Graph %d - Control - Ethereal", refnum); + gtk_window_set_title (GTK_WINDOW (toplevel), window_title); + g->gui.control_panel = toplevel; +} + +static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n) +{ + GtkWidget *zoom_frame; + GtkWidget *zoom_lock_frame; + GtkWidget *label; + GtkWidget *box; + + zoom_frame = control_panel_create_zoom_group (g); + gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5); + zoom_lock_frame = control_panel_create_zoomlock_group (g); + gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5); + box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0); + gtk_widget_show (box); + label = gtk_label_new ("Zoom"); + gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label); +} + +static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n) +{ + GtkWidget *mag_frame, *label; + + mag_frame = control_panel_create_magnify_group (g); + gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5); + label = gtk_label_new ("Magnify"); + gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label); +} + +static void control_panel_add_origin_page (struct graph *g, GtkWidget *n) +{ + GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame; + GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame; + GtkWidget *box, *label; + + /* time origin box */ + time_orig_cap = + gtk_radio_button_new_with_label (NULL, "beginning of capture"); + time_orig_conn = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)), + "beginning of this TCP connection"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE); + time_orig_box = gtk_vbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0); + time_orig_frame = gtk_frame_new ("Time origin"); + gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5); + gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box); + + /* sequence number origin group */ + seq_orig_isn = + gtk_radio_button_new_with_label (NULL, "initial sequence number"); + seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group ( + GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE); + seq_orig_box = gtk_vbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0); + seq_orig_frame = gtk_frame_new ("Sequence number origin"); + gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5); + gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box); + + g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn; + g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn; + + gtk_signal_connect (GTK_OBJECT (time_orig_conn), "toggled", + (GtkSignalFunc )callback_time_origin, g); + gtk_signal_connect (GTK_OBJECT (seq_orig_isn), "toggled", + (GtkSignalFunc )callback_seq_origin, g); + + box = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (box), 5); + gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0); + gtk_widget_show (box); + label = gtk_label_new ("Origin"); + gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label); +} + +static void control_panel_add_cross_page (struct graph *g, GtkWidget *n) +{ + GtkWidget *cross_frame, *label; + + cross_frame = control_panel_create_cross_group (g); + gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5); + label = gtk_label_new ("Cross"); + gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label); +} + +static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n) +{ + GtkWidget *frame, *label; + + frame = control_panel_create_graph_type_group (g); + gtk_container_set_border_width (GTK_CONTAINER (frame), 5); + label = gtk_label_new ("Graph type"); + gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label); +} + +static void callback_close (GtkWidget *widget, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (!(g->flags & GRAPH_DESTROYED)) { + g->flags |= GRAPH_DESTROYED; + graph_destroy ((struct graph * )data); + } +} + +static void callback_create_help (GtkWidget *widget, gpointer data) +{ + struct graph *g = (struct graph * )data; + GtkWidget *toplevel, *box, *text, *scroll, *close; + + toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_usize (toplevel, 500, 400); + box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (toplevel), box); + scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0); + text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (text), FALSE); + gtk_text_set_line_wrap (GTK_TEXT (text), FALSE); + gtk_text_set_word_wrap (GTK_TEXT (text), FALSE); + gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1); + gtk_container_add (GTK_CONTAINER (scroll), text); + close = gtk_button_new_with_label ("Close"); + gtk_box_pack_start (GTK_BOX (box), close, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT (close), "clicked", + (GtkSignalFunc )callback_close_help, toplevel); + + gtk_widget_show_all (toplevel); +} + +static void callback_close_help (GtkWidget *widget, gpointer data) +{ + gtk_widget_destroy ((GtkWidget * )data); +} + +static void callback_time_origin (GtkWidget *toggle, gpointer data) +{ + toggle_time_origin ((struct graph * )data); +} + +static void callback_seq_origin (GtkWidget *toggle, gpointer data) +{ + toggle_seq_origin ((struct graph * )data); +} + +static GtkWidget *control_panel_create_zoom_group (struct graph *g) +{ + GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame; + GtkAdjustment *zoom_h_adj, *zoom_v_adj; + GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step; + GtkWidget *zoom_v_step_label, *zoom_v_step; + GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table; + GtkWidget *zoom_ratio_toggle, *zoom_same_toggle; + GtkWidget *zoom_h_entry, *zoom_v_entry; + GtkWidget *zoom_h_label, *zoom_v_label; + + zoom_in = gtk_radio_button_new_with_label (NULL, "in"); + zoom_out = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE); + zoom_inout_box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10); + gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0); + + zoom_separator1 = gtk_hseparator_new (); + + zoom_h_entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000"); + gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE); + zoom_h_label = gtk_label_new ("Horizontal:"); + + zoom_v_entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000"); + gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE); + zoom_v_label = gtk_label_new ("Vertical:"); + + g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry; + g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry; + + zoom_table = gtk_table_new (2, 2, FALSE); + gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + + zoom_separator2 = gtk_hseparator_new (); + + zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0); + zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE); + zoom_h_step_label = gtk_label_new ("Horizontal step:"); + + zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0); + zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE); + zoom_v_step_label = gtk_label_new ("Vertical step:"); + + g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step; + g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step; + + zoom_same_toggle = gtk_check_button_new_with_label ("Keep them the same"); + zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio"); + gtk_object_set_data (GTK_OBJECT (zoom_same_toggle), "flag", + (gpointer )ZOOM_STEPS_SAME); + gtk_object_set_data (GTK_OBJECT (zoom_ratio_toggle), "flag", + (gpointer )ZOOM_STEPS_KEEP_RATIO); + gtk_signal_connect (GTK_OBJECT (zoom_same_toggle), "clicked", + (GtkSignalFunc )callback_zoom_flags, g); + gtk_signal_connect (GTK_OBJECT (zoom_ratio_toggle), "clicked", + (GtkSignalFunc )callback_zoom_flags, g); + + zoom_step_table = gtk_table_new (4, 2, FALSE); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + + zoom_box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0); + zoom_frame = gtk_frame_new ("Zoom"); + gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box); + + gtk_object_set_data (GTK_OBJECT (zoom_h_step), "direction", (gpointer )0); + gtk_object_set_data (GTK_OBJECT (zoom_v_step), "direction", (gpointer )1); + + gtk_signal_connect (GTK_OBJECT (zoom_in), "toggled", + (GtkSignalFunc )callback_zoom_inout, g); + gtk_signal_connect (GTK_OBJECT (zoom_h_step), "changed", + (GtkSignalFunc )callback_zoom_step, g); + gtk_signal_connect (GTK_OBJECT (zoom_v_step), "changed", + (GtkSignalFunc )callback_zoom_step, g); + + g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in; + g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out; + return zoom_frame; +} + +static void callback_zoom_inout (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (GTK_TOGGLE_BUTTON (toggle)->active) + g->zoom.flags &= ~ZOOM_OUT; + else + g->zoom.flags |= ZOOM_OUT; +} + +static void callback_zoom_step (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + float value; + int direction; + double *zoom_this, *zoom_other; + GtkSpinButton *widget_this, *widget_other; + double old_this; + + direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction"); + value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin)); + + if (direction) { + zoom_this = &g->zoom.step_y; + zoom_other = &g->zoom.step_x; + widget_this = g->zoom.widget.v_step; + widget_other = g->zoom.widget.h_step; + } else { + zoom_this = &g->zoom.step_x; + zoom_other = &g->zoom.step_y; + widget_this = g->zoom.widget.h_step; + widget_other = g->zoom.widget.v_step; + } + + old_this = *zoom_this; + *zoom_this = value; + if (g->zoom.flags & ZOOM_STEPS_SAME) { + *zoom_other = value; + gtk_spin_button_set_value (widget_other, *zoom_other); + } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) { + double old_other = *zoom_other; + *zoom_other *= value / old_this; + if (*zoom_other < 1.0) { + *zoom_other = 1.0; + *zoom_this = old_this * 1.0 / old_other; + gtk_spin_button_set_value (widget_this, *zoom_this); + } else if (*zoom_other > 5.0) { + *zoom_other = 5.0; + *zoom_this = old_this * 5.0 / old_other; + gtk_spin_button_set_value (widget_this, *zoom_this); + } + gtk_spin_button_set_value (widget_other, *zoom_other); + } +} + +static void callback_zoom_flags (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag"); + + if (GTK_TOGGLE_BUTTON (toggle)->active) + g->zoom.flags |= flag; + else + g->zoom.flags &= ~flag; +} + +static void update_zoom_spins (struct graph *g) +{ + char s[32]; + + snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x); + gtk_entry_set_text (g->zoom.widget.h_zoom, s); + snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y); + gtk_entry_set_text (g->zoom.widget.v_zoom, s); +} + +static GtkWidget *control_panel_create_magnify_group (struct graph *g) +{ + GtkWidget *mag_width_label, *mag_width; + GtkWidget *mag_height_label, *mag_height; + GtkWidget *mag_x_label, *mag_x; + GtkWidget *mag_y_label, *mag_y; + GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table; + GtkWidget *mag_h_zoom_label, *mag_h_zoom; + GtkWidget *mag_v_zoom_label, *mag_v_zoom; + GtkWidget *mag_zoom_same, *mag_zoom_ratio; + GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj; + GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj; + GtkWidget *mag_box, *mag_frame; + + mag_width_label = gtk_label_new ("Width:"); + mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0); + mag_width = gtk_spin_button_new (mag_width_adj, 0, 0); + + mag_height_label = gtk_label_new ("Height:"); + mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0); + mag_height = gtk_spin_button_new (mag_height_adj, 0, 0); + + mag_x_label = gtk_label_new ("X:"); + mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0); + mag_x = gtk_spin_button_new (mag_x_adj, 0, 0); + + mag_y_label = gtk_label_new ("Y:"); + mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0); + mag_y = gtk_spin_button_new (mag_y_adj, 0, 0); + + mag_wh_table = gtk_table_new (4, 2, FALSE); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0); + + mag_h_zoom_label = gtk_label_new ("Horizontal:"); + mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0); + mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1); + + mag_v_zoom_label = gtk_label_new ("Vertical:"); + mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0); + mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1); + + mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same"); + mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio"); + + mag_zoom_table = gtk_table_new (4, 2, FALSE); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + + mag_zoom_frame = gtk_frame_new ("Magnify zoom"); + gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table); + gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3); + + mag_box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0); + mag_frame = gtk_frame_new ("Magnify"); + gtk_container_add (GTK_CONTAINER (mag_frame), mag_box); + + g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom; + g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom; + gtk_object_set_data (GTK_OBJECT (mag_h_zoom), "direction", (gpointer )0); + gtk_object_set_data (GTK_OBJECT (mag_v_zoom), "direction", (gpointer )1); + gtk_object_set_data (GTK_OBJECT (mag_zoom_same), "flag", + (gpointer )MAGZOOMS_SAME); + gtk_object_set_data (GTK_OBJECT (mag_zoom_ratio), "flag", + (gpointer )MAGZOOMS_SAME_RATIO); + + gtk_signal_connect (GTK_OBJECT (mag_width), "changed", + (GtkSignalFunc )callback_mag_width, g); + gtk_signal_connect (GTK_OBJECT (mag_height), "changed", + (GtkSignalFunc )callback_mag_height, g); + gtk_signal_connect (GTK_OBJECT (mag_x), "changed", + (GtkSignalFunc )callback_mag_x, g); + gtk_signal_connect (GTK_OBJECT (mag_y), "changed", + (GtkSignalFunc )callback_mag_y, g); + gtk_signal_connect (GTK_OBJECT (mag_h_zoom), "changed", + (GtkSignalFunc )callback_mag_zoom, g); + gtk_signal_connect (GTK_OBJECT (mag_v_zoom), "changed", + (GtkSignalFunc )callback_mag_zoom, g); + gtk_signal_connect (GTK_OBJECT (mag_zoom_same), "clicked", + (GtkSignalFunc )callback_mag_flags, g); + gtk_signal_connect (GTK_OBJECT (mag_zoom_ratio), "clicked", + (GtkSignalFunc )callback_mag_flags, g); + + return mag_frame; +} + +static void callback_mag_width (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + + g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)); +} + +static void callback_mag_height (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + + g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); +} + +static void callback_mag_x (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + + g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); +} + +static void callback_mag_y (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + + g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); +} + +static void callback_mag_zoom (GtkWidget *spin, gpointer data) +{ + struct graph *g = (struct graph * )data; + float value; + int direction; + double *zoom_this, *zoom_other; + GtkSpinButton *widget_this, *widget_other; + double old_this; + + if (g->magnify.flags & MAGZOOMS_IGNORE) { + printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical"); + g->magnify.flags &= ~MAGZOOMS_IGNORE; + return; + } + direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction"); + value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin)); + + if (direction) { + zoom_this = &g->magnify.zoom.y; + zoom_other = &g->magnify.zoom.x; + widget_this = g->magnify.widget.v_zoom; + widget_other = g->magnify.widget.h_zoom; + } else { + zoom_this = &g->magnify.zoom.x; + zoom_other = &g->magnify.zoom.y; + widget_this = g->magnify.widget.h_zoom; + widget_other = g->magnify.widget.v_zoom; + } + + old_this = *zoom_this; + *zoom_this = value; + if (g->magnify.flags & MAGZOOMS_SAME) { + *zoom_other = value; + /* g->magnify.flags |= MAGZOOMS_IGNORE; */ + gtk_spin_button_set_value (widget_other, *zoom_other); + } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) { + double old_other = *zoom_other; + *zoom_other *= value / old_this; + if (*zoom_other < 1.0) { + *zoom_other = 1.0; + *zoom_this = old_this * 1.0 / old_other; + /* g->magnify.flags |= MAGZOOMS_IGNORE; */ + gtk_spin_button_set_value (widget_this, *zoom_this); + } else if (*zoom_other > 25.0) { + *zoom_other = 25.0; + *zoom_this = old_this * 25.0 / old_other; + /* g->magnify.flags |= MAGZOOMS_IGNORE; */ + gtk_spin_button_set_value (widget_this, *zoom_this); + } + /* g->magnify.flags |= MAGZOOMS_IGNORE; */ + gtk_spin_button_set_value (widget_other, *zoom_other); + } +} + +static void callback_mag_flags (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag"); + + if (GTK_TOGGLE_BUTTON (toggle)->active) + g->magnify.flags |= flag; + else + g->magnify.flags &= ~flag; +} + +static GtkWidget *control_panel_create_zoomlock_group (struct graph *g) +{ + GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box; + GtkWidget *zoom_lock_frame; + + zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none"); + zoom_lock_h = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)), + "horizontal"); + zoom_lock_v = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)), + "vertical"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE); + zoom_lock_box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0); + zoom_lock_frame = gtk_frame_new ("Zoom lock:"); + gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box); + + gtk_signal_connect (GTK_OBJECT (zoom_lock_h), "toggled", + (GtkSignalFunc )callback_zoomlock_h, g); + gtk_signal_connect (GTK_OBJECT (zoom_lock_v), "toggled", + (GtkSignalFunc )callback_zoomlock_v, g); + + return zoom_lock_frame; +} + +static void callback_zoomlock_h (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (GTK_TOGGLE_BUTTON (toggle)->active) + g->zoom.flags |= ZOOM_HLOCK; + else + g->zoom.flags &= ~ZOOM_HLOCK; +} + +static void callback_zoomlock_v (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (GTK_TOGGLE_BUTTON (toggle)->active) + g->zoom.flags |= ZOOM_VLOCK; + else + g->zoom.flags &= ~ZOOM_VLOCK; +} + +static GtkWidget *control_panel_create_cross_group (struct graph *g) +{ + GtkWidget *on, *off, *box, *frame, *vbox, *label; + + label = gtk_label_new ("Crosshairs:"); + off = gtk_radio_button_new_with_label (NULL, "off"); + on = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE); + box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10); + gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10); + gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0); + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15); + /* frame = gtk_frame_new ("Cross:"); */ + frame = gtk_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + gtk_signal_connect (GTK_OBJECT (on), "toggled", + (GtkSignalFunc )callback_cross_on_off, g); + + g->cross.on_toggle = (GtkToggleButton * )on; + g->cross.off_toggle = (GtkToggleButton * )off; + + return frame; +} + +static void callback_cross_on_off (GtkWidget *toggle, gpointer data) +{ + struct graph *g = (struct graph * )data; + + if (GTK_TOGGLE_BUTTON (toggle)->active) { + int x, y; + g->cross.draw = TRUE; + gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0); + cross_draw (g, x, y); + } else { + g->cross.draw = FALSE; + cross_erase (g); + } +} + +static GtkWidget *control_panel_create_graph_type_group (struct graph *g) +{ + GtkWidget *graph_tseqttrace, *graph_tseqstevens; + GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box; + GtkWidget *graph_frame; + + graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput"); + graph_tseqttrace = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)), + "Time/Sequence (tcptrace-style)"); + graph_tseqstevens = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)), + "Time/Sequence (Stevens'-style)"); + graph_rtt = gtk_radio_button_new_with_label ( + gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)), + "Round-trip Time"); + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE); + break; + case GRAPH_TSEQ_TCPTRACE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE); + break; + case GRAPH_THROUGHPUT: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE); + break; + case GRAPH_RTT: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE); + break; + } + graph_init = gtk_check_button_new_with_label ("Init on change"); + graph_sep = gtk_hseparator_new (); + graph_box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0); + graph_frame = gtk_frame_new ("Graph type:"); + gtk_container_add (GTK_CONTAINER (graph_frame), graph_box); + + gtk_object_set_data (GTK_OBJECT (graph_tseqstevens), "new-graph-type", + (gpointer )0); + gtk_object_set_data (GTK_OBJECT (graph_tseqttrace), "new-graph-type", + (gpointer )1); + gtk_object_set_data (GTK_OBJECT (graph_tput), "new-graph-type", + (gpointer )2); + gtk_object_set_data (GTK_OBJECT (graph_rtt), "new-graph-type", + (gpointer )3); + + gtk_signal_connect (GTK_OBJECT (graph_tseqttrace), "toggled", + (GtkSignalFunc )callback_graph_type, g); + gtk_signal_connect (GTK_OBJECT (graph_tseqstevens), "toggled", + (GtkSignalFunc )callback_graph_type, g); + gtk_signal_connect (GTK_OBJECT (graph_tput), "toggled", + (GtkSignalFunc )callback_graph_type, g); + gtk_signal_connect (GTK_OBJECT (graph_rtt), "toggled", + (GtkSignalFunc )callback_graph_type, g); + gtk_signal_connect (GTK_OBJECT (graph_init), "toggled", + (GtkSignalFunc )callback_graph_init_on_typechg, g); + + return graph_frame; +} + +static void callback_graph_type (GtkWidget *toggle, gpointer data) +{ + int old_type, new_type; + struct graph *g = (struct graph * )data; + + new_type = (int )gtk_object_get_data (GTK_OBJECT (toggle),"new-graph-type"); + + if (!GTK_TOGGLE_BUTTON (toggle)->active) + return; + + old_type = g->type; + g->type = new_type; + + graph_element_lists_free (g); + graph_element_lists_initialize (g); + + if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) { + /* throughput graph uses differently constructed segment list so we + * need to recreate it */ + graph_segment_list_free (g); + graph_segment_list_get (g); + } + + if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) { + g->geom.width = g->wp.width; + g->geom.height = g->wp.height; + g->geom.x = g->wp.x; + g->geom.y = g->wp.y; + } + g->x_axis->min = g->y_axis->min = 0; + gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE); + gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE); + graph_init_sequence (g); +} + +static void callback_graph_init_on_typechg (GtkWidget *toggle, gpointer data) +{ + ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE; +} + +static struct graph *graph_new (void) +{ + struct graph *g; + + g = (struct graph * )calloc (1, sizeof (struct graph)); + graph_element_lists_initialize (g); + + g->x_axis = (struct axis * )calloc (1, sizeof (struct axis)); + g->y_axis = (struct axis * )calloc (1, sizeof (struct axis)); + g->x_axis->g = g; + g->x_axis->flags = 0; + g->x_axis->flags |= AXIS_ORIENTATION; + g->x_axis->s.x = g->x_axis->s.y = 0; + g->x_axis->s.height = HAXIS_INIT_HEIGHT; + g->x_axis->p.x = VAXIS_INIT_WIDTH; + g->x_axis->p.height = HAXIS_INIT_HEIGHT; + g->y_axis->g = g; + g->y_axis->flags = 0; + g->y_axis->flags &= ~AXIS_ORIENTATION; + g->y_axis->p.x = g->y_axis->p.y = 0; + g->y_axis->p.width = VAXIS_INIT_WIDTH; + g->y_axis->s.x = 0; + g->y_axis->s.y = TITLEBAR_HEIGHT; + g->y_axis->s.width = VAXIS_INIT_WIDTH; + + return g; +} + +static void graph_initialize_values (struct graph *g) +{ + g->geom.width = g->wp.width = 750; + g->geom.height = g->wp.height = 550; + g->geom.x = g->wp.x = VAXIS_INIT_WIDTH; + g->geom.y = g->wp.y = TITLEBAR_HEIGHT; + g->flags = 0; + /* g->zoom.x = g->zoom.y = 1.0; */ + g->zoom.step_x = g->zoom.step_y = 1.2; + g->zoom.flags = 0; + g->cross.draw = g->cross.erase_needed = 0; + g->grab.grabbed = 0; + g->magnify.active = 0; + g->magnify.offset.x = g->magnify.offset.y = 0; + g->magnify.width = g->magnify.height = 250; + g->magnify.zoom.x = g->magnify.zoom.y = 10.0; + g->magnify.flags = 0; +} + +static void graph_put (struct graph *graph) +{ + struct graph *g; + if (graphs) { + for (g=graphs; g->next; g=g->next); + g->next = graph; + } else + graphs = graph; +} + +static void graph_init_sequence (struct graph *g) +{ + debug(DBS_FENTRY) puts ("graph_init_sequence()"); + + graph_type_dependent_initialize (g); + g->zoom.initial.x = g->zoom.x; + g->zoom.initial.y = g->zoom.y; + graph_element_lists_make (g); + g->x_axis->s.width = g->wp.width; + g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH; + g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height; + g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT; + g->y_axis->s.height = g->wp.height; + g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT; + graph_pixmaps_create (g); + axis_pixmaps_create (g->y_axis); + axis_pixmaps_create (g->x_axis); + graph_title_pixmap_create (g); + graph_title_pixmap_draw (g); + graph_title_pixmap_display (g); + graph_display (g); + axis_display (g->y_axis); + axis_display (g->x_axis); +} + +static void graph_type_dependent_initialize (struct graph *g) +{ + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + tseq_stevens_initialize (g); + break; + case GRAPH_THROUGHPUT: + tput_initialize (g); + break; + case GRAPH_RTT: + rtt_initialize (g); + break; + default: + break; + } +} + +static void graph_destroy (struct graph *g) +{ + struct graph *gtmp; + struct graph *p=NULL; + /* struct graph *tmp; */ + + debug(DBS_FENTRY) puts ("graph_destroy()"); + + for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next) + if (gtmp == g) + break; + + axis_destroy (g->x_axis); + axis_destroy (g->y_axis); + /* gtk_widget_destroy (g->drawing_area); */ + gtk_widget_destroy (g->gui.control_panel); + gtk_widget_destroy (g->toplevel); + /* gtk_widget_destroy (g->text); */ + gdk_gc_unref (g->fg_gc); + gdk_gc_unref (g->bg_gc); + gdk_font_unref (g->font); + gdk_pixmap_unref (g->pixmap[0]); + gdk_pixmap_unref (g->pixmap[1]); + free (g->x_axis); + free (g->y_axis); + free (g->title); + graph_segment_list_free (g); + graph_element_lists_free (g); +#if 0 + for (tmp=graphs; tmp; tmp=tmp->next) + printf ("%p next: %p\n", tmp, tmp->next); + printf ("p=%p, g=%p, p->next=%p, g->next=%p\n", + p, g, p ? p->next : NULL, g->next); +#endif + if (g==graphs) + graphs = g->next; + else + p->next = g->next; + free (g); +#if 0 + for (tmp=graphs; tmp; tmp=tmp->next) + printf ("%p next: %p\n", tmp, tmp->next); +#endif +} + +/* here we collect all the external data we will ever need */ +static void graph_segment_list_get (struct graph *g) +{ + frame_data *ptr; + char pd[4096]; + struct segment *segment=NULL, *last=NULL; + struct segment current; + int condition; + + debug(DBS_FENTRY) puts ("graph_segment_list_get()"); + get_headers (cfile.pd, ¤t); + if (g->type == GRAPH_THROUGHPUT) + condition = COMPARE_CURR_DIR; + else + condition = COMPARE_ANY_DIR; + + for (ptr=cfile.plist; ptr; ptr=ptr->next) { + wtap_seek_read (cfile.wth, ptr->file_off, &cfile.pseudo_header, + pd, 4096); + if (!segment) + segment = (struct segment * )malloc (sizeof (struct segment)); + if (!segment) + perror ("malloc failed"); + if (!get_headers (pd, segment)) + continue; /* not TCP over IP over Ethernet II */ + if (compare_headers (¤t, segment, condition)) { + segment->next = NULL; + segment->num = ptr->num; + segment->rel_secs = ptr->rel_secs; + segment->rel_usecs = ptr->rel_usecs; + segment->abs_secs = ptr->abs_secs; + segment->abs_usecs = ptr->abs_usecs; + segment->data = ntohs (segment->iphdr.tot_len) - +#ifdef BITFIELDS + 4*segment->iphdr.ihl - 4*segment->tcphdr.doff; +#else + 4*IHL(&(segment->iphdr)) - 4*DOFF(segment->tcphdr); +#endif /* BITFIELDS */ + if (g->segments) { + last->next = segment; + } else { + g->segments = segment; + } + last = segment; + if (ptr==cfile.current_frame) + g->current = segment; + } + segment = NULL; + } +} + +static int get_headers (char *pd, struct segment *hdrs) +{ + struct ether_header *e; + struct iphdr *ip; + struct tcphdr *tcp; + + e = (struct ether_header * )pd; + if (ntohs (e->ether_type) == ETHERTYPE_IP) { + ip = (struct iphdr * )((struct ether_header * )pd + 1); + } else if (((struct iphdr *)e)->protocol == IPPROTO_TCP) { + ip = (struct iphdr *)e; + } else { + /* printf ("not IP over Ethernet II or PPP\n"); */ + return FALSE; + } + if (ip->protocol != IPPROTO_TCP) { + /* printf ("transport protocol not TCP: %#1x\n", ip->protocol); */ + return FALSE; + } +#ifdef BITFIELDS + tcp = (struct tcphdr * )((char * )ip + 4*ip->ihl); +#else + tcp = (struct tcphdr * )((char * )ip + 4*IHL(ip)); +#endif + + hdrs->iphdr = *ip; + hdrs->tcphdr = *tcp; + return TRUE; +} + +static int compare_headers (struct segment *h1, struct segment *h2, int dir) +{ + if (dir == COMPARE_CURR_DIR) + return h1->iphdr.saddr == h2->iphdr.saddr && + h1->iphdr.daddr == h2->iphdr.daddr && + h1->tcphdr.source == h2->tcphdr.source && + h1->tcphdr.dest == h2->tcphdr.dest; + else + return (h1->iphdr.saddr == h2->iphdr.saddr && + h1->iphdr.daddr == h2->iphdr.daddr && + h1->tcphdr.source == h2->tcphdr.source && + h1->tcphdr.dest == h2->tcphdr.dest) || + (h1->iphdr.saddr == h2->iphdr.daddr && + h1->iphdr.daddr == h2->iphdr.saddr && + h1->tcphdr.source == h2->tcphdr.dest && + h1->tcphdr.dest == h2->tcphdr.source); +} + +static void graph_segment_list_free (struct graph *g) +{ + struct segment *segment; + + while (g->segments) { + segment = g->segments->next; + free (g->segments); + g->segments = segment; + } + g->segments = NULL; +} + +static void graph_element_lists_initialize (struct graph *g) +{ + g->elists = (struct element_list *)calloc (1, sizeof (struct element_list)); +} + +static void graph_element_lists_make (struct graph *g) +{ + debug(DBS_FENTRY) puts ("graph_element_lists_make()"); + + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + tseq_stevens_make_elmtlist (g); + break; + case GRAPH_TSEQ_TCPTRACE: + tseq_tcptrace_make_elmtlist (g); + break; + case GRAPH_THROUGHPUT: + tput_make_elmtlist (g); + break; + case GRAPH_RTT: + rtt_make_elmtlist (g); + break; + default: + printf ("graph_element_lists_make: unknown graph type: %d\n", g->type); + break; + } +} + +static void graph_element_lists_free (struct graph *g) +{ + struct element_list *list, *next_list; + +#if 0 + for (list=g->elists; list; list=list->next) + free (list->elements); + while (g->elists->next) { + list = g->elists->next->next; + free (g->elists->next); + g->elists->next = list; + } +#endif + + for (list=g->elists; list; list=next_list) { + free (list->elements); + next_list = list->next; + free (list); + } + g->elists = NULL; /* just to make debugging easier */ +} + +static void graph_title_pixmap_create (struct graph *g) +{ + if (g->title_pixmap) + gdk_pixmap_unref (g->title_pixmap); + + g->title_pixmap = gdk_pixmap_new (g->drawing_area->window, + g->x_axis->p.width, g->wp.y, -1); +} + +static void graph_title_pixmap_draw (struct graph *g) +{ + int i; + + gdk_draw_rectangle (g->title_pixmap, g->bg_gc, TRUE, 0, 0, + g->x_axis->p.width, g->wp.y); + for (i=0; g->title[i]; i++) { + gint w, h; + w = gdk_string_width (g->font, g->title[i]); + h = gdk_string_height (g->font, g->title[i]); + gdk_draw_string (g->title_pixmap, g->font, g->fg_gc, + g->wp.width/2 - w/2, 20+h + i*(h+3), g->title[i]); + } +} + +static void graph_title_pixmap_display (struct graph *g) +{ + gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap, + 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y); +} + +static void graph_pixmaps_create (struct graph *g) +{ + debug(DBS_FENTRY) puts ("graph_pixmaps_create()"); + + if (g->pixmap[0]) + gdk_pixmap_unref (g->pixmap[0]); + if (g->pixmap[1]) + gdk_pixmap_unref (g->pixmap[1]); + + g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window, + g->wp.width, g->wp.height, -1); + g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window, + g->wp.width, g->wp.height, -1); + + g->displayed = 0; +} + +static void graph_display (struct graph *g) +{ + graph_pixmap_draw (g); + graph_pixmaps_switch (g); + graph_pixmap_display (g); +} + +static void graph_pixmap_display (struct graph *g) +{ + gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, + g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y, + g->wp.width, g->wp.height); +} + +static void graph_pixmaps_switch (struct graph *g) +{ + g->displayed = 1 ^ g->displayed; +} + +static void graph_pixmap_draw (struct graph *g) +{ + struct element_list *list; + struct element *e; + int not_disp; + + debug(DBS_FENTRY) puts ("graph_display()"); + not_disp = 1 ^ g->displayed; + + gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE, + 0, 0, g->wp.width, g->wp.height); + + for (list=g->elists; list; list=list->next) + for (e=list->elements; e->type != ELMT_NONE; e++) { + switch (e->type) { + case ELMT_RECT: + break; + case ELMT_LINE: + draw_element_line (g, e); + break; + case ELMT_ARC: + draw_element_arc (g, e); + break; + default: + break; + } + } +} + +static void draw_element_line (struct graph *g, struct element *e) +{ + int x1, x2, y1, y2; + + debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), " + "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1, + e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num); + x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x); + x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x); + y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y); + y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y); + if (x1 > x2) { + int tmp=x2; + x2=x1; + x1=tmp; + } + if (y1 > y2) { + int tmp=y2; + y2=y1; + y1=tmp; + } + if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) || + (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) { + debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n", + x1, y1, x2, y2); + return; + } + if (x2 > g->wp.width-1) + x2 = g->wp.width-1; + if (x1 < 0) + x1 = 0; + if (y2 > g->wp.height-1) + y2 = g->wp.height-1; + if (y1 < 0) + y1 = 0; + debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2); + gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2); +} + +static void draw_element_arc (struct graph *g, struct element *e) +{ + int x1, x2, y1, y2; + + x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x); + x2 = e->p.arc.dim.width; + y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y); + y2 = e->p.arc.dim.height; + if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height) + return; + debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2); + gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1, + y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2); +} + +static void axis_pixmaps_create (struct axis *axis) +{ + debug(DBS_FENTRY) puts ("axis_pixmaps_create()"); + if (axis->pixmap[0]) + gdk_pixmap_unref (axis->pixmap[0]); + if (axis->pixmap[1]) + gdk_pixmap_unref (axis->pixmap[1]); + + axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window, + axis->p.width, axis->p.height, -1); + axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window, + axis->p.width, axis->p.height, -1); + + axis->displayed = 0; +} + +static void axis_destroy (struct axis *axis) +{ + gdk_pixmap_unref (axis->pixmap[0]); + gdk_pixmap_unref (axis->pixmap[1]); + free (axis->label); +} + +static void axis_display (struct axis *axis) +{ + if (axis->flags & AXIS_ORIENTATION) + h_axis_pixmap_draw (axis); + else + v_axis_pixmap_draw (axis); + axis_pixmaps_switch (axis); + axis_pixmap_display (axis); +} + +static void v_axis_pixmap_draw (struct axis *axis) +{ + struct graph *g = axis->g; + int i; + double major_tick; + int not_disp, rdigits, offset, imin, imax; + double bottom, top, j, fl, corr; + + debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()"); + bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) / + (double )g->geom.height * g->bounds.height; + bottom += axis->min; + top = (g->geom.height - (g->wp.y + (-g->geom.y))) / + (double )g->geom.height * g->bounds.height; + top += axis->min; + axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL); + + j = axis->major - floor (axis->major); + for (rdigits=0; rdigits<=6; rdigits++) { + j *= 10; + if (j<=0.000001) + break; + j = j - floor (j); + } + + not_disp = 1 ^ axis->displayed; + gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0, + axis->p.width, axis->p.height); + /* axis */ + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1, + (axis->p.height-axis->s.height)/2.0, axis->s.width - 1, + axis->p.height); + + offset = g->wp.y + (-g->geom.y); + fl = floor (axis->min / axis->major) * axis->major; + corr = rint ((axis->min - fl) * g->zoom.y); + + /* major ticks */ + major_tick = axis->major * g->zoom.y; + imin = (g->geom.height - offset + corr - g->wp.height) / major_tick + 1; + imax = (g->geom.height - offset + corr) / major_tick; + for (i=imin; i <= imax; i++) { + gint w, h; + char desc[32]; + int y = g->geom.height-1 - (int )rint (i * major_tick) - + offset + corr + axis->s.y; + + debug(DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->major + fl, y); + if (y < 0 || y > axis->p.height) + continue; + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, + axis->s.width - 15, y, axis->s.width - 1, y); + snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl); + w = gdk_string_width (g->font, desc); + h = gdk_string_height (g->font, desc); + gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc, + axis->s.width-15-4-w, y + h/2, desc); + } + /* minor ticks */ + if (axis->minor) { + double minor_tick = axis->minor * g->zoom.y; + imin = (g->geom.height - offset + corr - g->wp.height)/minor_tick + 1; + imax = (g->geom.height - offset + corr) / minor_tick; + for (i=imin; i <= imax; i++) { + int y = g->geom.height-1 - (int )rint (i*minor_tick) - + offset + corr + axis->s.y; + + debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y); + if (y > 0 && y < axis->p.height) + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, + axis->s.width - 8, y, axis->s.width - 1, y); + } + } + for (i=0; axis->label[i]; i++) { + gint w, h; + w = gdk_string_width (g->font, axis->label[i]); + h = gdk_string_height (g->font, axis->label[i]); + gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc, + (axis->p.width - w)/2 , TITLEBAR_HEIGHT-15 - i*(h+3), + axis->label[i]); + } +} + +static void h_axis_pixmap_draw (struct axis *axis) +{ + struct graph *g = axis->g; + int i; + double major_tick, minor_tick; + int not_disp, rdigits, offset, imin, imax; + double left, right, j, fl, corr; + + debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()"); + left = (g->wp.x-g->geom.x) / + (double )g->geom.width * g->bounds.width; + left += axis->min; + right = (g->wp.x-g->geom.x+g->wp.width) / + (double )g->geom.width * g->bounds.width; + right += axis->min; + axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL); + + j = axis->major - floor (axis->major); + for (rdigits=0; rdigits<=6; rdigits++) { + j *= 10; + if (j<=0.000001) + break; + j = j - floor (j); + } + + not_disp = 1 ^ axis->displayed; + gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0, + axis->p.width, axis->p.height); + /* axis */ + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0, + axis->s.width + (axis->p.width-axis->s.width)/2.0, 0); + offset = g->wp.x - g->geom.x; + + fl = floor (axis->min / axis->major) * axis->major; + corr = rint ((axis->min - fl) * g->zoom.x); + + /* major ticks */ + major_tick = axis->major*g->zoom.x; + imin = (offset + corr) / major_tick + 1; + imax = (offset + corr + axis->s.width) / major_tick; + for (i=imin; i <= imax; i++) { + char desc[32]; + int w, h; + int x = (int )rint (i * major_tick) - offset - corr; + + /* printf ("%f @ %d\n", i*axis->major + fl, x); */ + if (x < 0 || x > axis->s.width) + continue; + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15); + snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl); + w = gdk_string_width (g->font, desc); + h = gdk_string_height (g->font, desc); + gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc, + x - w/2, 15+h+4, desc); + } + if (axis->minor > 0) { + /* minor ticks */ + minor_tick = axis->minor*g->zoom.x; + imin = (offset + corr) / minor_tick + 1; + imax = (offset + corr + g->wp.width) / minor_tick; + for (i=imin; i <= imax; i++) { + int x = (int )rint (i * minor_tick) - offset - corr; + if (x > 0 && x < axis->s.width) + gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8); + } + } + for (i=0; axis->label[i]; i++) { + gint w, h; + w = gdk_string_width (g->font, axis->label[i]); + h = gdk_string_height (g->font, axis->label[i]); + gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc, + axis->s.width - w - 50, 15+2*h+15 + i*(h+3), + axis->label[i]); + } +} + +static void axis_pixmaps_switch (struct axis *axis) +{ + axis->displayed = 1 ^ axis->displayed; +} + +static void axis_pixmap_display (struct axis *axis) +{ + gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc, + axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y, + axis->p.width, axis->p.height); +} + +static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir) +{ + int i, j, ii, jj, ms; + double zoom, x, steps[3]={ 0.1, 0.5 }; + int dim, check_needed, diminished; + double majthresh[2]={2.0, 3.0}; + + debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()"); + debug(DBS_AXES_TICKS) + printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL"); + + zoom = axis_zoom_get (axis, dir); + x = xmax-x0; + for (i=-9; i<=12; i++) { + if (x / pow (10, i) < 1) + break; + } + --i; + ms = (int )(x / pow (10, i)); + + if (ms > 5) { + j = 0; + ++i; + } else if (ms > 2) + j = 1; + else + j = 0; + + axis->major = steps[j] * pow (10, i); + + debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->" + " axis->major=%f\n", zoom, x, i, ms, j, axis->major); + + /* let's compute minor ticks */ + jj = j; + ii = i; + axis_ticks_down (&ii, &jj); + axis->minor = steps[jj] * pow (10, ii); + /* we don't want minors if they would be less than 10 pixels apart */ + if (axis->minor*zoom < 10) { + debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: " + "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom); + axis->minor = 0; + } + + check_needed = TRUE; + diminished = FALSE; + while (check_needed) { + check_needed = FALSE; + dim = get_label_dim (axis, dir, xmax); + debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>" + " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n", + axis->major, axis->minor, axis->major*zoom/dim, + axis->minor*zoom/dim); + + /* corrections: if majors are less than majthresh[dir] times label + * dimension apart, we need to use bigger ones */ + if (axis->major*zoom / dim < majthresh[dir]) { + axis_ticks_up (&ii, &jj); + axis->minor = axis->major; + axis_ticks_up (&i, &j); + axis->major = steps[j] * pow (10, i); + check_needed = TRUE; + debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n", + axis->major); + } + /* if minor ticks are bigger than majthresh[dir] times label dimension, + * we could promote them to majors as well */ + if (axis->minor*zoom / dim > majthresh[dir] && !diminished) { + axis_ticks_down (&i, &j); + axis->major = axis->minor; + axis_ticks_down (&ii, &jj); + axis->minor = steps[jj] * pow (10, ii); + check_needed = TRUE; + diminished = TRUE; + + debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n", + axis->minor); + + if (axis->minor*zoom < 10) { + debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: " + "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom); + axis->minor = 0; + } + } + } + + debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> " + "axis->minor == %.1f\n", axis->major, axis->minor); +} + +static void axis_ticks_up (int *i, int *j) +{ + (*j)++; + if (*j>1) { + (*i)++; + *j=0; + } +} + +static void axis_ticks_down (int *i, int *j) +{ + (*j)--; + if (*j<0) { + (*i)--; + *j=1; + } +} + +static int get_label_dim (struct axis *axis, int dir, double label) +{ + double y; + char str[32]; + int rdigits, dim; + + /* First, let's compute how many digits to the right of radix + * we need to print */ + y = axis->major - floor (axis->major); + for (rdigits=0; rdigits<=6; rdigits++) { + y *= 10; + if (y<=0.000001) + break; + y = y - floor (y); + } + snprintf (str, 32, "%.*f", rdigits, label); + switch (dir) { + case AXIS_HORIZONTAL: + dim = gdk_string_width (axis->g->font, str); + break; + case AXIS_VERTICAL: + dim = gdk_string_height (axis->g->font, str); + break; + default: + puts ("initialize axis: an axis must be either horizontal or vertical"); + return -1; + break; + } + return dim; +} + +static double axis_zoom_get (struct axis *axis, int dir) +{ + switch (dir) { + case AXIS_HORIZONTAL: + return axis->g->zoom.x; + break; + case AXIS_VERTICAL: + return axis->g->zoom.y; + break; + default: + return -1; + break; + } +} + +static void graph_select_segment (struct graph *g, int x, int y) +{ + struct element_list *list; + struct element *e; + + debug(DBS_FENTRY) puts ("graph_select_segment()"); + + x -= g->geom.x; + y = g->geom.height-1 - (y - g->geom.y); + + for (list=g->elists; list; list=list->next) + for (e=list->elements; e->type != ELMT_NONE; e++) { + switch (e->type) { + case ELMT_RECT: + break; + case ELMT_LINE: + if (line_detect_collision (e, x, y)) { + int row = e->parent->num - 1; + update_packet_list (row); + } + break; + case ELMT_ARC: + if (arc_detect_collision (e, x, y)) { + int row = e->parent->num - 1; + update_packet_list (row); + } + break; + default: + break; + } + } +} + +static int line_detect_collision (struct element *e, int x, int y) +{ + int x1, y1, x2, y2; + + if (e->p.line.dim.x1 < e->p.line.dim.x2) { + x1 = (int )rint (e->p.line.dim.x1); + x2 = (int )rint (e->p.line.dim.x2); + } else { + x1 = (int )rint (e->p.line.dim.x2); + x2 = (int )rint (e->p.line.dim.x1); + } + if (e->p.line.dim.y1 < e->p.line.dim.y2) { + y1 = (int )rint (e->p.line.dim.y1); + y2 = (int )rint (e->p.line.dim.y2); + } else { + y1 = (int )rint (e->p.line.dim.y2); + y2 = (int )rint (e->p.line.dim.y1); + } + /* + printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y); + */ + if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2)) + return TRUE; + else + return FALSE; +} + +static int arc_detect_collision (struct element *e, int x, int y) +{ + int x1, y1, x2, y2; + + x1 = (int )rint (e->p.arc.dim.x); + x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width); + y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height); + y2 = (int )rint (e->p.arc.dim.y); + /* + printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y); + */ + if (x1<=x && x<=x2 && y1<=y && y<=y2) + return TRUE; + else + return FALSE; +} + +static void update_packet_list (int row) +{ + select_packet (&cfile, row); + if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) != + GTK_VISIBILITY_FULL) + gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.5, 0.5); + GTK_CLIST(packet_list)->focus_row = row; + gtk_clist_select_row(GTK_CLIST(packet_list), row, -1); +} + +static void cross_xor (struct graph *g, int x, int y) +{ + if (x > g->wp.x && x < g->wp.x+g->wp.width && + y >= g->wp.y && y < g->wp.y+g->wp.height) { + gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x, + y, g->wp.x + g->wp.width, y); + gdk_draw_line (g->drawing_area->window, xor_gc, x, + g->wp.y, x, g->wp.y + g->wp.height); + } +} + +static void cross_draw (struct graph *g, int x, int y) +{ + cross_xor (g, x, y); + g->cross.x = x; + g->cross.y = y; + g->cross.erase_needed = 1; +} + +static void cross_erase (struct graph *g) +{ + cross_xor (g, g->cross.x, g->cross.y); + g->cross.erase_needed = 0; +} + +static void magnify_create (struct graph *g, int x, int y) +{ + struct graph *mg; + struct element_list *list, *new_list; + struct ipoint pos, offsetpos; + GdkEvent *e=NULL; + + mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph)); + memcpy ((void * )mg, (void * )g, sizeof (struct graph)); + + mg->toplevel = gtk_window_new (GTK_WINDOW_POPUP); + mg->drawing_area = mg->toplevel; + gtk_widget_set_usize (mg->toplevel, g->magnify.width, g->magnify.height); + gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK + /* | GDK_ENTER_NOTIFY_MASK */ + /* | GDK_ALL_EVENTS_MASK */ + ); + + mg->wp.x = 0; + mg->wp.y = 0; + mg->wp.width = g->magnify.width; + mg->wp.height = g->magnify.height; + mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x); + mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y); + mg->zoom.x = (mg->geom.width - 1) / g->bounds.width; + mg->zoom.y = (mg->geom.height- 1) / g->bounds.height; + + /* in order to keep original element lists intact we need our own */ + graph_element_lists_initialize (mg); + list = g->elists->next; + new_list = mg->elists; + for ( ; list; list=list->next) { + new_list->next = + (struct element_list * )malloc (sizeof (struct element_list)); + new_list = new_list->next; + new_list->next = NULL; + new_list->elements = NULL; + } + graph_element_lists_make (mg); + + gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y); + g->magnify.x = pos.x + x - g->magnify.width/2; + g->magnify.y = pos.y + y - g->magnify.height/2; + offsetpos.x = g->magnify.x + g->magnify.offset.x; + offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0; + offsetpos.y = g->magnify.y + g->magnify.offset.y; + offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0; + gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y); + magnify_get_geom (g, x, y); + + gtk_widget_show (mg->drawing_area); + + /* we need to wait for the first expose event before we start drawing */ + while (!gdk_events_pending ()); + do { + e = gdk_event_get (); + if (e) { + if (e->any.type == GDK_EXPOSE) { + gdk_event_free (e); + break; + } + gdk_event_free (e); + } + } while (e); + + mg->pixmap[0] = mg->pixmap[1] = NULL; + graph_pixmaps_create (mg); + magnify_draw (g); + g->magnify.active = 1; +} + +static void magnify_move (struct graph *g, int x, int y) +{ + struct ipoint pos, offsetpos; + + gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y); + g->magnify.x = pos.x + x - g->magnify.width/2; + g->magnify.y = pos.y + y - g->magnify.height/2; + offsetpos.x = g->magnify.x + g->magnify.offset.x; + offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0; + offsetpos.y = g->magnify.y + g->magnify.offset.y; + offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0; + magnify_get_geom (g, x, y); + gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x, + offsetpos.y); + magnify_draw (g); +} + +static void magnify_destroy (struct graph *g) +{ + struct element_list *list; + struct graph *mg = g->magnify.g; + + gtk_widget_destroy (GTK_WIDGET (mg->drawing_area)); + gdk_pixmap_unref (mg->pixmap[0]); + gdk_pixmap_unref (mg->pixmap[1]); + for (list=mg->elists; list; list=list->next) + free (list->elements); + while (mg->elists->next) { + list = mg->elists->next->next; + free (mg->elists->next); + mg->elists->next = list; + } + free (g->magnify.g); + g->magnify.active = 0; +} + +static void magnify_get_geom (struct graph *g, int x, int y) +{ + int posx, posy; + + gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy); + + g->magnify.g->geom.x = g->geom.x; + g->magnify.g->geom.y = g->geom.y; + + g->magnify.g->geom.x -= + (int )rint ((g->magnify.g->geom.width - g->geom.width) * + ((x-g->geom.x)/(double )g->geom.width)); + g->magnify.g->geom.y -= + (int )rint ((g->magnify.g->geom.height - g->geom.height) * + ((y-g->geom.y)/(double )g->geom.height)); + + /* we have coords of origin of graph relative to origin of g->toplevel. + * now we need them to relate to origin of magnify window */ + g->magnify.g->geom.x -= (g->magnify.x - posx); + g->magnify.g->geom.y -= (g->magnify.y - posy); +} + +static void magnify_draw (struct graph *g) +{ + int not_disp = 1 ^ g->magnify.g->displayed; + + graph_pixmap_draw (g->magnify.g); + /* graph pixmap is almost ready, just add border */ + gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0, + g->magnify.width - 1, 0); + gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, + g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height); + gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0, + 0, g->magnify.height - 1); + gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, + g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1); + + graph_pixmaps_switch (g->magnify.g); + graph_pixmap_display (g->magnify.g); + +} + +static gint configure_event (GtkWidget *widget, GdkEventConfigure *event) +{ + struct graph *g; + struct { + double x, y; + } zoom; + int cur_g_width, cur_g_height; + int cur_wp_width, cur_wp_height; + + debug(DBS_FENTRY) puts ("configure_event()"); + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + cur_wp_width = g->wp.width; + cur_wp_height = g->wp.height; + g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH; + g->wp.height = event->height - g->x_axis->p.height - g->wp.y; + g->x_axis->s.width = g->wp.width; + g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH; + g->y_axis->p.height = g->wp.height + g->wp.y; + g->y_axis->s.height = g->wp.height; + g->x_axis->p.y = g->y_axis->p.height; + zoom.x = (double )g->wp.width / cur_wp_width; + zoom.y = (double )g->wp.height / cur_wp_height; + cur_g_width = g->geom.width; + cur_g_height = g->geom.height; + g->geom.width = (int )rint (g->geom.width * zoom.x); + g->geom.height = (int )rint (g->geom.height * zoom.y); + g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width; + g->zoom.y = (double )(g->geom.height -1) / g->bounds.height; + /* g->zoom.initial.x = g->zoom.x; */ + /* g->zoom.initial.y = g->zoom.y; */ + + g->geom.x = g->wp.x - (double )g->geom.width/cur_g_width * + (g->wp.x - g->geom.x); + g->geom.y = g->wp.y - (double )g->geom.height/cur_g_height * + (g->wp.y - g->geom.y); +#if 0 + printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); " + "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width, + g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height, + g->zoom.x, g->zoom.y); +#endif + + update_zoom_spins (g); + graph_element_lists_make (g); + graph_pixmaps_create (g); + graph_title_pixmap_create (g); + axis_pixmaps_create (g->y_axis); + axis_pixmaps_create (g->x_axis); + /* we don't do actual drawing here; we leave it to expose handler */ + graph_pixmap_draw (g); + graph_pixmaps_switch (g); + graph_title_pixmap_draw (g); + h_axis_pixmap_draw (g->x_axis); + axis_pixmaps_switch (g->x_axis); + v_axis_pixmap_draw (g->y_axis); + axis_pixmaps_switch (g->y_axis); + return TRUE; +} + +static gint expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + struct graph *g; + + debug(DBS_FENTRY) puts ("expose_event()"); + + if (event->count) + return TRUE; + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + /* lower left corner */ + gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0, + g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height); + /* right margin */ + gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, + g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height); + + graph_pixmap_display (g); + graph_title_pixmap_display (g); + axis_pixmap_display (g->x_axis); + axis_pixmap_display (g->y_axis); + + return TRUE; +} + +static gint button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + struct graph *g; + + debug(DBS_FENTRY) puts ("button_press_event()"); + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + if (event->button == 3) { + if (event->state & GDK_CONTROL_MASK) + magnify_create (g, (int )rint (event->x), (int )rint (event->y)); + else { + g->grab.x = (int )rint (event->x) - g->geom.x; + g->grab.y = (int )rint (event->y) - g->geom.y; + g->grab.grabbed = TRUE; + } +#ifdef WIN32 + /* Windows mouse control: */ + /* [<ctrl>-left] - select packet */ + /* [left] - zoom in */ + /* [<shift>-left] - zoom out */ + } else if (event->button == 1) { + if (event->state & GDK_CONTROL_MASK) { + graph_select_segment (g, (int)event->x, (int)event->y); + } else { +#else /* WIN32 */ + } else if (event->button == 2) { +#endif + int cur_width = g->geom.width, cur_height = g->geom.height; + struct { double x, y; } factor; + + if (g->zoom.flags & ZOOM_OUT) { + if (g->zoom.flags & ZOOM_HLOCK) + factor.x = 1.0; + else + factor.x = 1 / g->zoom.step_x; + if (g->zoom.flags & ZOOM_VLOCK) + factor.y = 1.0; + else + factor.y = 1 / g->zoom.step_y; + } else { + if (g->zoom.flags & ZOOM_HLOCK) + factor.x = 1.0; + else + factor.x = g->zoom.step_x; + if (g->zoom.flags & ZOOM_VLOCK) + factor.y = 1.0; + else + factor.y = g->zoom.step_y; + } + + g->geom.width = (int )rint (g->geom.width * factor.x); + g->geom.height = (int )rint (g->geom.height * factor.y); + if (g->geom.width < g->wp.width) + g->geom.width = g->wp.width; + if (g->geom.height < g->wp.height) + g->geom.height = g->wp.height; + g->zoom.x = (g->geom.width - 1) / g->bounds.width; + g->zoom.y = (g->geom.height- 1) / g->bounds.height; + + g->geom.x -= (int )rint ((g->geom.width - cur_width) * + ((event->x-g->geom.x)/(double )cur_width)); + g->geom.y -= (int )rint ((g->geom.height - cur_height) * + ((event->y-g->geom.y)/(double )cur_height)); + + if (g->geom.x > g->wp.x) + g->geom.x = g->wp.x; + if (g->geom.y > g->wp.y) + g->geom.y = g->wp.y; + if (g->wp.x + g->wp.width > g->geom.x + g->geom.width) + g->geom.x = g->wp.width + g->wp.x - g->geom.width; + if (g->wp.y + g->wp.height > g->geom.y + g->geom.height) + g->geom.y = g->wp.height + g->wp.y - g->geom.height; +#if 0 + printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), " + "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y, + g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width, + g->wp.height, g->zoom.x, g->zoom.y); +#endif + graph_element_lists_make (g); + graph_display (g); + axis_display (g->y_axis); + axis_display (g->x_axis); + update_zoom_spins (g); + if (g->cross.draw) + cross_draw (g, event->x, event->y); +#ifndef WIN32 + } else if (event->button == 1) { + graph_select_segment (g, (int )event->x, (int )event->y); +#else /* WIN32 */ + } +#endif + } + return TRUE; +} + +static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +{ + struct graph *g; + int x, y; + GdkModifierType state; + + /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */ + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else { + x = event->x; + y = event->y; + state = event->state; + } + + /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1 + * is pressed while pointer is in motion, we will receive one more motion + * notify *before* we get the button press. This last motion notify works + * with stale grab coordinates */ + if (state & GDK_BUTTON3_MASK) { + if (g->grab.grabbed) { + g->geom.x = x-g->grab.x; + g->geom.y = y-g->grab.y; + + if (g->geom.x > g->wp.x) + g->geom.x = g->wp.x; + if (g->geom.y > g->wp.y) + g->geom.y = g->wp.y; + if (g->wp.x + g->wp.width > g->geom.x + g->geom.width) + g->geom.x = g->wp.width + g->wp.x - g->geom.width; + if (g->wp.y + g->wp.height > g->geom.y + g->geom.height) + g->geom.y = g->wp.height + g->wp.y - g->geom.height; + graph_display (g); + axis_display (g->y_axis); + axis_display (g->x_axis); + if (g->cross.draw) + cross_draw (g, x, y); + } else if (g->magnify.active) + magnify_move (g, x, y); + } else if (state & GDK_BUTTON1_MASK) { + graph_select_segment (g, x, y); + if (g->cross.erase_needed) + cross_erase (g); + if (g->cross.draw) + cross_draw (g, x, y); + } else { + if (g->cross.erase_needed) + cross_erase (g); + if (g->cross.draw) + cross_draw (g, x, y); + } + + return TRUE; +} + +static gint button_release_event (GtkWidget *widget, GdkEventButton *event) +{ + struct graph *g; + + debug(DBS_FENTRY) puts ("button_release_event()"); + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + if (event->button == 3) + g->grab.grabbed = FALSE; + + if (g->magnify.active) + magnify_destroy (g); + return TRUE; +} + +static gint key_press_event (GtkWidget *widget, GdkEventKey *event) +{ + struct graph *g; + + debug(DBS_FENTRY) puts ("key_press_event()"); + + for (g=graphs; g; g=g->next) + if (g->toplevel == widget) + break; + + if (event->keyval == 32 /*space*/) { + g->cross.draw ^= 1; +#if 0 + if (g->cross.draw) { + int x, y; + gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0); + cross_draw (g); + } else if (g->cross.erase_needed) { + cross_erase (g); + } +#endif + /* toggle buttons emit their "toggled" signals so don't bother doing + * any real work here, it will be done in signal handlers */ + if (g->cross.draw) + gtk_toggle_button_set_active (g->cross.on_toggle, TRUE); + else + gtk_toggle_button_set_active (g->cross.off_toggle, TRUE); + } else if (event->keyval == 't') + toggle_time_origin (g); + else if (event->keyval == 's') + toggle_seq_origin (g); + else if (event->keyval == GDK_Shift_L) { + /* g->zoom.flags |= ZOOM_OUT; */ + gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE); + } + return TRUE; +} + +static gint key_release_event (GtkWidget *widget, GdkEventKey *event) +{ + struct graph *g; + + debug(DBS_FENTRY) puts ("key_release_event()"); + + for (g=graphs; g; g=g->next) + if (g->toplevel == widget) + break; + + if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) { + /* g->zoom.flags &= ~ZOOM_OUT; */ + gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE); + } + return TRUE; +} + +static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event) +{ + struct graph *g; + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + if (g->cross.erase_needed) + cross_erase (g); + + return TRUE; +} + +static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event) +{ + struct graph *g; + + for (g=graphs; g; g=g->next) + if (g->drawing_area == widget) + break; + + /* graph_pixmap_display (g); */ + if (g->cross.draw) { + int x, y; + gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0); + cross_draw (g, x, y); + } + return TRUE; +} + +static void toggle_time_origin (struct graph *g) +{ + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + tseq_stevens_toggle_time_origin (g); + break; + case GRAPH_TSEQ_TCPTRACE: + tseq_tcptrace_toggle_time_origin (g); + break; + case GRAPH_THROUGHPUT: + tput_toggle_time_origin (g); + break; + default: + break; + } + axis_display (g->x_axis); +} + +static void toggle_seq_origin (struct graph *g) +{ + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + tseq_stevens_toggle_seq_origin (g); + axis_display (g->y_axis); + break; + case GRAPH_TSEQ_TCPTRACE: + tseq_tcptrace_toggle_seq_origin (g); + axis_display (g->y_axis); + break; + case GRAPH_RTT: + rtt_toggle_seq_origin (g); + axis_display (g->x_axis); + break; + default: + break; + } +} + +static int get_num_dsegs (struct graph *g) +{ + int count; + struct segment *tmp; + + for (tmp=g->segments, count=0; tmp; tmp=tmp->next) { + if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) { + count++; + } + } + return count; +} + +static int get_num_acks (struct graph *g) +{ + int count; + struct segment *tmp; + + for (tmp=g->segments, count=0; tmp; tmp=tmp->next) { + if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) { + count++; + } + } + return count; +} + +/* + * Stevens-style time-sequence grapH + */ + +static void tseq_stevens_read_config (struct graph *g) +{ + debug(DBS_FENTRY) puts ("tseq_stevens_read_config()"); + + g->s.tseq_stevens.seq_width = 4; + g->s.tseq_stevens.seq_height = 4; + g->s.tseq_stevens.flags = 0; + + g->title = (char ** )malloc (2 * sizeof (char *)); + g->title[0] = "Time/Sequence Graph"; + g->title[1] = NULL; + g->y_axis->label = (char ** )malloc (3 * sizeof (char * )); + g->y_axis->label[0] = "number[B]"; + g->y_axis->label[1] = "Sequence"; + g->y_axis->label[2] = NULL; + g->x_axis->label = (char ** )malloc (2 * sizeof (char * )); + g->x_axis->label[0] = "Time[s]"; + g->x_axis->label[1] = NULL; +} + +static void tseq_stevens_initialize (struct graph *g) +{ + debug(DBS_FENTRY) puts ("tseq_stevens_initialize()"); + tseq_stevens_get_bounds (g); + + g->x_axis->min = 0; + g->y_axis->min = 0; + + switch (g->type) { + case GRAPH_TSEQ_STEVENS: + tseq_stevens_read_config(g); + break; + case GRAPH_TSEQ_TCPTRACE: + tseq_tcptrace_read_config(g); + break; + } +} + +static void tseq_stevens_get_bounds (struct graph *g) +{ + struct segment *tmp, *last, *first; + double t0, tmax, y0, ymax; + + for (first=g->segments; first->next; first=first->next) { + if (compare_headers (g->current, first, COMPARE_CURR_DIR)) + break; + } + last = NULL; + ymax = 0; + for (tmp=g->segments; tmp; tmp=tmp->next) { + unsigned int highest_byte_num; + last = tmp; + if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) + highest_byte_num = ntohl (tmp->tcphdr.seq) + tmp->data; + else + highest_byte_num = ntohl (tmp->tcphdr.ack_seq); + if (highest_byte_num > ymax) + ymax = highest_byte_num; + } + if (!last) { + puts ("tseq_stevens_get_bounds: segment list corrupted!"); + return; + } + + t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0; + tmax = last->rel_secs + last->rel_usecs / 1000000.0; + y0 = ntohl (first->tcphdr.seq); + + g->bounds.x0 = t0; + g->bounds.y0 = y0; + g->bounds.width = tmax - t0; + g->bounds.height = ymax - y0; + g->zoom.x = (g->geom.width - 1) / g->bounds.width; + g->zoom.y = (g->geom.height -1) / g->bounds.height; +} + +static void tseq_stevens_make_elmtlist (struct graph *g) +{ + struct segment *tmp; + struct element *elements, *e; + double x0 = g->bounds.x0, y0 = g->bounds.y0; + + debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()"); + if (g->elists->elements == NULL) { + int n = 1 + get_num_dsegs (g); + e = elements = (struct element * )malloc (n*sizeof (struct element)); + } else + e = elements = g->elists->elements; + + for (tmp=g->segments; tmp; tmp=tmp->next) { + double secs, seqno; + + if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) + continue; + + secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0); + seqno = g->zoom.y * (ntohl (tmp->tcphdr.seq) - y0); + + e->type = ELMT_ARC; + e->parent = tmp; + e->gc = g->fg_gc; + e->p.arc.dim.width = g->s.tseq_stevens.seq_width; + e->p.arc.dim.height = g->s.tseq_stevens.seq_height; + e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0; + e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0; + e->p.arc.filled = TRUE; + e->p.arc.angle1 = 0; + e->p.arc.angle2 = 23040; + e++; + } + e->type = ELMT_NONE; + g->elists->elements = elements; +} + +static void tseq_stevens_toggle_seq_origin (struct graph *g) +{ + g->s.tseq_stevens.flags ^= SEQ_ORIGIN; + + if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO) + g->y_axis->min = g->bounds.y0; + else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */ + g->y_axis->min = 0; +} + +static void tseq_stevens_toggle_time_origin (struct graph *g) +{ + g->s.tseq_stevens.flags ^= TIME_ORIGIN; + + if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP) + g->x_axis->min = g->bounds.x0; + else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */ + g->x_axis->min = 0; +} + +/* + * tcptrace-style time-sequence graph + */ + +static void tseq_tcptrace_read_config (struct graph *g) +{ + GdkColormap *colormap; + GdkColor color; + + g->s.tseq_tcptrace.flags = 0; + g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window); + g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window); + g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window); + colormap = gdk_window_get_colormap (g->drawing_area->window); + gdk_color_parse ("black", &color); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color); + gdk_color_parse ("LightSlateGray", &color); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color); + gdk_color_parse ("LightGray", &color); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color); + + g->elists->next = (struct element_list * ) + malloc (sizeof (struct element_list)); + g->elists->next->next = NULL; + g->elists->next->elements = NULL; + + g->title = (char ** )malloc (2 * sizeof (char *)); + g->title[0] = "Time/Sequence Graph"; + g->title[1] = NULL; + g->y_axis->label = (char ** )malloc (3 * sizeof (char * )); + g->y_axis->label[0] = "number[B]"; + g->y_axis->label[1] = "Sequence"; + g->y_axis->label[2] = NULL; + g->x_axis->label = (char ** )malloc (2 * sizeof (char * )); + g->x_axis->label[0] = "Time[s]"; + g->x_axis->label[1] = NULL; +} + +static void tseq_tcptrace_make_elmtlist (struct graph *g) +{ + struct segment *tmp; + struct element *elements0, *e0; /* list of elmts with prio 0 */ + struct element *elements1, *e1; /* list of elmts with prio 1 */ + double x0, y0; + double p_t; /* ackno, window and time of previous segment */ + double p_ackno, p_win; + int toggle=0; + + debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()"); + + if (g->elists->elements == NULL) { + int n = 1 + 4*get_num_acks(g); + e0 = elements0 = (struct element * )malloc (n*sizeof (struct element)); + } else + e0 = elements0 = g->elists->elements; + + if (g->elists->next->elements == NULL ) { + int n = 1 + 3*get_num_dsegs(g); + e1 = elements1 = (struct element * )malloc (n*sizeof (struct element)); + } else + e1 = elements1 = g->elists->next->elements; + + x0 = g->bounds.x0; + y0 = g->bounds.y0; + /* initialize "previous" values */ + for (tmp=g->segments; tmp; tmp=tmp->next) + if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) + break; + /* + p_ackno = (unsigned int )(g->zoom.y * (ntohl (tmp->tcphdr.ack_seq) - y0)); + */ + p_ackno = 0; + p_win = g->zoom.y * ntohs (tmp->tcphdr.window); + p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0; + for (tmp=g->segments; tmp; tmp=tmp->next) { + double secs, seqno, data; + double x; + + secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0; + x = secs - x0; + x *= g->zoom.x; + if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) { + /* forward direction -> we need seqno and amount of data */ + double y1, y2; + + seqno = ntohl (tmp->tcphdr.seq); +#ifdef BITFIELDS + if (tmp->tcphdr.syn) +#else + if (TCP_SYN (tmp->tcphdr)) +#endif /* BITFIELDS */ + data = 1; + else + data = tmp->data; + + y1 = g->zoom.y * (seqno - y0); + y2 = g->zoom.y * (seqno - y0 + data); + e1->type = ELMT_LINE; + e1->parent = tmp; + e1->gc = g->s.tseq_tcptrace.gc_seq; + e1->p.line.dim.x1 = e1->p.line.dim.x2 = x; + e1->p.line.dim.y1 = y1; + e1->p.line.dim.y2 = y2; + e1++; + e1->type = ELMT_LINE; + e1->parent = tmp; + e1->gc = g->s.tseq_tcptrace.gc_seq; + e1->p.line.dim.x1 = x - 1; + e1->p.line.dim.x2 = x + 1; + e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1; + e1++; + e1->type = ELMT_LINE; + e1->parent = tmp; + e1->gc = g->s.tseq_tcptrace.gc_seq; + e1->p.line.dim.x1 = x + 1; + e1->p.line.dim.x2 = x - 1; + e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2; + e1++; + } else { + double ackno, win; +#ifdef BITFIELDS + if (tmp->tcphdr.syn && !tmp->tcphdr.ack) +#else + if (TCP_SYN (tmp->tcphdr) && ! TCP_ACK (tmp->tcphdr)) +#endif + /* SYN's have ACK==0 and are useless here */ + continue; + /* backward direction -> we need ackno and window */ + ackno = (ntohl (tmp->tcphdr.ack_seq) - y0) * g->zoom.y; + win = ntohs (tmp->tcphdr.window) * g->zoom.y; + + /* ack line */ + e0->type = ELMT_LINE; + e0->parent = tmp; + e0->gc = g->s.tseq_tcptrace.gc_ack[toggle]; + e0->p.line.dim.x1 = p_t; + e0->p.line.dim.y1 = p_ackno; + e0->p.line.dim.x2 = x; + e0->p.line.dim.y2 = p_ackno; + e0++; + e0->type = ELMT_LINE; + e0->parent = tmp; + e0->gc = g->s.tseq_tcptrace.gc_ack[toggle]; + e0->p.line.dim.x1 = x; + e0->p.line.dim.y1 = p_ackno; + e0->p.line.dim.x2 = x; + e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4; + e0++; + /* window line */ + e0->type = ELMT_LINE; + e0->parent = tmp; + e0->gc = g->s.tseq_tcptrace.gc_ack[toggle]; + e0->p.line.dim.x1 = p_t; + e0->p.line.dim.y1 = p_win + p_ackno; + e0->p.line.dim.x2 = x; + e0->p.line.dim.y2 = p_win + p_ackno; + e0++; + e0->type = ELMT_LINE; + e0->parent = tmp; + e0->gc = g->s.tseq_tcptrace.gc_ack[toggle]; + e0->p.line.dim.x1 = x; + e0->p.line.dim.y1 = p_win + p_ackno; + e0->p.line.dim.x2 = x; + e0->p.line.dim.y2 = win + ackno; + e0++; + p_ackno = ackno; + p_win = win; + p_t = x; + toggle = 1^toggle; + } + } + e0->type = ELMT_NONE; + e1->type = ELMT_NONE; + g->elists->elements = elements0; + g->elists->next->elements = elements1; +} + +static void tseq_tcptrace_toggle_seq_origin (struct graph *g) +{ + g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN; + + if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO) + g->y_axis->min = g->bounds.y0; + else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */ + g->y_axis->min = 0; +} + +static void tseq_tcptrace_toggle_time_origin (struct graph *g) +{ + g->s.tseq_tcptrace.flags ^= TIME_ORIGIN; + + if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP) + g->x_axis->min = g->bounds.x0; + else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */ + g->x_axis->min = 0; +} + +/* + * throughput graph + */ + +static void tput_make_elmtlist (struct graph *g) +{ + struct segment *tmp, *oldest; + struct element *elements, *e; + int i, sum=0; + double dtime, tput; + + if (g->elists->elements == NULL) { + int n = 1 + get_num_dsegs (g); + e = elements = (struct element * )malloc (n*sizeof (struct element)); + } else + e = elements = g->elists->elements; + + for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) { + double time = tmp->rel_secs + tmp->rel_usecs/1000000.0; + dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0); + if (i>g->s.tput.nsegs) { + sum -= oldest->data; + oldest=oldest->next; + } + sum += tmp->data; + tput = sum / dtime; + /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */ + + e->type = ELMT_ARC; + e->parent = tmp; + e->gc = g->fg_gc; + e->p.arc.dim.width = g->s.tput.width; + e->p.arc.dim.height = g->s.tput.height; + e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0; + e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0; + e->p.arc.filled = TRUE; + e->p.arc.angle1 = 0; + e->p.arc.angle2 = 23040; + e++; + } + e->type = ELMT_NONE; + g->elists->elements = elements; +} + +/* Purpose of <graph_type>_initialize functions: + * - find maximum and minimum for both axes + * - call setup routine for style struct */ +static void tput_initialize (struct graph *g) +{ + struct segment *tmp, *oldest, *last; + int i, sum=0; + double dtime, tput, tputmax=0; + double t0, tmax, y0, ymax; + + debug(DBS_FENTRY) puts ("tput_initialize()"); + + tput_read_config(g); + + for (last=g->segments; last->next; last=last->next); + for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) { + dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 - + (oldest->rel_secs + oldest->rel_usecs/1000000.0); + if (i>g->s.tput.nsegs) { + sum -= oldest->data; + oldest=oldest->next; + } + sum += tmp->data; + tput = sum / dtime; + debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); + if (tput > tputmax) + tputmax = tput; + } + + t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0; + tmax = last->rel_secs + last->rel_usecs / 1000000.0; + y0 = 0; + ymax = tputmax; + + g->bounds.x0 = t0; + g->bounds.y0 = y0; + g->bounds.width = tmax - t0; + g->bounds.height = ymax - y0; + g->zoom.x = (g->geom.width - 1) / g->bounds.width; + g->zoom.y = (g->geom.height -1) / g->bounds.height; +} + +static void tput_read_config (struct graph *g) +{ + debug(DBS_FENTRY) puts ("tput_read_config()"); + + g->s.tput.width = 4; + g->s.tput.height = 4; + g->s.tput.nsegs = 20; + + g->title = (char ** )malloc (2 * sizeof (char *)); + g->title[0] = "Throughput Graph"; + g->title[1] = NULL; + g->y_axis->label = (char ** )malloc (3 * sizeof (char * )); + g->y_axis->label[0] = "[B/s]"; + g->y_axis->label[1] = "Throughput"; + g->y_axis->label[2] = NULL; + g->x_axis->label = (char ** )malloc (2 * sizeof (char * )); + g->x_axis->label[0] = "Time[s]"; + g->x_axis->label[1] = NULL; + g->s.tput.flags = 0; +} + +static void tput_toggle_time_origin (struct graph *g) +{ + g->s.tput.flags ^= TIME_ORIGIN; + + if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP) + g->x_axis->min = g->bounds.x0; + else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */ + g->x_axis->min = 0; +} + +/* RTT graph */ + +static void rtt_read_config (struct graph *g) +{ + debug(DBS_FENTRY) puts ("rtt_read_config()"); + + g->s.rtt.width = 4; + g->s.rtt.height = 4; + g->s.rtt.flags = 0; + + g->title = (char ** )malloc (2 * sizeof (char *)); + g->title[0] = "Round Trip Time Graph"; + g->title[1] = NULL; + g->y_axis->label = (char ** )malloc (3 * sizeof (char * )); + g->y_axis->label[0] = "RTT [s]"; + g->y_axis->label[1] = NULL; + g->x_axis->label = (char ** )malloc (2 * sizeof (char * )); + g->x_axis->label[0] = "Sequence Number[B]"; + g->x_axis->label[1] = NULL; +} + +static void rtt_initialize (struct graph *g) +{ + struct segment *tmp, *first=NULL; + struct unack *unack = NULL, *u; + double rttmax=0; + double x0, xmax=0, y0, ymax; + + debug(DBS_FENTRY) puts ("rtt_initialize()"); + + rtt_read_config (g); + + for (tmp=g->segments; tmp; tmp=tmp->next) { + if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) { + unsigned int seqno = ntohl (tmp->tcphdr.seq); + + if (!first) + first= tmp; + + if (tmp->data && !rtt_is_retrans (unack, seqno)) { + double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0; + u = rtt_get_new_unack (time, seqno); + if (!u) return; + rtt_put_unack_on_list (&unack, u); + } + + if (seqno + tmp->data > xmax) + xmax = seqno + tmp->data; + } else { + unsigned int ackno = ntohl (tmp->tcphdr.ack_seq); + double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0; + struct unack *v; + + for (u=unack; u; u=v) + if (ackno > u->seqno) { + double rtt = time - u->time; + if (rtt > rttmax) + rttmax = rtt; + v=u->next; + rtt_delete_unack_from_list (&unack, u); + } else + v=u->next; + } + } + + x0 = ntohl (first->tcphdr.seq); + y0 = 0; + ymax = rttmax; + + g->bounds.x0 = x0; + g->bounds.y0 = y0; + g->bounds.width = xmax - x0; + g->bounds.height = ymax - y0; + g->zoom.x = g->geom.width / g->bounds.width; + g->zoom.y = g->geom.height / g->bounds.height; +} + +static int rtt_is_retrans (struct unack *list, unsigned int seqno) +{ + struct unack *u; + + for (u=list; u; u=u->next) + if (u->seqno == seqno) + return TRUE; + + return FALSE; +} + +static struct unack *rtt_get_new_unack (double time, unsigned int seqno) +{ + struct unack *u; + + u = (struct unack * )malloc (sizeof (struct unack)); + if (!u) + return NULL; + u->next = NULL; + u->time = time; + u->seqno = seqno; + return u; +} + +static void rtt_put_unack_on_list (struct unack **l, struct unack *new) +{ + struct unack *u, *list = *l; + + for (u=list; u; u=u->next) + if (!u->next) + break; + + if (u) + u->next = new; + else + *l = new; +} + +static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead) +{ + struct unack *u, *list = *l; + + if (!dead || !list) + return; + + if (dead==list) { + *l = list->next; + free (list); + } else + for (u=list; u; u=u->next) + if (u->next == dead) { + u->next = u->next->next; + free (dead); + break; + } +} + +static void rtt_make_elmtlist (struct graph *g) +{ + struct segment *tmp; + struct unack *unack = NULL, *u; + struct element *elements, *e; + + debug(DBS_FENTRY) puts ("rtt_make_elmtlist()"); + + if (g->elists->elements == NULL) { + int n = 1 + get_num_dsegs (g); + e = elements = (struct element * )malloc (n*sizeof (struct element)); + } else + e = elements = g->elists->elements; + + for (tmp=g->segments; tmp; tmp=tmp->next) { + if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) { + unsigned int seqno = ntohl (tmp->tcphdr.seq); + + if (tmp->data && !rtt_is_retrans (unack, seqno)) { + double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0; + u = rtt_get_new_unack (time, seqno); + if (!u) return; + rtt_put_unack_on_list (&unack, u); + } + } else { + unsigned int ackno = ntohl (tmp->tcphdr.ack_seq); + double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0; + struct unack *v; + + for (u=unack; u; u=v) + if (ackno > u->seqno) { + double rtt = time - u->time; + + e->type = ELMT_ARC; + e->parent = tmp; + e->gc = g->fg_gc; + e->p.arc.dim.width = g->s.rtt.width; + e->p.arc.dim.height = g->s.rtt.height; + e->p.arc.dim.x = g->zoom.x * (u->seqno - g->bounds.x0) + - g->s.rtt.width/2.0; + e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0; + e->p.arc.filled = TRUE; + e->p.arc.angle1 = 0; + e->p.arc.angle2 = 23040; + e++; + + v=u->next; + rtt_delete_unack_from_list (&unack, u); + } else + v=u->next; + } + } + e->type = ELMT_NONE; + g->elists->elements = elements; +} + +static void rtt_toggle_seq_origin (struct graph *g) +{ + g->s.rtt.flags ^= SEQ_ORIGIN; + + if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO) + g->x_axis->min = g->bounds.x0; + else + g->x_axis->min = 0; +} + +#ifdef WIN32 +/* replacement of Unix rint() for Windows */ +static int rint (double x) +{ + char *buf; + int i,dec,sig; + + buf = _fcvt(x, 0, &dec, &sig); + i = atoi(buf); + if(sig == 1) { + i = i * -1; + } + return(i); +} +#endif diff --git a/gtk/tcp_graph.h b/gtk/tcp_graph.h new file mode 100644 index 0000000000..ea302f0450 --- /dev/null +++ b/gtk/tcp_graph.h @@ -0,0 +1,27 @@ +/* tcp_graph.h + * Declarations for TCP graph drawing code + * By Pavel Mores <pvl@uh.cz> + * Win32 port: rwh@unifiedtech.com + * + * $Id: tcp_graph.h,v 1.1 2001/12/08 09:27:51 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +extern void tcp_graph_cb (GtkWidget *, gpointer, guint); |