HACK: 1. try to match RowsetProperties
[wireshark-wip.git] / ui / gtk / tcp_graph.c
blobf1120cb41acee55f285fe2774cafcd5a631e944d
1 /* tcp_graph.c
2 * TCP graph drawing code
3 * By Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
6 * $Id$
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "config.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <math.h>
32 #include <string.h>
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36 #if GTK_CHECK_VERSION(3,0,0)
37 # include <gdk/gdkkeysyms-compat.h>
38 #endif
40 #include <epan/packet.h>
41 #include <epan/to_str.h>
42 #include <epan/etypes.h>
43 #include <epan/ppptypes.h>
44 #include <epan/epan_dissect.h>
45 #include <epan/dissectors/packet-tcp.h>
46 #include <epan/address.h>
48 #include "../../globals.h"
49 #include "../../stat_menu.h"
51 #include "ui/tap-tcp-stream.h"
52 #include "ui/utf8_entities.h"
54 #include "ui/gtk/gui_utils.h"
55 #include "ui/gtk/dlg_utils.h"
56 #include "ui/gtk/gui_stat_menu.h"
58 #include "ui/gtk/old-gtk-compat.h"
60 #define TCP_SYN(flags) ( flags & TH_SYN )
61 #define TCP_ACK(flags) ( flags & TH_ACK )
62 #define TCP_FIN(flags) ( flags & TH_FIN )
64 #define TXT_WIDTH 850
65 #define TXT_HEIGHT 550
67 /* initialize_axis() */
68 #define AXIS_HORIZONTAL 0
69 #define AXIS_VERTICAL 1
71 #define WINDOW_TITLE_LENGTH 256
73 #define MOUSE_BUTTON_LEFT 1
74 #define MOUSE_BUTTON_MIDDLE 2
75 #define MOUSE_BUTTON_RIGHT 3
77 struct rect {
78 double x, y, width, height;
81 struct line {
82 double x1, y1, x2, y2;
85 struct irect {
86 int x, y, width, height;
89 struct ipoint {
90 int x, y;
93 struct zoomfactor {
94 double x, y;
97 typedef enum {
98 ELMT_NONE=0,
99 ELMT_RECT=1,
100 ELMT_LINE=2,
101 ELMT_ELLIPSE=3
102 } ElementType;
104 struct rect_params {
105 struct rect dim;
106 gint filled;
109 struct line_params {
110 struct line dim;
113 struct ellipse_params {
114 struct rect dim;
117 struct element {
118 ElementType type;
119 GdkRGBA *elment_color_p;
120 struct segment *parent;
121 union {
122 struct ellipse_params ellipse;
123 struct rect_params rect;
124 struct line_params line;
125 } p;
128 struct element_list {
129 struct element_list *next;
130 struct element *elements;
133 struct axis {
134 struct gtk_graph *g; /* which graph we belong to */
135 GtkWidget *drawing_area;
136 #if GTK_CHECK_VERSION(2,22,0)
137 cairo_surface_t *surface[2];
138 #else
139 GdkPixmap *pixmap[2];
140 #endif
141 int displayed;
142 #define AXIS_ORIENTATION 1 << 0
143 int flags;
144 /* dim and orig (relative to origin of window) of axis' pixmap */
145 struct irect p;
146 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
147 struct irect s;
148 gdouble min, max;
149 gdouble major, minor; /* major and minor ticks */
150 const char **label;
153 #define HAXIS_INIT_HEIGHT 70
154 #define VAXIS_INIT_WIDTH 100
155 #define TITLEBAR_HEIGHT 50
156 #define RMARGIN_WIDTH 30
158 struct style_tseq_tcptrace {
159 GdkRGBA seq_color;
160 GdkRGBA ack_color[2];
161 GdkRGBA sack_color[2];
162 int flags;
165 struct style_tseq_stevens {
166 int seq_width;
167 int seq_height;
168 int flags;
171 struct style_tput {
172 int width, height;
173 int ma_size;
174 int flags;
177 struct style_rtt {
178 int width, height;
179 int flags;
182 struct style_wscale {
183 int win_width;
184 int win_height;
185 int flags;
188 /* style flags */
189 #define SEQ_ORIGIN 0x1
190 /* show absolute sequence numbers (not differences from isn) */
191 #define SEQ_ORIGIN_ZERO 0x1
192 #define SEQ_ORIGIN_ISN 0x0
193 #define TIME_ORIGIN 0x10
194 /* show time from beginning of capture as opposed to time from beginning
195 * of the connection */
196 #define TIME_ORIGIN_CAP 0x10
197 #define TIME_ORIGIN_CONN 0x0
199 struct cross {
200 int x, y;
201 int draw; /* indicates whether we should draw cross at all */
202 int erase_needed;
203 GtkToggleButton *on_toggle;
204 GtkToggleButton *off_toggle;
207 struct bounds {
208 double x0, y0, width, height;
211 struct zoom {
212 double x, y;
215 struct zooms {
216 double x, y;
217 double step_x, step_y;
218 struct zoom initial;
219 #define ZOOM_OUT (1 << 0)
220 #define ZOOM_HLOCK (1 << 1)
221 #define ZOOM_VLOCK (1 << 2)
222 #define ZOOM_STEPS_SAME (1 << 3)
223 #define ZOOM_STEPS_KEEP_RATIO (1 << 4)
224 int flags;
225 /* unfortunately, we need them both because gtk_toggle_button_set_active()
226 * with second argument FALSE doesn't do anything, somehow */
227 struct {
228 GtkToggleButton *in_toggle;
229 GtkToggleButton *out_toggle;
230 GtkEntry *h_zoom;
231 GtkEntry *v_zoom;
232 GtkSpinButton *h_step;
233 GtkSpinButton *v_step;
234 } widget;
237 struct grab {
238 int grabbed;
239 int x, y;
242 struct magnify {
243 int active;
244 int x, y;
245 struct ipoint offset;
246 int width, height;
247 struct zoom zoom;
248 struct gtk_graph *g;
249 #define MAGZOOMS_SAME (1U << 0)
250 #define MAGZOOMS_SAME_RATIO (1U << 1)
251 #define MAGZOOMS_IGNORE (1U << 31)
252 guint flags;
253 struct {
254 GtkSpinButton *h_zoom, *v_zoom;
255 } widget;
258 struct gtk_graph {
259 #define GRAPH_DESTROYED (1 << 0)
260 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
261 int flags;
262 GtkWidget *toplevel; /* keypress handler needs this */
263 GtkWidget *drawing_area;
264 GtkWidget *text; /* text widget for seg list - probably
265 * temporary
267 PangoFontDescription *font; /* font used for annotations etc. */
268 #if GTK_CHECK_VERSION(2,22,0)
269 cairo_surface_t *title_surface;
270 cairo_surface_t *surface[2];
271 #else
272 GdkPixmap *title_pixmap;
273 GdkPixmap *pixmap[2];
274 #endif
275 int displayed; /* which of both pixmaps is on screen right now */
276 struct {
277 GtkWidget *control_panel;
278 /* this belongs to style structs of graph types that make use of it */
279 GtkToggleButton *time_orig_conn, *seq_orig_isn;
280 } gui;
281 const char **title;
283 /* Next 4 attribs describe the graph in natural units, before any scaling.
284 * For example, if we want to display graph of TCP conversation that
285 * started 112.309845 s after beginning of the capture and ran until
286 * 479.093582 s, 237019 B went through the connection (in one direction)
287 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
288 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
289 struct bounds bounds;
291 /* dimensions and position of the graph, both expressed already in pixels.
292 * x and y give the position of upper left corner of the graph relative
293 * to origin of the graph window, size is basically bounds*zoom */
294 struct irect geom;
296 /* viewport (=graph window area which is reserved for graph itself), its
297 * size and position relative to origin of the graph window */
298 struct irect wp;
300 /* whether and where the graph has been 'grabbed' and may now be moved */
301 struct grab grab;
303 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
304 * pixels, we have to scale the graph down by factor of 0.002109. This
305 * number would be zoom.y. Obviously, both directions have separate zooms.*/
306 struct zooms zoom;
308 struct cross cross;
309 gboolean zoomrect_erase_needed;
310 struct magnify magnify;
311 struct axis *x_axis, *y_axis;
313 struct tcp_graph tg;
315 struct element_list *elists; /* element lists */
316 union {
317 struct style_tseq_stevens tseq_stevens;
318 struct style_tseq_tcptrace tseq_tcptrace;
319 struct style_tput tput;
320 struct style_rtt rtt;
321 struct style_wscale wscale;
322 } s;
323 /* This allows keyboard to set the radio button */
324 struct {
325 GtkToggleButton *graph_rtt, *graph_tput, *graph_tseqstevens, *graph_tseqttrace;
326 GtkToggleButton *graph_wscale;
327 } gt;
330 static int refnum=0;
332 #define debug(section) if (debugging & section)
333 /* print function entry points */
334 #define DBS_FENTRY (1 << 0)
335 #define DBS_AXES_TICKS (1 << 1)
336 #define DBS_AXES_DRAWING (1 << 2)
337 #define DBS_GRAPH_DRAWING (1 << 3)
338 #define DBS_TPUT_ELMTS (1 << 4)
339 /*static int debugging = DBS_FENTRY;*/
340 static int debugging = 0;
341 /*static int debugging = DBS_AXES_TICKS;*/
342 /*static int debugging = DBS_AXES_DRAWING;*/
343 /*static int debugging = DBS_GRAPH_DRAWING;*/
344 /*static int debugging = DBS_TPUT_ELMTS;*/
346 static void create_gui(struct gtk_graph * );
347 #if 0
348 static void create_text_widget(struct gtk_graph * );
349 static void display_text(struct gtk_graph * );
350 #endif
351 static void create_drawing_area(struct gtk_graph * );
352 static void control_panel_create(struct gtk_graph * );
353 static GtkWidget *control_panel_create_zoom_group(struct gtk_graph * );
354 static GtkWidget *control_panel_create_magnify_group(struct gtk_graph * );
355 static GtkWidget *control_panel_create_cross_group(struct gtk_graph * );
356 static GtkWidget *control_panel_create_zoomlock_group(struct gtk_graph * );
357 static GtkWidget *control_panel_create_graph_type_group(struct gtk_graph * );
358 static void control_panel_add_zoom_page(struct gtk_graph * , GtkWidget * );
359 static void control_panel_add_magnify_page(struct gtk_graph * , GtkWidget * );
360 static void control_panel_add_origin_page(struct gtk_graph * , GtkWidget * );
361 static void control_panel_add_cross_page(struct gtk_graph * , GtkWidget * );
362 static void control_panel_add_graph_type_page(struct gtk_graph * , GtkWidget * );
363 static void callback_toplevel_destroy(GtkWidget * , gpointer );
364 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
365 static void callback_close(GtkWidget * , gpointer );
366 static void callback_time_origin(GtkWidget * , gpointer );
367 static void callback_seq_origin(GtkWidget * , gpointer );
368 static void callback_zoomlock_h(GtkWidget * , gpointer );
369 static void callback_zoomlock_v(GtkWidget * , gpointer );
370 static void callback_zoom_inout(GtkWidget * , gpointer );
371 static void callback_zoom_step(GtkWidget * , gpointer );
372 static void callback_zoom_flags(GtkWidget * , gpointer );
373 static void callback_cross_on_off(GtkWidget * , gpointer );
374 static void callback_mag_width(GtkWidget * , gpointer );
375 static void callback_mag_height(GtkWidget * , gpointer );
376 static void callback_mag_x(GtkWidget * , gpointer );
377 static void callback_mag_y(GtkWidget * , gpointer );
378 static void callback_mag_zoom(GtkWidget * , gpointer );
379 static void callback_mag_flags(GtkWidget * , gpointer );
380 static void callback_graph_type(GtkWidget * , gpointer );
381 static void callback_graph_init_on_typechg(GtkWidget * , gpointer );
382 static void callback_create_help(GtkWidget * , gpointer );
383 static void get_mouse_position(GtkWidget *, int *pointer_x, int *pointer_y, GdkModifierType *mask);
384 static void update_zoom_spins(struct gtk_graph * );
385 static void graph_type_dependent_initialize(struct gtk_graph * );
386 static struct gtk_graph *graph_new(void);
387 static void graph_destroy(struct gtk_graph * );
388 static void graph_initialize_values(struct gtk_graph * );
389 static void graph_init_sequence(struct gtk_graph * );
390 static void draw_element_line(struct gtk_graph * , struct element * , cairo_t *cr, GdkRGBA *new_color);
391 static void draw_element_ellipse(struct gtk_graph * , struct element * , cairo_t *cr);
392 static void graph_display(struct gtk_graph * );
393 static void graph_pixmaps_create(struct gtk_graph * );
394 static void graph_pixmaps_switch(struct gtk_graph * );
395 static void graph_pixmap_draw(struct gtk_graph * );
396 static void graph_pixmap_display(struct gtk_graph * );
397 static void graph_element_lists_make(struct gtk_graph * );
398 static void graph_element_lists_free(struct gtk_graph * );
399 static void graph_element_lists_initialize(struct gtk_graph * );
400 static void graph_title_pixmap_create(struct gtk_graph * );
401 static void graph_title_pixmap_draw(struct gtk_graph * );
402 static void graph_title_pixmap_display(struct gtk_graph * );
403 static void graph_select_segment(struct gtk_graph * , int , int );
404 static int line_detect_collision(struct element * , int , int );
405 static int ellipse_detect_collision(struct element * , int , int );
406 static void axis_pixmaps_create(struct axis * );
407 static void axis_pixmaps_switch(struct axis * );
408 static void axis_display(struct axis * );
409 static void v_axis_pixmap_draw(struct axis * );
410 static void h_axis_pixmap_draw(struct axis * );
411 static void axis_pixmap_display(struct axis * );
412 static void axis_compute_ticks(struct axis * , double , double , int );
413 static double axis_zoom_get(struct axis * , int );
414 static void axis_ticks_up(int * , int * );
415 static void axis_ticks_down(int * , int * );
416 static void axis_destroy(struct axis * );
417 static int get_label_dim(struct axis * , int , double );
418 static void toggle_crosshairs(struct gtk_graph *g);
419 static void toggle_time_origin(struct gtk_graph * );
420 static void toggle_seq_origin(struct gtk_graph * );
421 static void restore_initial_graph_view(struct gtk_graph *g);
422 static void cross_draw(struct gtk_graph * , int , int );
423 static void cross_erase(struct gtk_graph * );
424 static void zoomrect_draw(struct gtk_graph * , int , int );
425 static void zoomrect_erase(struct gtk_graph * );
426 static void magnify_move(struct gtk_graph * , int , int );
427 static void magnify_create(struct gtk_graph * , int , int );
428 static void magnify_destroy(struct gtk_graph * );
429 static void magnify_draw(struct gtk_graph * );
430 static void magnify_get_geom(struct gtk_graph * , int , int );
431 static gboolean configure_event(GtkWidget * , GdkEventConfigure * , gpointer );
432 #if GTK_CHECK_VERSION(3,0,0)
433 static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
434 #else
435 static gboolean expose_event(GtkWidget * , GdkEventExpose * , gpointer );
436 #endif
437 static gboolean button_press_event(GtkWidget * , GdkEventButton * , gpointer );
438 static gboolean button_release_event(GtkWidget * , GdkEventButton * , gpointer );
439 static gboolean motion_notify_event(GtkWidget * , GdkEventMotion * , gpointer );
440 static gboolean leave_notify_event(GtkWidget * , GdkEventCrossing * , gpointer );
441 static gboolean enter_notify_event(GtkWidget * , GdkEventCrossing * , gpointer );
442 static gboolean key_press_event(GtkWidget * , GdkEventKey * , gpointer );
443 static gboolean key_release_event(GtkWidget * , GdkEventKey * , gpointer );
444 static void tseq_initialize(struct gtk_graph * );
445 static void tseq_get_bounds(struct gtk_graph * );
446 static void tseq_stevens_read_config(struct gtk_graph * );
447 static void tseq_stevens_make_elmtlist(struct gtk_graph * );
448 static void tseq_stevens_toggle_seq_origin(struct gtk_graph * );
449 static void tseq_stevens_toggle_time_origin(struct gtk_graph * );
450 static void tseq_tcptrace_read_config(struct gtk_graph * );
451 static void tseq_tcptrace_make_elmtlist(struct gtk_graph * );
452 static void tseq_tcptrace_toggle_seq_origin(struct gtk_graph * );
453 static void tseq_tcptrace_toggle_time_origin(struct gtk_graph * );
454 static void tput_initialize(struct gtk_graph * );
455 static void tput_read_config(struct gtk_graph * );
456 static void tput_make_elmtlist(struct gtk_graph * );
457 static void tput_toggle_time_origin(struct gtk_graph * );
458 static void rtt_read_config(struct gtk_graph * );
459 static void rtt_initialize(struct gtk_graph * );
460 static void rtt_make_elmtlist(struct gtk_graph * );
461 static void rtt_toggle_seq_origin(struct gtk_graph * );
462 static void wscale_initialize(struct gtk_graph *);
463 static void wscale_read_config(struct gtk_graph *);
464 static void wscale_make_elmtlist(struct gtk_graph *);
465 #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
466 /* Starting VS2013, rint already defined in math.h. No need to redefine */
467 static int rint(double ); /* compiler template for Windows */
468 #endif
470 /* This should arguably be part of the graph, but in practice you can
471 only click on one graph at a time, so this is probably OK */
472 static struct irect zoomrect;
475 * Uncomment the following define to revert WIN32 to
476 * use original mouse button controls
479 /* #define ORIGINAL_WIN32_BUTTONS 1 */
481 /* XXX - what about OS X? */
482 /* XXX: Needs work to ensire that the columns line up properly in both Gtk2 & Gtk3 */
483 /* What is the proper way to do this ?? */
484 static char helptext[] =
485 "Here's what you can do:\n"
486 "\n"
487 #ifdef ORIGINAL_WIN32_BUTTONS
488 " <Ctrl>-Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
489 "\n"
490 " Left Mouse Button zooms in (towards area under mouse pointer)\n"
491 " <Shift>-Left Mouse Button zooms out\n"
492 "\n"
493 " Right Mouse Button moves the graph (if zoomed in)\n"
494 " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
495 #else /* !ORIGINAL_WIN32_BUTTONS */
496 " Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
497 " can also drag to zoom in on a rectangular region\n"
498 "\n"
499 " Middle Mouse Button zooms in (towards area under cursor)\n"
500 " <Shift>-Middle Mouse Button zooms out\n"
501 "\n"
502 " Right Mouse Button moves the graph (if zoomed in)\n"
503 " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
504 #endif
505 "\n"
506 "\n"
507 " '1' display Round Trip Time Graph\n"
508 " '2' display Throughput Graph\n"
509 " '3' display Time/Sequence Graph (Stevens)\n"
510 " '4' display Time/Sequence Graph (tcptrace)\n"
511 " '5' display Window Scaling Graph\n"
512 "\n"
513 " <Space bar> toggles crosshairs on/off\n"
514 "\n"
515 " 'i' or '+' zoom in (towards area under mouse pointer)\n"
516 " 'o' or '-' zoom out\n"
517 " 'r' or <Home> restore graph to initial state (zoom out max)\n"
518 " 's' toggles relative/absolute sequence numbers\n"
519 " 't' toggles time origin\n"
520 " 'g' go to frame under cursor in Wireshark's packet list (if possible)\n"
521 "\n"
522 " <Left> move view left by 100 pixels (if zoomed in)\n"
523 " <Right> move view right 100 pixels (if zoomed in)\n"
524 " <Up> move view up by 100 pixels (if zoomed in)\n"
525 " <Down> move view down by 100 pixels (if zoomed in)\n"
526 "\n"
527 " <Shift><Left> move view left by 10 pixels (if zoomed in)\n"
528 " <Shift><Right> move view right 10 pixels (if zoomed in)\n"
529 " <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
530 " <Shift><Down> move view down by 10 pixels (if zoomed in)\n"
531 "\n"
532 " <Ctrl><Left> move view left by 1 pixel (if zoomed in)\n"
533 " <Ctrl><Right> move view right 1 pixel (if zoomed in)\n"
534 " <Ctrl><Up> move view up by 1 pixel (if zoomed in)\n"
535 " <Ctrl><Down> move view down by 1 pixel (if zoomed in)\n"
538 #if 0
539 static void debug_coord(struct gtk_graph *g, const char *c)
541 static guint count = 0;
543 count++;
544 printf("%u: %s\n", count, c);
545 printf("%u: g->geom.width %d\n", count, g->geom.width);
546 printf("%u: g->geom.height %d\n", count, g->geom.height);
547 printf("%u: g->geom.x %d\n", count, g->geom.x);
548 printf("%u: g->geom.y %d\n", count, g->geom.y);
550 printf("%u: g->wp.width %d\n", count, g->wp.width);
551 printf("%u: g->wp.height %d\n", count, g->wp.height);
552 printf("%u: g->wp.x %d\n", count, g->wp.x);
553 printf("%u: g->wp.y %d\n", count, g->wp.y);
554 printf("---------------\n");
556 #endif
558 static void set_busy_cursor(GdkWindow *w)
560 GdkCursor *cursor;
562 cursor = gdk_cursor_new(GDK_WATCH);
563 gdk_window_set_cursor(w, cursor);
564 gdk_flush();
565 #if GTK_CHECK_VERSION(3,0,0)
566 g_object_unref(cursor);
567 #else
568 gdk_cursor_unref(cursor);
569 #endif
572 static void unset_busy_cursor(GdkWindow *w, gboolean cross)
574 GdkCursor *cursor;
576 if (cross) {
577 cursor = gdk_cursor_new(GDK_CROSSHAIR);
578 gdk_window_set_cursor(w, cursor);
579 gdk_flush();
580 #if GTK_CHECK_VERSION(3,0,0)
581 g_object_unref(cursor);
582 #else
583 gdk_cursor_unref(cursor);
584 #endif
585 } else {
586 gdk_window_set_cursor(w, NULL);
587 gdk_flush();
591 void tcp_graph_cb(GtkAction *action, gpointer user_data _U_)
593 struct segment current;
594 struct gtk_graph *g;
595 const gchar *name;
596 tcp_graph_type graph_type;
598 name = gtk_action_get_name(action);
599 if (strcmp(name, "/Statistics/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens") == 0) {
600 graph_type = GRAPH_TSEQ_STEVENS;
601 } else if (strcmp(name, "/Statistics/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace") == 0) {
602 graph_type = GRAPH_TSEQ_TCPTRACE;
603 } else if (strcmp(name, "/Statistics/TCPStreamGraphMenu/Throughput-Graph") == 0) {
604 graph_type = GRAPH_THROUGHPUT;
605 } else if (strcmp(name, "/Statistics/TCPStreamGraphMenu/RTT-Graph") == 0) {
606 graph_type = GRAPH_RTT;
607 } else if (strcmp(name, "/Statistics/TCPStreamGraphMenu/Window-Scaling-Graph") == 0) {
608 graph_type = GRAPH_WSCALE;
609 } else {
610 return;
613 debug(DBS_FENTRY) puts("tcp_graph_cb()");
615 if (!select_tcpip_session(&cfile, &current)) {
616 return;
619 if (! (g = graph_new()))
620 return;
622 refnum++;
623 graph_initialize_values(g);
625 g->tg.type = graph_type;
627 graph_segment_list_get(&cfile, &g->tg, FALSE);
628 create_gui(g);
629 /* display_text(g); */
630 graph_init_sequence(g);
634 void tcp_graph_known_stream_launch(address *src_address, guint16 src_port,
635 address *dst_address, guint16 dst_port)
637 struct gtk_graph *g;
639 if (!(g = graph_new())) {
640 return;
643 refnum++;
644 graph_initialize_values(g);
646 /* Can set stream info for graph now */
647 COPY_ADDRESS(&g->tg.src_address, src_address);
648 g->tg.src_port = src_port;
649 COPY_ADDRESS(&g->tg.dst_address, dst_address);
650 g->tg.dst_port = dst_port;
652 /* This graph type is arguably the most useful, so start there */
653 g->tg.type = GRAPH_TSEQ_TCPTRACE;
655 /* Get our list of segments from the packet list */
656 graph_segment_list_get(&cfile, &g->tg, TRUE);
658 create_gui(g);
659 graph_init_sequence(g);
663 static void create_gui(struct gtk_graph *g)
665 /* ToDo: Ensure that drawing area window doesn't
666 * (completely) cover the contraol_panel window.
668 debug(DBS_FENTRY) puts("create_gui()");
669 /* create_text_widget(g); */
670 control_panel_create(g);
671 create_drawing_area(g);
676 static void create_drawing_area(struct gtk_graph *g)
678 #if GTK_CHECK_VERSION(3,0,0)
679 GtkStyleContext *context;
680 #endif
681 char *display_name;
682 char window_title[WINDOW_TITLE_LENGTH];
683 GtkAllocation widget_alloc;
684 #if 0
685 /* Prep. to include the controls in the graph window */
686 GtkWidget *frame;
687 GtkWidget *vbox;
688 GtkWidget *hbox;
689 #endif
690 debug(DBS_FENTRY) puts("create_drawing_area()");
692 /* Set title of window with file + conversation details */
693 display_name = cf_get_display_name(&cfile);
694 g_snprintf(window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d " UTF8_RIGHTWARDS_ARROW " %s:%d",
695 refnum,
696 display_name,
697 ep_address_to_str(&g->tg.src_address),
698 g->tg.src_port,
699 ep_address_to_str(&g->tg.dst_address),
700 g->tg.dst_port
702 g_free(display_name);
703 g->toplevel = dlg_window_new("Tcp Graph");
704 gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
705 gtk_widget_set_name(g->toplevel, "Test Graph");
707 /* Create the drawing area */
708 g->drawing_area = gtk_drawing_area_new();
709 g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
710 gtk_widget_set_size_request(g->drawing_area,
711 g->wp.width + g->wp.x + RMARGIN_WIDTH,
712 g->wp.height + g->wp.y + g->x_axis->s.height);
713 gtk_widget_show(g->drawing_area);
715 #if GTK_CHECK_VERSION(3,0,0)
716 g_signal_connect(g->drawing_area, "draw", G_CALLBACK(draw_event), g);
717 #else
718 g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), g);
719 #endif
720 /* this has to be done later, after the widget has been shown */
722 g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event), g);
725 g_signal_connect(g->drawing_area, "button_press_event",
726 G_CALLBACK(button_press_event), g);
727 g_signal_connect(g->drawing_area, "button_release_event",
728 G_CALLBACK(button_release_event), g);
729 g_signal_connect(g->drawing_area, "motion_notify_event",
730 G_CALLBACK(motion_notify_event), g);
731 g_signal_connect(g->drawing_area, "leave_notify_event",
732 G_CALLBACK(leave_notify_event), g);
733 g_signal_connect(g->drawing_area, "enter_notify_event",
734 G_CALLBACK(enter_notify_event), g);
735 g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
736 /* why doesn't drawing area send key_press_signals? */
737 g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), g);
738 g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
740 gtk_widget_set_events(g->toplevel, GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
742 gtk_widget_set_events(g->drawing_area,
743 GDK_EXPOSURE_MASK
744 | GDK_LEAVE_NOTIFY_MASK
745 | GDK_ENTER_NOTIFY_MASK
746 | GDK_BUTTON_PRESS_MASK
747 | GDK_BUTTON_RELEASE_MASK
748 | GDK_POINTER_MOTION_MASK
749 | GDK_POINTER_MOTION_HINT_MASK);
751 #if 0
752 /* Prep. to include the controls in the graph window */
754 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
755 gtk_container_add(GTK_CONTAINER(g->toplevel), vbox);
756 gtk_container_set_border_width(GTK_CONTAINER(g->toplevel), 5);
757 gtk_widget_show(vbox);
759 frame = gtk_frame_new(NULL);
760 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
761 gtk_container_add(GTK_CONTAINER(frame), g->drawing_area);
762 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
763 gtk_widget_show(frame);
766 /*gtk_box_pack_start(GTK_BOX(vbox), g->gui.control_panel, FALSE, FALSE, 0);*/
768 hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
769 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
770 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
771 gtk_widget_show(hbox);
773 create_ctrl_area(g, hbox);
775 #endif
777 gtk_container_add(GTK_CONTAINER(g->toplevel), g->drawing_area);
778 gtk_widget_show(g->toplevel);
780 /* in case we didn't get what we asked for */
781 gtk_widget_get_allocation(GTK_WIDGET(g->drawing_area), &widget_alloc);
782 g->wp.width = widget_alloc.width - g->wp.x - RMARGIN_WIDTH;
783 g->wp.height = widget_alloc.height - g->wp.y - g->x_axis->s.height;
785 #if GTK_CHECK_VERSION(3,0,0)
786 context = gtk_widget_get_style_context(g->drawing_area);
787 gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL,
788 GTK_STYLE_PROPERTY_FONT, &g->font,
789 NULL);
790 #else
791 g->font = gtk_widget_get_style(g->drawing_area)->font_desc;
794 /* this is probably quite an ugly way to get rid of the first configure
795 * event
796 * immediately after gtk_widget_show(window) drawing_area gets a configure
797 * event which is handled during the next return to gtk_main which is
798 * probably the gdk_gc_new() call. configure handler calls
799 * graph_element_lists_make() which is not good because the graph struct is
800 * not fully set up yet - namely we're not sure about actual geometry
801 * and we don't have the GC's at all. so we just postpone installation
802 * of configure handler until we're ready to deal with it.
804 * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
807 #endif
808 g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event),
811 /* puts("exiting create_drawing_area()"); */
814 static void callback_toplevel_destroy(GtkWidget *widget _U_, gpointer data)
816 struct gtk_graph *g = (struct gtk_graph *)data;
818 if (!(g->flags & GRAPH_DESTROYED)) {
819 g->flags |= GRAPH_DESTROYED;
820 graph_destroy((struct gtk_graph *)data);
824 static void control_panel_create(struct gtk_graph *g)
826 GtkWidget *toplevel, *notebook;
827 GtkWidget *top_vb;
828 GtkWidget *help_bt, *close_bt, *bbox;
829 char window_title[WINDOW_TITLE_LENGTH];
831 debug(DBS_FENTRY) puts("control_panel_create()");
833 notebook = gtk_notebook_new();
834 control_panel_add_zoom_page(g, notebook);
835 control_panel_add_magnify_page(g, notebook);
836 control_panel_add_origin_page(g, notebook);
837 control_panel_add_cross_page(g, notebook);
838 control_panel_add_graph_type_page(g, notebook);
840 g_snprintf(window_title, WINDOW_TITLE_LENGTH,
841 "Graph %d - Control - Wireshark", refnum);
842 toplevel = dlg_window_new("tcp-graph-control");
843 gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
845 gtk_window_set_resizable(GTK_WINDOW(toplevel), FALSE); /* XXX: Acceptable ? */
847 top_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
848 gtk_container_add(GTK_CONTAINER(toplevel), top_vb);
850 gtk_box_pack_start(GTK_BOX(top_vb), notebook, FALSE, FALSE, 5);
852 /* Button row. */
853 bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
854 gtk_box_pack_start(GTK_BOX(top_vb), bbox, FALSE, FALSE, 5);
856 help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
857 g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
859 close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
860 window_set_cancel_button(toplevel, close_bt, NULL);
861 g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
863 g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
864 g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
866 gtk_widget_show_all(toplevel);
867 window_present(toplevel);
869 g->gui.control_panel = toplevel;
872 static void control_panel_add_zoom_page(struct gtk_graph *g, GtkWidget *n)
874 GtkWidget *zoom_frame;
875 GtkWidget *zoom_lock_frame;
876 GtkWidget *label;
877 GtkWidget *box;
879 zoom_frame = control_panel_create_zoom_group(g);
880 gtk_container_set_border_width(GTK_CONTAINER(zoom_frame), 5);
881 zoom_lock_frame = control_panel_create_zoomlock_group(g);
882 gtk_container_set_border_width(GTK_CONTAINER(zoom_lock_frame), 5);
883 box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
884 gtk_box_pack_start(GTK_BOX(box), zoom_frame, TRUE, TRUE, 0);
885 gtk_box_pack_start(GTK_BOX(box), zoom_lock_frame, TRUE, TRUE, 0);
886 gtk_widget_show(box);
887 label = gtk_label_new("Zoom");
888 gtk_notebook_append_page(GTK_NOTEBOOK(n), box, label);
891 static void control_panel_add_magnify_page(struct gtk_graph *g, GtkWidget *n)
893 GtkWidget *mag_frame, *label;
895 mag_frame = control_panel_create_magnify_group(g);
896 gtk_container_set_border_width(GTK_CONTAINER(mag_frame), 5);
897 label = gtk_label_new("Magnify");
898 gtk_notebook_append_page(GTK_NOTEBOOK(n), mag_frame, label);
901 static void control_panel_add_origin_page(struct gtk_graph *g, GtkWidget *n)
903 GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
904 GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
905 GtkWidget *box, *label;
907 /* time origin box */
908 time_orig_cap = gtk_radio_button_new_with_label(NULL, "beginning of capture");
909 time_orig_conn = gtk_radio_button_new_with_label(
910 gtk_radio_button_get_group(GTK_RADIO_BUTTON(time_orig_cap)),
911 "beginning of this TCP connection");
912 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(time_orig_conn), TRUE);
913 time_orig_box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
914 gtk_box_pack_start(GTK_BOX(time_orig_box), time_orig_conn, TRUE, TRUE, 0);
915 gtk_box_pack_start(GTK_BOX(time_orig_box), time_orig_cap, TRUE, TRUE, 0);
916 time_orig_frame = gtk_frame_new("Time origin");
917 gtk_container_set_border_width(GTK_CONTAINER(time_orig_frame), 5);
918 gtk_container_add(GTK_CONTAINER(time_orig_frame), time_orig_box);
920 /* sequence number origin group */
921 seq_orig_isn = gtk_radio_button_new_with_label(NULL, "initial sequence number");
922 seq_orig_zero = gtk_radio_button_new_with_label(
923 gtk_radio_button_get_group(GTK_RADIO_BUTTON(seq_orig_isn)),
924 "0 (=absolute)");
925 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(seq_orig_isn), TRUE);
926 seq_orig_box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
927 gtk_box_pack_start(GTK_BOX(seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
928 gtk_box_pack_start(GTK_BOX(seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
929 seq_orig_frame = gtk_frame_new("Sequence number origin");
930 gtk_container_set_border_width(GTK_CONTAINER(seq_orig_frame), 5);
931 gtk_container_add(GTK_CONTAINER(seq_orig_frame), seq_orig_box);
933 g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
934 g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
936 g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
937 g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
939 box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
940 gtk_container_set_border_width(GTK_CONTAINER(box), 5);
941 gtk_box_pack_start(GTK_BOX(box), time_orig_frame, TRUE, TRUE, 0);
942 gtk_box_pack_start(GTK_BOX(box), seq_orig_frame, TRUE, TRUE, 0);
943 gtk_widget_show(box);
944 label = gtk_label_new("Origin");
945 gtk_notebook_append_page(GTK_NOTEBOOK(n), box, label);
948 static void control_panel_add_cross_page (struct gtk_graph *g, GtkWidget *n)
950 GtkWidget *cross_frame, *label;
952 cross_frame = control_panel_create_cross_group(g);
953 gtk_container_set_border_width(GTK_CONTAINER(cross_frame), 5);
954 label = gtk_label_new("Cross");
955 gtk_notebook_append_page(GTK_NOTEBOOK(n), cross_frame, label);
958 static void control_panel_add_graph_type_page(struct gtk_graph *g, GtkWidget *n)
960 GtkWidget *frame, *label;
962 frame = control_panel_create_graph_type_group(g);
963 gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
964 label = gtk_label_new("Graph type");
965 gtk_notebook_append_page(GTK_NOTEBOOK(n), frame, label);
968 /* Treat this as a cancel, by calling "callback_close()" */
969 static gboolean
970 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
971 gpointer data)
973 callback_close(NULL, data);
974 return FALSE;
977 static void callback_close(GtkWidget *widget _U_, gpointer data)
979 struct gtk_graph *g = (struct gtk_graph * )data;
981 if (!(g->flags & GRAPH_DESTROYED)) {
982 g->flags |= GRAPH_DESTROYED;
983 graph_destroy((struct gtk_graph * )data);
987 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
989 GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
990 GtkTextBuffer *buf;
992 toplevel = dlg_window_new("Help for TCP graphing");
993 gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
995 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
996 gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
997 gtk_container_add(GTK_CONTAINER(toplevel), vbox);
999 scroll = scrolled_window_new(NULL, NULL);
1000 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1001 GTK_SHADOW_IN);
1002 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1003 text = gtk_text_view_new();
1004 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1005 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1006 gtk_text_buffer_set_text(buf, helptext, -1);
1007 gtk_container_add(GTK_CONTAINER(scroll), text);
1009 /* Button row. */
1010 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1011 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
1012 gtk_widget_show(bbox);
1014 close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1015 window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
1017 g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1019 gtk_widget_show_all(toplevel);
1020 window_present(toplevel);
1023 static void get_mouse_position(GtkWidget *widget, int *pointer_x, int *pointer_y, GdkModifierType *mask)
1025 #if GTK_CHECK_VERSION(3,0,0)
1026 gdk_window_get_device_position(gtk_widget_get_window(widget),
1027 gdk_device_manager_get_client_pointer(
1028 gdk_display_get_device_manager(
1029 gtk_widget_get_display(GTK_WIDGET(widget)))),
1030 pointer_x, pointer_y, mask);
1032 #else
1033 gdk_window_get_pointer(gtk_widget_get_window(widget), pointer_x, pointer_y, mask);
1034 #endif
1038 static void callback_time_origin(GtkWidget *toggle _U_, gpointer data)
1040 toggle_time_origin((struct gtk_graph * )data);
1043 static void callback_seq_origin(GtkWidget *toggle _U_, gpointer data)
1045 toggle_seq_origin((struct gtk_graph * )data);
1048 static GtkWidget *control_panel_create_zoom_group(struct gtk_graph *g)
1050 GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1051 GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1052 GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1053 GtkWidget *zoom_v_step_label, *zoom_v_step;
1054 GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_grid, *zoom_grid;
1055 GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1056 GtkWidget *zoom_h_entry, *zoom_v_entry;
1057 GtkWidget *zoom_h_label, *zoom_v_label;
1059 zoom_in = gtk_radio_button_new_with_label(NULL, "in");
1060 zoom_out = gtk_radio_button_new_with_label(
1061 gtk_radio_button_get_group(GTK_RADIO_BUTTON(zoom_in)), "out");
1062 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(zoom_in), TRUE);
1063 zoom_inout_box = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1064 gtk_box_pack_start(GTK_BOX(zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1065 gtk_box_pack_start(GTK_BOX(zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1067 zoom_separator1 = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1069 zoom_h_entry = gtk_entry_new();
1070 gtk_entry_set_text(GTK_ENTRY(zoom_h_entry), "1.000");
1071 gtk_editable_set_editable(GTK_EDITABLE(zoom_h_entry), FALSE);
1072 zoom_h_label = gtk_label_new("Horizontal:");
1074 zoom_v_entry = gtk_entry_new();
1075 gtk_entry_set_text(GTK_ENTRY(zoom_v_entry), "1.000");
1076 gtk_editable_set_editable(GTK_EDITABLE(zoom_v_entry), FALSE);
1077 zoom_v_label = gtk_label_new("Vertical:");
1079 g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1080 g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1082 zoom_grid = ws_gtk_grid_new();
1083 ws_gtk_grid_attach_extended(GTK_GRID(zoom_grid), zoom_h_label, 0, 0, 1, 1,
1084 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1085 ws_gtk_grid_attach_extended(GTK_GRID(zoom_grid), zoom_h_entry, 1, 0, 1, 1,
1086 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1087 ws_gtk_grid_attach_extended(GTK_GRID(zoom_grid), zoom_v_label, 0, 1, 1, 1,
1088 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1089 ws_gtk_grid_attach_extended(GTK_GRID(zoom_grid), zoom_v_entry, 1, 1, 1, 1,
1090 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1092 zoom_separator2 = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1094 zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1095 zoom_h_step = gtk_spin_button_new(zoom_h_adj, 0, 1);
1096 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(zoom_h_step), TRUE);
1097 zoom_h_step_label = gtk_label_new("Horizontal step:");
1099 zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1100 zoom_v_step = gtk_spin_button_new(zoom_v_adj, 0, 1);
1101 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(zoom_v_step), TRUE);
1102 zoom_v_step_label = gtk_label_new("Vertical step:");
1104 g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1105 g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1107 zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1108 zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1109 g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
1110 g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
1111 (gpointer)ZOOM_STEPS_KEEP_RATIO);
1112 g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1113 g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1115 zoom_step_grid = ws_gtk_grid_new();
1116 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_h_step_label, 0, 0, 1, 1,
1117 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1118 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_h_step, 1, 0, 1, 1,
1119 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1120 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_v_step_label, 0, 1, 1, 1,
1121 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1122 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_v_step, 1, 1, 1, 1,
1123 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1124 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_same_toggle, 0, 2, 2, 1,
1125 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1126 ws_gtk_grid_attach_extended(GTK_GRID(zoom_step_grid), zoom_ratio_toggle, 0, 3, 2, 1,
1127 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1129 zoom_box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1130 gtk_box_pack_start(GTK_BOX(zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1131 gtk_box_pack_start(GTK_BOX(zoom_box), zoom_separator1, TRUE, TRUE, 0);
1132 gtk_box_pack_start(GTK_BOX(zoom_box), zoom_grid, TRUE, TRUE, 0);
1133 gtk_box_pack_start(GTK_BOX(zoom_box), zoom_separator2, TRUE, TRUE, 0);
1134 gtk_box_pack_start(GTK_BOX(zoom_box), zoom_step_grid, TRUE, TRUE, 0);
1135 zoom_frame = gtk_frame_new("Zoom");
1136 gtk_container_add(GTK_CONTAINER(zoom_frame), zoom_box);
1138 g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
1139 g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
1141 g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
1142 g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
1143 g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
1145 g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1146 g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1147 return zoom_frame;
1150 static void callback_zoom_inout(GtkWidget *toggle, gpointer data)
1152 struct gtk_graph *g = (struct gtk_graph * )data;
1154 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1155 g->zoom.flags &= ~ZOOM_OUT;
1156 else
1157 g->zoom.flags |= ZOOM_OUT;
1160 static void callback_zoom_step(GtkWidget *spin, gpointer data)
1162 struct gtk_graph *g = (struct gtk_graph * )data;
1163 double value;
1164 int direction;
1165 double *zoom_this, *zoom_other;
1166 GtkSpinButton *widget_this, *widget_other;
1167 double old_this;
1169 direction = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(spin), "direction"));
1170 value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1172 if (direction) {
1173 zoom_this = &g->zoom.step_y;
1174 zoom_other = &g->zoom.step_x;
1175 widget_this = g->zoom.widget.v_step;
1176 widget_other = g->zoom.widget.h_step;
1177 } else {
1178 zoom_this = &g->zoom.step_x;
1179 zoom_other = &g->zoom.step_y;
1180 widget_this = g->zoom.widget.h_step;
1181 widget_other = g->zoom.widget.v_step;
1184 old_this = *zoom_this;
1185 *zoom_this = value;
1186 if (g->zoom.flags & ZOOM_STEPS_SAME) {
1187 *zoom_other = value;
1188 gtk_spin_button_set_value(widget_other, (gfloat) *zoom_other);
1189 } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1190 double old_other = *zoom_other;
1191 *zoom_other *= value / old_this;
1192 if (*zoom_other < 1.0) {
1193 *zoom_other = 1.0;
1194 *zoom_this = old_this * 1.0 / old_other;
1195 gtk_spin_button_set_value(widget_this, (gfloat) *zoom_this);
1196 } else if (*zoom_other > 5.0) {
1197 *zoom_other = 5.0;
1198 *zoom_this = old_this * 5.0 / old_other;
1199 gtk_spin_button_set_value(widget_this, (gfloat) *zoom_this);
1201 gtk_spin_button_set_value(widget_other, (gfloat) *zoom_other);
1205 static void callback_zoom_flags(GtkWidget *toggle, gpointer data)
1207 struct gtk_graph *g = (struct gtk_graph * )data;
1208 int flag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toggle), "flag"));
1210 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1211 g->zoom.flags |= flag;
1212 else
1213 g->zoom.flags &= ~flag;
1216 static void update_zoom_spins(struct gtk_graph *g)
1218 char s[32];
1220 g_snprintf(s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
1221 gtk_entry_set_text(g->zoom.widget.h_zoom, s);
1222 g_snprintf(s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
1223 gtk_entry_set_text(g->zoom.widget.v_zoom, s);
1226 static GtkWidget *control_panel_create_magnify_group(struct gtk_graph *g)
1228 GtkWidget *mag_width_label, *mag_width;
1229 GtkWidget *mag_height_label, *mag_height;
1230 GtkWidget *mag_x_label, *mag_x;
1231 GtkWidget *mag_y_label, *mag_y;
1232 GtkWidget *mag_wh_grid, *mag_zoom_frame, *mag_zoom_grid;
1233 GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1234 GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1235 GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1236 GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1237 GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1238 GtkWidget *mag_box, *mag_frame;
1240 mag_width_label = gtk_label_new("Width:");
1241 mag_width_adj = (GtkAdjustment * )gtk_adjustment_new(250, 100, 600, 1, 10, 0);
1242 mag_width = gtk_spin_button_new(mag_width_adj, 0, 0);
1244 mag_height_label = gtk_label_new("Height:");
1245 mag_height_adj = (GtkAdjustment * )gtk_adjustment_new(250, 100, 600, 1, 10, 0);
1246 mag_height = gtk_spin_button_new(mag_height_adj, 0, 0);
1248 mag_x_label = gtk_label_new("X:");
1249 mag_x_adj = (GtkAdjustment * )gtk_adjustment_new(0, -1000, 1000, 1, 10, 0);
1250 mag_x = gtk_spin_button_new(mag_x_adj, 0, 0);
1252 mag_y_label = gtk_label_new("Y:");
1253 mag_y_adj = (GtkAdjustment * )gtk_adjustment_new(0, -1000, 1000, 1, 10, 0);
1254 mag_y = gtk_spin_button_new(mag_y_adj, 0, 0);
1256 mag_wh_grid = ws_gtk_grid_new();
1257 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_width_label, 0, 0, 1, 1,
1258 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1259 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_width, 1, 0, 1, 1,
1260 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1261 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_height_label, 0, 1, 1, 1,
1262 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1263 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_height, 1, 1, 1, 1,
1264 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1265 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_x_label, 0, 2, 1, 1,
1266 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1267 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_x, 1, 2, 1, 1,
1268 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1269 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_y_label, 0, 3, 1, 1,
1270 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1271 ws_gtk_grid_attach_extended(GTK_GRID(mag_wh_grid), mag_y, 1, 3, 1, 1,
1272 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 5, 0);
1274 mag_h_zoom_label = gtk_label_new("Horizontal:");
1275 mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1276 mag_h_zoom = gtk_spin_button_new(mag_h_zoom_adj, 0, 1);
1278 mag_v_zoom_label = gtk_label_new("Vertical:");
1279 mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1280 mag_v_zoom = gtk_spin_button_new(mag_v_zoom_adj, 0, 1);
1282 mag_zoom_same = gtk_check_button_new_with_label("Keep them the same");
1283 mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1285 mag_zoom_grid = ws_gtk_grid_new();
1286 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_h_zoom_label, 0, 0, 1, 1,
1287 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1288 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_h_zoom, 1, 0, 1, 1,
1289 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1290 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_v_zoom_label, 0, 1 , 1, 1,
1291 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1292 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_v_zoom, 1, 1, 1, 1,
1293 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1294 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_zoom_same, 0, 2, 2, 1,
1295 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1296 ws_gtk_grid_attach_extended(GTK_GRID(mag_zoom_grid), mag_zoom_ratio, 0, 3, 2, 1,
1297 (GtkAttachOptions)(GTK_FILL|GTK_EXPAND), (GtkAttachOptions)0, 0, 0);
1299 mag_zoom_frame = gtk_frame_new("Magnify zoom");
1300 gtk_container_add(GTK_CONTAINER(mag_zoom_frame), mag_zoom_grid);
1301 gtk_container_set_border_width(GTK_CONTAINER(mag_zoom_frame), 3);
1303 mag_box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1304 gtk_box_pack_start(GTK_BOX(mag_box), mag_wh_grid, TRUE, TRUE, 0);
1305 gtk_box_pack_start(GTK_BOX(mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1306 mag_frame = gtk_frame_new("Magnify");
1307 gtk_container_add(GTK_CONTAINER(mag_frame), mag_box);
1309 g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1310 g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1311 g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
1312 g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
1313 g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
1314 g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1316 g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
1317 g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
1318 g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
1319 g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
1320 g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1321 g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1322 g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
1323 g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
1325 return mag_frame;
1328 static void callback_mag_width(GtkWidget *spin, gpointer data)
1330 struct gtk_graph *g = (struct gtk_graph * )data;
1332 g->magnify.width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1335 static void callback_mag_height(GtkWidget *spin, gpointer data)
1337 struct gtk_graph *g = (struct gtk_graph * )data;
1339 g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1342 static void callback_mag_x(GtkWidget *spin, gpointer data)
1344 struct gtk_graph *g = (struct gtk_graph * )data;
1346 g->magnify.offset.x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1349 static void callback_mag_y(GtkWidget *spin, gpointer data)
1351 struct gtk_graph *g = (struct gtk_graph * )data;
1353 g->magnify.offset.y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1356 static void callback_mag_zoom(GtkWidget *spin, gpointer data)
1358 struct gtk_graph *g = (struct gtk_graph * )data;
1359 double value;
1360 int direction;
1361 double *zoom_this, *zoom_other;
1362 GtkSpinButton *widget_this, *widget_other;
1363 double old_this;
1365 if (g->magnify.flags & MAGZOOMS_IGNORE) {
1366 printf("refusing callback for %s zoom widget.\n",
1367 ((GtkSpinButton * )spin == g->magnify.widget.h_zoom) ? "horizontal" : "vertical");
1368 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1369 return;
1371 direction = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(spin), "direction"));
1372 value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1374 if (direction) {
1375 zoom_this = &g->magnify.zoom.y;
1376 zoom_other = &g->magnify.zoom.x;
1377 widget_this = g->magnify.widget.v_zoom;
1378 widget_other = g->magnify.widget.h_zoom;
1379 } else {
1380 zoom_this = &g->magnify.zoom.x;
1381 zoom_other = &g->magnify.zoom.y;
1382 widget_this = g->magnify.widget.h_zoom;
1383 widget_other = g->magnify.widget.v_zoom;
1386 old_this = *zoom_this;
1387 *zoom_this = value;
1388 if (g->magnify.flags & MAGZOOMS_SAME) {
1389 *zoom_other = value;
1390 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1391 gtk_spin_button_set_value(widget_other, (gfloat) *zoom_other);
1392 } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1393 double old_other = *zoom_other;
1394 *zoom_other *= value / old_this;
1395 if (*zoom_other < 1.0) {
1396 *zoom_other = 1.0;
1397 *zoom_this = old_this * 1.0 / old_other;
1398 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1399 gtk_spin_button_set_value(widget_this, (gfloat) *zoom_this);
1400 } else if (*zoom_other > 25.0) {
1401 *zoom_other = 25.0;
1402 *zoom_this = old_this * 25.0 / old_other;
1403 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1404 gtk_spin_button_set_value(widget_this, (gfloat) *zoom_this);
1406 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1407 gtk_spin_button_set_value(widget_other, (gfloat) *zoom_other);
1411 static void callback_mag_flags(GtkWidget *toggle, gpointer data)
1413 struct gtk_graph *g = (struct gtk_graph * )data;
1414 int flag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toggle), "flag"));
1416 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1417 g->magnify.flags |= flag;
1418 else
1419 g->magnify.flags &= ~flag;
1422 static GtkWidget *control_panel_create_zoomlock_group(struct gtk_graph *g)
1424 GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1425 GtkWidget *zoom_lock_frame;
1427 zoom_lock_none = gtk_radio_button_new_with_label(NULL, "none");
1428 zoom_lock_h = gtk_radio_button_new_with_label(
1429 gtk_radio_button_get_group(GTK_RADIO_BUTTON(zoom_lock_none)),
1430 "horizontal");
1431 zoom_lock_v = gtk_radio_button_new_with_label(
1432 gtk_radio_button_get_group(GTK_RADIO_BUTTON(zoom_lock_none)),
1433 "vertical");
1434 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(zoom_lock_none), TRUE);
1435 zoom_lock_box = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1436 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0);
1437 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1438 gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1439 zoom_lock_frame = gtk_frame_new("Zoom lock:");
1440 gtk_container_add(GTK_CONTAINER(zoom_lock_frame), zoom_lock_box);
1442 g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
1443 g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
1445 return zoom_lock_frame;
1448 static void callback_zoomlock_h(GtkWidget *toggle, gpointer data)
1450 struct gtk_graph *g = (struct gtk_graph * )data;
1452 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1453 g->zoom.flags |= ZOOM_HLOCK;
1454 else
1455 g->zoom.flags &= ~ZOOM_HLOCK;
1458 static void callback_zoomlock_v(GtkWidget *toggle, gpointer data)
1460 struct gtk_graph *g = (struct gtk_graph * )data;
1462 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1463 g->zoom.flags |= ZOOM_VLOCK;
1464 else
1465 g->zoom.flags &= ~ZOOM_VLOCK;
1468 static GtkWidget *control_panel_create_cross_group(struct gtk_graph *g)
1470 GtkWidget *on, *off, *box, *frame, *vbox, *label;
1472 label = gtk_label_new("Crosshairs:");
1473 off = gtk_radio_button_new_with_label(NULL, "off");
1474 on = gtk_radio_button_new_with_label(
1475 gtk_radio_button_get_group(GTK_RADIO_BUTTON(off)), "on");
1476 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(off), TRUE);
1477 box = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1478 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 10);
1479 gtk_box_pack_start(GTK_BOX(box), off, FALSE, FALSE, 10);
1480 gtk_box_pack_start(GTK_BOX(box), on, FALSE, FALSE, 0);
1481 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1482 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 15);
1483 /* frame = gtk_frame_new("Cross:"); */
1484 frame = gtk_frame_new(NULL);
1485 gtk_container_add(GTK_CONTAINER(frame), vbox);
1487 g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
1489 g->cross.on_toggle = (GtkToggleButton * )on;
1490 g->cross.off_toggle = (GtkToggleButton * )off;
1492 return frame;
1494 static void callback_cross_on_off(GtkWidget *toggle, gpointer data)
1496 struct gtk_graph *g = (struct gtk_graph * )data;
1498 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))) {
1499 int x, y;
1500 g->cross.draw = TRUE;
1501 get_mouse_position(g->drawing_area, &x, &y, 0);
1502 cross_draw(g, x, y);
1503 } else {
1504 g->cross.draw = FALSE;
1505 if (g->cross.erase_needed) {
1506 cross_erase(g);
1511 static GtkWidget *control_panel_create_graph_type_group(struct gtk_graph *g)
1513 GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1514 GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1515 GtkWidget *graph_frame;
1516 GtkWidget *graph_wscale;
1518 graph_tput = gtk_radio_button_new_with_label(NULL, "Throughput");
1519 graph_tseqttrace = gtk_radio_button_new_with_label(
1520 gtk_radio_button_get_group(GTK_RADIO_BUTTON(graph_tput)),
1521 "Time/Sequence (tcptrace-style)");
1522 graph_tseqstevens = gtk_radio_button_new_with_label(
1523 gtk_radio_button_get_group(GTK_RADIO_BUTTON(graph_tput)),
1524 "Time/Sequence (Stevens'-style)");
1525 graph_rtt = gtk_radio_button_new_with_label(
1526 gtk_radio_button_get_group(GTK_RADIO_BUTTON(graph_tput)),
1527 "Round-trip Time");
1528 graph_wscale = gtk_radio_button_new_with_label(
1529 gtk_radio_button_get_group(GTK_RADIO_BUTTON(graph_tput)),
1530 "Window Scaling");
1532 switch (g->tg.type) {
1533 case GRAPH_TSEQ_STEVENS:
1534 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens), TRUE);
1535 break;
1536 case GRAPH_TSEQ_TCPTRACE:
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqttrace), TRUE);
1538 break;
1539 case GRAPH_THROUGHPUT:
1540 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tput), TRUE);
1541 break;
1542 case GRAPH_RTT:
1543 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_rtt), TRUE);
1544 break;
1545 case GRAPH_WSCALE:
1546 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_wscale), TRUE);
1547 break;
1549 graph_init = gtk_check_button_new_with_label("Init on change");
1550 graph_sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1551 graph_box = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1552 gtk_box_pack_start(GTK_BOX(graph_box), graph_rtt, TRUE, TRUE, 0);
1553 gtk_box_pack_start(GTK_BOX(graph_box), graph_tput, TRUE, TRUE, 0);
1554 gtk_box_pack_start(GTK_BOX(graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1555 gtk_box_pack_start(GTK_BOX(graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1556 gtk_box_pack_start(GTK_BOX(graph_box), graph_wscale, TRUE, TRUE, 0);
1557 gtk_box_pack_start(GTK_BOX(graph_box), graph_sep, TRUE, TRUE, 0);
1558 gtk_box_pack_start(GTK_BOX(graph_box), graph_init, TRUE, TRUE, 0);
1559 graph_frame = gtk_frame_new("Graph type:");
1560 gtk_container_add(GTK_CONTAINER(graph_frame), graph_box);
1562 g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
1563 GINT_TO_POINTER(GRAPH_TSEQ_STEVENS));
1564 g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type",
1565 GINT_TO_POINTER(GRAPH_TSEQ_TCPTRACE));
1566 g_object_set_data(G_OBJECT(graph_tput), "new-graph-type",
1567 GINT_TO_POINTER(GRAPH_THROUGHPUT));
1568 g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type",
1569 GINT_TO_POINTER(GRAPH_RTT));
1570 g_object_set_data(G_OBJECT(graph_wscale), "new-graph-type",
1571 GINT_TO_POINTER(GRAPH_WSCALE));
1573 g->gt.graph_wscale = (GtkToggleButton * )graph_wscale;
1574 g->gt.graph_rtt = (GtkToggleButton * )graph_rtt;
1575 g->gt.graph_tput = (GtkToggleButton * )graph_tput;
1576 g->gt.graph_tseqstevens = (GtkToggleButton * )graph_tseqstevens;
1577 g->gt.graph_tseqttrace = (GtkToggleButton * )graph_tseqttrace;
1579 g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
1580 g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
1581 g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
1582 g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
1583 g_signal_connect(graph_wscale, "toggled", G_CALLBACK(callback_graph_type), g);
1584 g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg), g);
1586 return graph_frame;
1589 static void callback_graph_type(GtkWidget *toggle, gpointer data)
1591 tcp_graph_type old_type, new_type;
1592 struct gtk_graph *g = (struct gtk_graph * )data;
1594 new_type = (tcp_graph_type)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toggle), "new-graph-type"));
1596 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1597 return;
1599 old_type = g->tg.type;
1600 g->tg.type = new_type;
1602 graph_element_lists_free(g);
1603 graph_element_lists_initialize(g);
1605 if ((old_type == GRAPH_THROUGHPUT) || (new_type == GRAPH_THROUGHPUT)) {
1606 /* throughput graph uses differently constructed segment list so we
1607 * need to recreate it */
1608 graph_segment_list_free(&g->tg);
1609 graph_segment_list_get(&cfile, &g->tg, TRUE);
1612 if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1613 g->geom.width = g->wp.width;
1614 g->geom.height = g->wp.height;
1615 g->geom.x = g->wp.x;
1616 g->geom.y = g->wp.y;
1618 g->x_axis->min = g->y_axis->min = 0;
1619 gtk_toggle_button_set_active(g->gui.time_orig_conn, TRUE);
1620 gtk_toggle_button_set_active(g->gui.seq_orig_isn, TRUE);
1621 graph_init_sequence(g);
1624 static void callback_graph_init_on_typechg(GtkWidget *toggle _U_, gpointer data)
1626 ((struct gtk_graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1629 static struct gtk_graph *graph_new(void)
1631 struct gtk_graph *g;
1633 g = (struct gtk_graph * )g_malloc0(sizeof(struct gtk_graph));
1634 graph_element_lists_initialize(g);
1636 g->x_axis = (struct axis * )g_malloc0(sizeof(struct axis));
1637 g->y_axis = (struct axis * )g_malloc0(sizeof(struct axis));
1638 g->x_axis->g = g;
1639 g->x_axis->flags = 0;
1640 g->x_axis->flags |= AXIS_ORIENTATION;
1641 g->x_axis->s.x = g->x_axis->s.y = 0;
1642 g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1643 g->x_axis->p.x = VAXIS_INIT_WIDTH;
1644 g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1645 g->y_axis->g = g;
1646 g->y_axis->flags = 0;
1647 g->y_axis->flags &= ~AXIS_ORIENTATION;
1648 g->y_axis->p.x = g->y_axis->p.y = 0;
1649 g->y_axis->p.width = VAXIS_INIT_WIDTH;
1650 g->y_axis->s.x = 0;
1651 g->y_axis->s.y = TITLEBAR_HEIGHT;
1652 g->y_axis->s.width = VAXIS_INIT_WIDTH;
1654 return g;
1657 static void graph_initialize_values(struct gtk_graph *g)
1659 g->geom.width = g->wp.width = 750;
1660 g->geom.height = g->wp.height = 550;
1661 g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1662 g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1663 g->flags = 0;
1664 /* g->zoom.x = g->zoom.y = 1.0; */
1665 g->zoom.step_x = g->zoom.step_y = 1.2;
1666 g->zoom.flags = 0;
1667 g->cross.draw = g->cross.erase_needed = FALSE;
1668 g->zoomrect_erase_needed = FALSE;
1669 g->grab.grabbed = 0;
1670 g->magnify.active = 0;
1671 g->magnify.offset.x = g->magnify.offset.y = 0;
1672 g->magnify.width = g->magnify.height = 250;
1673 g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1674 g->magnify.flags = 0;
1677 static void graph_init_sequence(struct gtk_graph *g)
1679 debug(DBS_FENTRY) puts("graph_init_sequence()");
1681 graph_type_dependent_initialize(g);
1682 g->zoom.initial.x = g->zoom.x;
1683 g->zoom.initial.y = g->zoom.y;
1684 graph_element_lists_make(g);
1685 g->x_axis->s.width = g->wp.width;
1686 g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1687 g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1688 g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1689 g->y_axis->s.height = g->wp.height;
1690 g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1691 graph_pixmaps_create(g);
1692 axis_pixmaps_create(g->y_axis);
1693 axis_pixmaps_create(g->x_axis);
1694 graph_title_pixmap_create(g);
1695 graph_title_pixmap_draw(g);
1696 graph_title_pixmap_display(g);
1697 graph_display(g);
1698 axis_display(g->y_axis);
1699 axis_display(g->x_axis);
1702 static void graph_type_dependent_initialize(struct gtk_graph *g)
1704 switch (g->tg.type) {
1705 case GRAPH_TSEQ_STEVENS:
1706 case GRAPH_TSEQ_TCPTRACE:
1707 tseq_initialize(g);
1708 break;
1709 case GRAPH_THROUGHPUT:
1710 tput_initialize(g);
1711 break;
1712 case GRAPH_RTT:
1713 rtt_initialize(g);
1714 break;
1715 case GRAPH_WSCALE:
1716 wscale_initialize(g);
1717 break;
1718 default:
1719 break;
1723 static void graph_destroy(struct gtk_graph *g)
1725 debug(DBS_FENTRY) puts("graph_destroy()");
1727 axis_destroy(g->x_axis);
1728 axis_destroy(g->y_axis);
1729 /* window_destroy(g->drawing_area); */
1730 window_destroy(g->gui.control_panel);
1731 window_destroy(g->toplevel);
1732 /* window_destroy(g->text); */
1733 #if GTK_CHECK_VERSION(2,22,0)
1734 if (g->title_surface) {
1735 cairo_surface_destroy(g->title_surface);
1737 if (g->surface[0]) {
1738 cairo_surface_destroy(g->surface[0]);
1740 if (g->surface[1]) {
1741 cairo_surface_destroy(g->surface[1]);
1743 #else
1744 g_object_unref(g->pixmap[0]);
1745 g_object_unref(g->pixmap[1]);
1746 #endif /* GTK_CHECK_VERSION(2,22,0) */
1747 g_free(g->x_axis);
1748 g_free(g->y_axis);
1749 g_free((gpointer )(g->title));
1750 graph_segment_list_free(&g->tg);
1751 graph_element_lists_free(g);
1753 g_free(g);
1756 static void graph_element_lists_initialize(struct gtk_graph *g)
1758 g->elists = (struct element_list *)g_malloc0(sizeof(struct element_list));
1761 static void graph_element_lists_make(struct gtk_graph *g)
1763 debug(DBS_FENTRY) puts("graph_element_lists_make()");
1765 switch (g->tg.type) {
1766 case GRAPH_TSEQ_STEVENS:
1767 tseq_stevens_make_elmtlist(g);
1768 break;
1769 case GRAPH_TSEQ_TCPTRACE:
1770 tseq_tcptrace_make_elmtlist(g);
1771 break;
1772 case GRAPH_THROUGHPUT:
1773 tput_make_elmtlist(g);
1774 break;
1775 case GRAPH_RTT:
1776 rtt_make_elmtlist(g);
1777 break;
1778 case GRAPH_WSCALE:
1779 wscale_make_elmtlist(g);
1780 break;
1781 default:
1782 printf("graph_element_lists_make: unknown graph type: %d\n", g->tg.type);
1783 break;
1787 static void graph_element_lists_free(struct gtk_graph *g)
1789 struct element_list *list, *next_list;
1791 for (list=g->elists; list; list=next_list) {
1792 g_free(list->elements);
1793 next_list = list->next;
1794 g_free(list);
1796 g->elists = NULL; /* just to make debugging easier */
1799 static void graph_title_pixmap_create(struct gtk_graph *g)
1801 #if GTK_CHECK_VERSION(2,22,0)
1802 if (g->title_surface) {
1803 cairo_surface_destroy(g->title_surface);
1804 g->title_surface = NULL;
1807 g->title_surface = gdk_window_create_similar_surface(gtk_widget_get_window(g->drawing_area),
1808 CAIRO_CONTENT_COLOR,
1809 g->x_axis->p.width,
1810 g->wp.y);
1812 #else
1813 if (g->title_pixmap)
1814 g_object_unref(g->title_pixmap);
1816 g->title_pixmap = gdk_pixmap_new(gtk_widget_get_window(g->drawing_area),
1817 g->x_axis->p.width, g->wp.y, -1);
1818 #endif
1821 static void graph_title_pixmap_draw(struct gtk_graph *g)
1823 int i;
1824 cairo_t *cr;
1826 #if GTK_CHECK_VERSION(2,22,0)
1827 cr = cairo_create(g->title_surface);
1828 #else
1829 cr = gdk_cairo_create(g->title_pixmap);
1830 #endif
1831 cairo_set_source_rgb(cr, 1, 1, 1); /* set fill color */
1832 cairo_rectangle(cr, 0, 0, g->x_axis->p.width, g->wp.y);
1833 cairo_fill(cr);
1834 cairo_set_source_rgb(cr, 0, 0, 0); /* set text color */
1836 for (i=0; g->title[i]; i++) {
1837 gint w, h;
1838 PangoLayout *layout;
1839 layout = gtk_widget_create_pango_layout(g->drawing_area,
1840 g->title[i]);
1841 pango_layout_get_pixel_size(layout, &w, &h);
1842 cairo_move_to(cr, g->wp.width/2 - w/2, 20 + i*(h+3));
1843 pango_cairo_show_layout(cr, layout);
1844 g_object_unref(G_OBJECT(layout));
1846 cairo_destroy(cr);
1849 static void graph_title_pixmap_display(struct gtk_graph *g)
1851 cairo_t *cr;
1853 cr = gdk_cairo_create(gtk_widget_get_window(g->drawing_area));
1854 #if GTK_CHECK_VERSION(2,22,0)
1855 cairo_set_source_surface(cr, g->title_surface, g->wp.x, 0);
1856 #else
1857 gdk_cairo_set_source_pixmap(cr, g->title_pixmap, g->wp.x, 0);
1858 #endif
1859 cairo_rectangle(cr, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
1860 cairo_fill(cr);
1861 cairo_destroy(cr);
1864 static void graph_pixmaps_create(struct gtk_graph *g)
1866 debug(DBS_FENTRY) puts("graph_pixmaps_create()");
1867 #if GTK_CHECK_VERSION(2,22,0)
1868 if (g->surface[0]) {
1869 cairo_surface_destroy(g->surface[0]);
1870 g->surface[0] = NULL;
1873 if (g->surface[1]) {
1874 cairo_surface_destroy(g->surface[1]);
1875 g->surface[1] = NULL;
1878 g->surface[0] = gdk_window_create_similar_surface(gtk_widget_get_window(g->drawing_area),
1879 CAIRO_CONTENT_COLOR,
1880 g->wp.width,
1881 g->wp.height);
1883 g->surface[1] = gdk_window_create_similar_surface(gtk_widget_get_window(g->drawing_area),
1884 CAIRO_CONTENT_COLOR,
1885 g->wp.width,
1886 g->wp.height);
1888 g->displayed = 0;
1889 #else
1890 if (g->pixmap[0])
1891 g_object_unref(g->pixmap[0]);
1892 if (g->pixmap[1])
1893 g_object_unref(g->pixmap[1]);
1895 g->pixmap[0] = gdk_pixmap_new(gtk_widget_get_window(g->drawing_area),
1896 g->wp.width, g->wp.height, -1);
1897 g->pixmap[1] = gdk_pixmap_new(gtk_widget_get_window(g->drawing_area),
1898 g->wp.width, g->wp.height, -1);
1900 g->displayed = 0;
1901 #endif /* GTK_CHECK_VERSION(2,22,0) */
1904 static void graph_display(struct gtk_graph *g)
1906 set_busy_cursor(gtk_widget_get_window(g->drawing_area));
1907 graph_pixmap_draw(g);
1908 unset_busy_cursor(gtk_widget_get_window(g->drawing_area), g->cross.draw);
1909 graph_pixmaps_switch(g);
1910 graph_pixmap_display(g);
1913 static void graph_pixmap_display(struct gtk_graph *g)
1915 cairo_t *cr;
1917 cr = gdk_cairo_create(gtk_widget_get_window(g->drawing_area));
1918 #if GTK_CHECK_VERSION(2,22,0)
1919 cairo_set_source_surface(cr, g->surface[g->displayed], g->wp.x, g->wp.y);
1920 #else
1921 gdk_cairo_set_source_pixmap(cr, g->pixmap[g->displayed], g->wp.x, g->wp.y);
1922 #endif /* GTK_CHECK_VERSION(2,22,0) */
1923 cairo_rectangle(cr, g->wp.x, g->wp.y, g->wp.width, g->wp.height);
1924 cairo_fill(cr);
1925 cairo_destroy(cr);
1926 if (g->cross.erase_needed) {
1927 cross_erase(g);
1931 static void graph_pixmaps_switch(struct gtk_graph *g)
1933 g->displayed = 1 ^ g->displayed;
1936 static void graph_pixmap_draw(struct gtk_graph *g)
1938 struct element_list *list;
1939 struct element *e;
1940 int not_disp;
1941 cairo_t *cr;
1942 GdkRGBA *current_line_color = NULL;
1943 GdkRGBA *color_to_set = NULL;
1944 gboolean line_stroked = TRUE;
1946 debug(DBS_FENTRY) puts("graph_pixmap_draw()");
1947 not_disp = 1 ^ g->displayed;
1949 #if GTK_CHECK_VERSION(2,22,0)
1950 cr = cairo_create(g->surface[not_disp]);
1951 #else
1952 cr = gdk_cairo_create(g->pixmap[not_disp]);
1953 #endif /* GTK_CHECK_VERSION(2,22,0) */
1954 cairo_set_source_rgb(cr, 1, 1, 1);
1955 cairo_rectangle(cr, 0, 0, g->wp.width, g->wp.height);
1956 cairo_fill(cr);
1958 /* Want line width 1 for all elements */
1959 cairo_set_line_width(cr, 1.0);
1961 for (list=g->elists; list; list=list->next) {
1962 for (e=list->elements; e->type != ELMT_NONE; e++) {
1963 switch (e->type) {
1964 case ELMT_RECT:
1965 current_line_color = NULL;
1966 break;
1968 case ELMT_LINE:
1969 /* Work out if we need to change colour */
1970 if (current_line_color == e->elment_color_p) {
1971 /* No change needed */
1972 color_to_set = NULL;
1974 else {
1975 /* Changing colour */
1976 current_line_color = color_to_set = e->elment_color_p;
1979 /* Draw the line */
1980 draw_element_line(g, e, cr, color_to_set);
1981 line_stroked = FALSE;
1982 break;
1984 case ELMT_ELLIPSE:
1985 if (!line_stroked) {
1986 cairo_stroke(cr);
1987 line_stroked = TRUE;
1989 draw_element_ellipse(g, e, cr);
1990 break;
1992 default:
1993 break;
1998 /* Make sure any remaining lines get drawn */
1999 if (!line_stroked)
2000 cairo_stroke(cr);
2002 cairo_destroy(cr);
2005 static void draw_element_line(struct gtk_graph *g, struct element *e, cairo_t *cr,
2006 GdkRGBA *new_color)
2008 int xx1, xx2, yy1, yy2;
2010 debug(DBS_GRAPH_DRAWING) printf("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2011 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2012 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2014 /* Set our new colour (if changed) */
2015 if (new_color != NULL) {
2016 /* First draw any previous lines with old colour */
2017 cairo_stroke(cr);
2018 gdk_cairo_set_source_rgba(cr, new_color);
2021 xx1 = (int )rint(e->p.line.dim.x1 + g->geom.x - g->wp.x);
2022 xx2 = (int )rint(e->p.line.dim.x2 + g->geom.x - g->wp.x);
2023 yy1 = (int )rint((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2024 yy2 = (int )rint((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2026 /* If line completely out of the area, we won't show it */
2027 if (((xx1 < 0) && (xx2 < 0)) || ((xx1 >= g->wp.width) && (xx2 >= g->wp.width)) ||
2028 ((yy1 < 0) && (yy2 < 0)) || ((yy1 >= g->wp.height) && (yy2 >= g->wp.height))) {
2029 debug(DBS_GRAPH_DRAWING) printf(" refusing: (%d,%d)->(%d,%d)\n",
2030 xx1, yy1, xx2, yy2);
2031 return;
2034 /* If one end of the line is out of bounds, don't worry. Cairo will
2035 clip the line to the outside of g->wp at the correct angle! */
2037 debug(DBS_GRAPH_DRAWING) printf("line: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2, yy2);
2039 g_assert(e->elment_color_p!=NULL);
2041 cairo_move_to(cr, xx1+0.5, yy1+0.5);
2042 cairo_line_to(cr, xx2+0.5, yy2+0.5);
2045 static void draw_element_ellipse(struct gtk_graph *g, struct element *e, cairo_t *cr)
2047 gdouble w = e->p.ellipse.dim.width;
2048 gdouble h = e->p.ellipse.dim.height;
2049 gdouble x = e->p.ellipse.dim.x + g->geom.x - g->wp.x;
2050 gdouble y = g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y;
2052 debug(DBS_GRAPH_DRAWING) printf("ellipse: (x, y) " UTF8_RIGHTWARDS_ARROW " (w, h): (%f, %f) " UTF8_RIGHTWARDS_ARROW " (%f, %f)\n", x, y, w, h);
2054 cairo_save(cr);
2055 cairo_set_source_rgb(cr, 0, 0, 0);
2056 cairo_translate(cr, x + w / 2., y + h / 2.);
2057 cairo_scale(cr, w / 2., h / 2.);
2058 cairo_arc(cr, 0., 0., 1., 0., 2 * G_PI);
2059 cairo_fill(cr);
2060 cairo_restore(cr);
2063 static void axis_pixmaps_create(struct axis *axis)
2065 debug(DBS_FENTRY) puts("axis_pixmaps_create()");
2066 #if GTK_CHECK_VERSION(2,22,0)
2067 if (axis->surface[0]) {
2068 cairo_surface_destroy(axis->surface[0]);
2069 axis->surface[0] = NULL;
2071 if (axis->surface[1]) {
2072 cairo_surface_destroy(axis->surface[1]);
2073 axis->surface[1] = NULL;
2075 axis->surface[0] = gdk_window_create_similar_surface(gtk_widget_get_window(axis->drawing_area),
2076 CAIRO_CONTENT_COLOR,
2077 axis->p.width,
2078 axis->p.height);
2080 axis->surface[1] = gdk_window_create_similar_surface(gtk_widget_get_window(axis->drawing_area),
2081 CAIRO_CONTENT_COLOR,
2082 axis->p.width,
2083 axis->p.height);
2085 axis->displayed = 0;
2086 #else
2087 if (axis->pixmap[0])
2088 g_object_unref(axis->pixmap[0]);
2089 if (axis->pixmap[1])
2090 g_object_unref(axis->pixmap[1]);
2092 axis->pixmap[0] = gdk_pixmap_new(gtk_widget_get_window(axis->drawing_area),
2093 axis->p.width, axis->p.height, -1);
2094 axis->pixmap[1] = gdk_pixmap_new(gtk_widget_get_window(axis->drawing_area),
2095 axis->p.width, axis->p.height, -1);
2097 axis->displayed = 0;
2098 #endif
2101 static void axis_destroy(struct axis *axis)
2103 #if GTK_CHECK_VERSION(2,22,0)
2104 if (axis->surface[0]) {
2105 cairo_surface_destroy(axis->surface[0]);
2106 axis->surface[0] = NULL;
2108 if (axis->surface[1]) {
2109 cairo_surface_destroy(axis->surface[1]);
2110 axis->surface[1] = NULL;
2112 #else
2113 g_object_unref(axis->pixmap[0]);
2114 g_object_unref(axis->pixmap[1]);
2115 #endif
2116 g_free( (gpointer) (axis->label) );
2119 static void axis_display(struct axis *axis)
2121 if (axis->flags & AXIS_ORIENTATION)
2122 h_axis_pixmap_draw(axis);
2123 else
2124 v_axis_pixmap_draw(axis);
2125 axis_pixmaps_switch(axis);
2126 axis_pixmap_display(axis);
2129 static void v_axis_pixmap_draw(struct axis *axis)
2131 struct gtk_graph *g = axis->g;
2132 int i;
2133 double major_tick;
2134 int not_disp, rdigits, offset, imin, imax;
2135 double bottom, top, j, fl, corr;
2136 PangoLayout *layout;
2137 cairo_t *cr;
2139 debug(DBS_FENTRY) puts("v_axis_pixmap_draw()");
2140 bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2141 (double )g->geom.height * g->bounds.height;
2142 bottom += axis->min;
2143 top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2144 (double )g->geom.height * g->bounds.height;
2145 top += axis->min;
2146 axis_compute_ticks(axis, bottom, top, AXIS_VERTICAL);
2148 j = axis->major - floor(axis->major);
2149 for (rdigits=0; rdigits <= 6; rdigits++) {
2150 j *= 10;
2151 if (j <= 0.000001)
2152 break;
2153 j = j - floor(j);
2156 not_disp = 1 ^ axis->displayed;
2158 #if GTK_CHECK_VERSION(2,22,0)
2159 cr = cairo_create(axis->surface[not_disp]);
2160 #else
2161 cr = gdk_cairo_create(axis->pixmap[not_disp]);
2162 #endif
2163 cairo_set_source_rgb(cr, 1, 1, 1);
2164 cairo_rectangle(cr, 0, 0, axis->p.width, axis->p.height);
2165 cairo_fill(cr);
2167 /* axis */
2168 cairo_set_source_rgb(cr, 0, 0, 0);
2169 cairo_set_line_width(cr, 1.0);
2170 cairo_move_to(cr, axis->p.width - 1.5, (axis->p.height-axis->s.height)/2.0);
2171 cairo_line_to(cr, axis->s.width - 1.5, axis->p.height);
2173 offset = g->wp.y + (-g->geom.y);
2174 fl = floor(axis->min / axis->major) * axis->major;
2175 corr = rint((axis->min - fl) * g->zoom.y);
2177 /* major ticks */
2178 major_tick = axis->major * g->zoom.y;
2179 imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2180 imax = (int) ((g->geom.height - offset + corr) / major_tick);
2181 for (i=imin; i <= imax; i++) {
2182 gint w, h;
2183 char desc[32];
2184 int y = (int) (g->geom.height - 1 - (int )rint(i * major_tick) -
2185 offset + corr + axis->s.y);
2187 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2188 (i * axis->major) + fl, y);
2189 if ((y < 0) || (y > axis->p.height))
2190 continue;
2192 cairo_move_to(cr, axis->p.width - 15, y + 0.5);
2193 cairo_line_to(cr, axis->s.width - 1, y + 0.5);
2195 g_snprintf(desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2196 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2197 pango_layout_get_pixel_size(layout, &w, &h);
2198 cairo_move_to(cr, axis->s.width-14-4-w, y - h/2);
2199 pango_cairo_show_layout(cr, layout);
2200 g_object_unref(G_OBJECT(layout));
2202 /* minor ticks */
2203 if (axis->minor) {
2204 double minor_tick = axis->minor * g->zoom.y;
2205 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2206 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2207 for (i=imin; i <= imax; i++) {
2208 int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2209 offset + corr + axis->s.y);
2211 debug(DBS_AXES_DRAWING) printf("%f @ %d\n", i*axis->minor+fl, y);
2212 if ((y > 0) && (y < axis->p.height)) {
2213 cairo_move_to(cr, axis->s.width - 8, y+0.5);
2214 cairo_line_to(cr, axis->s.width - 1, y+0.5);
2218 for (i=0; axis->label[i]; i++) {
2219 gint w, h;
2220 layout = gtk_widget_create_pango_layout(g->drawing_area,
2221 axis->label[i]);
2222 pango_layout_get_pixel_size(layout, &w, &h);
2223 cairo_move_to(cr,(axis->p.width - w)/2, TITLEBAR_HEIGHT-10 - i*(h+3) - h);
2224 pango_cairo_show_layout(cr, layout);
2225 g_object_unref(G_OBJECT(layout));
2227 cairo_stroke(cr);
2228 cairo_destroy(cr);
2231 static void h_axis_pixmap_draw(struct axis *axis)
2233 struct gtk_graph *g = axis->g;
2234 int i;
2235 double major_tick, minor_tick;
2236 int not_disp, rdigits, offset, imin, imax;
2237 double left, right, j, fl, corr;
2238 PangoLayout *layout;
2239 cairo_t *cr;
2241 debug(DBS_FENTRY) puts("h_axis_pixmap_draw()");
2242 left = (g->wp.x-g->geom.x) / (double )g->geom.width * g->bounds.width;
2243 left += axis->min;
2244 right = (g->wp.x - g->geom.x + g->wp.width) / (double )g->geom.width * g->bounds.width;
2245 right += axis->min;
2246 axis_compute_ticks(axis, left, right, AXIS_HORIZONTAL);
2248 j = axis->major - floor(axis->major);
2249 for (rdigits=0; rdigits <= 6; rdigits++) {
2250 j *= 10;
2251 if (j <= 0.000001)
2252 break;
2253 j = j - floor(j);
2256 not_disp = 1 ^ axis->displayed;
2258 #if GTK_CHECK_VERSION(2,22,0)
2259 cr = cairo_create(axis->surface[not_disp]);
2260 #else
2261 cr = gdk_cairo_create(axis->pixmap[not_disp]);
2262 #endif
2263 cairo_set_source_rgb(cr, 1, 1, 1);
2264 cairo_rectangle(cr, 0, 0, axis->p.width, axis->p.height);
2265 cairo_fill(cr);
2267 /* axis */
2268 cairo_set_source_rgb(cr, 0, 0, 0);
2269 cairo_set_line_width(cr, 1.0);
2270 cairo_move_to(cr, 0, 0.5);
2271 cairo_line_to(cr, axis->s.width + (axis->p.width-axis->s.width)/2.0, 0.5);
2273 offset = g->wp.x - g->geom.x;
2275 fl = floor(axis->min / axis->major) * axis->major;
2276 corr = rint((axis->min - fl) * g->zoom.x);
2278 /* major ticks */
2279 major_tick = axis->major*g->zoom.x;
2280 imin = (int) ((offset + corr) / major_tick + 1);
2281 imax = (int) ((offset + corr + axis->s.width) / major_tick);
2282 for (i=imin; i <= imax; i++) {
2283 char desc[32];
2284 int w, h;
2285 int x = (int ) (rint(i * major_tick) - offset - corr);
2287 /* printf("%f @ %d\n", i*axis->major + fl, x); */
2288 if ((x < 0) || (x > axis->s.width))
2289 continue;
2290 cairo_move_to(cr, x+0.5, 0);
2291 cairo_line_to(cr, x+0.5, 15);
2293 g_snprintf(desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2294 layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2295 pango_layout_get_pixel_size(layout, &w, &h);
2296 cairo_move_to(cr, x - w/2, 15+4);
2297 pango_cairo_show_layout(cr, layout);
2298 g_object_unref(G_OBJECT(layout));
2300 if (axis->minor > 0) {
2301 /* minor ticks */
2302 minor_tick = axis->minor*g->zoom.x;
2303 imin = (int) ((offset + corr) / minor_tick + 1);
2304 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2305 for (i=imin; i <= imax; i++) {
2306 int x = (int) (rint(i * minor_tick) - offset - corr);
2307 if ((x > 0) && (x < axis->s.width)) {
2308 cairo_move_to(cr, x+0.5, 0);
2309 cairo_line_to(cr, x+0.5, 8);
2313 for (i=0; axis->label[i]; i++) {
2314 gint w, h;
2315 layout = gtk_widget_create_pango_layout(g->drawing_area,
2316 axis->label[i]);
2317 pango_layout_get_pixel_size(layout, &w, &h);
2318 cairo_move_to(cr, axis->s.width - w - 50, 15+h+15 + i*(h+3));
2319 pango_cairo_show_layout(cr, layout);
2320 g_object_unref(G_OBJECT(layout));
2322 cairo_stroke(cr);
2323 cairo_destroy(cr);
2326 static void axis_pixmaps_switch(struct axis *axis)
2328 axis->displayed = 1 ^ axis->displayed;
2331 static void axis_pixmap_display(struct axis *axis)
2333 cairo_t *cr;
2335 cr = gdk_cairo_create(gtk_widget_get_window(axis->drawing_area));
2336 #if GTK_CHECK_VERSION(2,22,0)
2337 cairo_set_source_surface(cr, axis->surface[axis->displayed], axis->p.x, axis->p.y);
2338 #else
2339 gdk_cairo_set_source_pixmap(cr, axis->pixmap[axis->displayed], axis->p.x, axis->p.y);
2340 #endif
2341 cairo_rectangle(cr, axis->p.x, axis->p.y, axis->p.width, axis->p.height);
2342 cairo_fill(cr);
2343 cairo_destroy(cr);
2347 static void axis_compute_ticks(struct axis *axis, double x0, double xmax, int dir)
2349 int i, j, ii, jj, ms;
2350 double zoom, x, steps[3] = { 0.1, 0.5 };
2351 int dim, check_needed, diminished;
2352 double majthresh[2] = {2.0, 3.0};
2354 debug((DBS_FENTRY | DBS_AXES_TICKS)) puts("axis_compute_ticks()");
2355 debug(DBS_AXES_TICKS)
2356 printf("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2358 zoom = axis_zoom_get(axis, dir);
2359 x = xmax-x0;
2360 for (i=-9; i <= 12; i++) {
2361 if (x / pow(10, i) < 1)
2362 break;
2364 --i;
2365 ms = (int )(x / pow(10, i));
2367 if (ms > 5) {
2368 j = 0;
2369 ++i;
2370 } else if (ms > 2)
2371 j = 1;
2372 else
2373 j = 0;
2375 axis->major = steps[j] * pow(10, i);
2377 debug(DBS_AXES_TICKS) printf("zoom=%.1f, x=%f " UTF8_RIGHTWARDS_ARROW " i=%d " UTF8_RIGHTWARDS_ARROW " ms=%d " UTF8_RIGHTWARDS_ARROW " j=%d ->"
2378 " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2380 /* let's compute minor ticks */
2381 jj = j;
2382 ii = i;
2383 axis_ticks_down(&ii, &jj);
2384 axis->minor = steps[jj] * pow(10, ii);
2385 /* we don't want minors if they would be less than 10 pixels apart */
2386 if (axis->minor*zoom < 10) {
2387 debug(DBS_AXES_TICKS) printf("refusing axis->minor of %f: "
2388 "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2389 axis->minor = 0;
2392 check_needed = TRUE;
2393 diminished = FALSE;
2394 while (check_needed) {
2395 check_needed = FALSE;
2396 dim = get_label_dim(axis, dir, xmax);
2397 debug(DBS_AXES_TICKS) printf("axis->major==%.1f, axis->minor==%.1f =>"
2398 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2399 axis->major, axis->minor, axis->major*zoom/dim,
2400 axis->minor*zoom/dim);
2402 /* corrections: if majors are less than majthresh[dir] times label
2403 * dimension apart, we need to use bigger ones */
2404 if (axis->major*zoom / dim < majthresh[dir]) {
2405 axis_ticks_up(&ii, &jj);
2406 axis->minor = axis->major;
2407 axis_ticks_up(&i, &j);
2408 axis->major = steps[j] * pow(10, i);
2409 check_needed = TRUE;
2410 debug(DBS_AXES_TICKS) printf("axis->major enlarged to %.1f\n",
2411 axis->major);
2413 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2414 * we could promote them to majors as well */
2415 if (((axis->minor * zoom / dim) > majthresh[dir]) && !diminished) {
2416 axis_ticks_down(&i, &j);
2417 axis->major = axis->minor;
2418 axis_ticks_down(&ii, &jj);
2419 axis->minor = steps[jj] * pow(10, ii);
2420 check_needed = TRUE;
2421 diminished = TRUE;
2423 debug(DBS_AXES_TICKS) printf("axis->minor diminished to %.1f\n",
2424 axis->minor);
2426 if (axis->minor*zoom < 10) {
2427 debug(DBS_AXES_TICKS) printf("refusing axis->minor of %f:"
2428 " axis->minor*zoom == %f\n",
2429 axis->minor, axis->minor*zoom);
2430 axis->minor = 0;
2435 debug(DBS_AXES_TICKS) printf("corrected: axis->major == %.1f ->"
2436 " axis->minor == %.1f\n", axis->major, axis->minor);
2439 static void axis_ticks_up(int *i, int *j)
2441 (*j)++;
2442 if (*j > 1) {
2443 (*i)++;
2444 *j = 0;
2448 static void axis_ticks_down(int *i, int *j)
2450 (*j)--;
2451 if (*j < 0) {
2452 (*i)--;
2453 *j = 1;
2457 static int get_label_dim(struct axis *axis, int dir, double label)
2459 double y;
2460 char str[32];
2461 int rdigits, dim;
2462 PangoLayout *layout;
2464 /* First, let's compute how many digits to the right of radix
2465 * we need to print */
2466 y = axis->major - floor(axis->major);
2467 for (rdigits=0; rdigits <= 6; rdigits++) {
2468 y *= 10;
2469 if (y <= 0.000001)
2470 break;
2471 y = y - floor(y);
2473 g_snprintf(str, sizeof(str), "%.*f", rdigits, label);
2474 switch (dir) {
2475 case AXIS_HORIZONTAL:
2476 layout = gtk_widget_create_pango_layout(axis->g->drawing_area, str);
2477 pango_layout_get_pixel_size(layout, &dim, NULL);
2478 g_object_unref(G_OBJECT(layout));
2479 break;
2480 case AXIS_VERTICAL:
2481 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2482 str);
2483 pango_layout_get_pixel_size(layout, NULL, &dim);
2484 g_object_unref(G_OBJECT(layout));
2485 break;
2486 default:
2487 puts("initialize axis: an axis must be either horizontal or vertical");
2488 return -1;
2490 return dim;
2493 static double axis_zoom_get(struct axis *axis, int dir)
2495 switch (dir) {
2496 case AXIS_HORIZONTAL:
2497 return axis->g->zoom.x;
2498 case AXIS_VERTICAL:
2499 return axis->g->zoom.y;
2500 default:
2501 return -1;
2505 static void graph_select_segment(struct gtk_graph *g, int x, int y)
2507 struct element_list *list;
2508 struct element *e;
2509 guint num = 0;
2511 debug(DBS_FENTRY) puts("graph_select_segment()");
2513 x -= g->geom.x;
2514 y = g->geom.height-1 - (y - g->geom.y);
2516 set_busy_cursor(gtk_widget_get_window(g->drawing_area));
2518 for (list=g->elists; list; list=list->next) {
2519 for (e=list->elements; e->type != ELMT_NONE; e++) {
2520 switch (e->type) {
2521 case ELMT_RECT:
2522 break;
2523 case ELMT_LINE:
2524 if (line_detect_collision(e, x, y)) {
2525 num = e->parent->num;
2527 break;
2528 case ELMT_ELLIPSE:
2529 if (ellipse_detect_collision(e, x, y)) {
2530 num = e->parent->num;
2532 break;
2533 default:
2534 break;
2539 if (num) {
2540 cf_goto_frame(&cfile, num);
2542 unset_busy_cursor(gtk_widget_get_window(g->drawing_area), g->cross.draw);
2545 static int line_detect_collision(struct element *e, int x, int y)
2547 int xx1, yy1, xx2, yy2;
2549 if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2550 xx1 = (int )rint(e->p.line.dim.x1);
2551 xx2 = (int )rint(e->p.line.dim.x2);
2552 } else {
2553 xx1 = (int )rint(e->p.line.dim.x2);
2554 xx2 = (int )rint(e->p.line.dim.x1);
2556 if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2557 yy1 = (int )rint(e->p.line.dim.y1);
2558 yy2 = (int )rint(e->p.line.dim.y2);
2559 } else {
2560 yy1 = (int )rint(e->p.line.dim.y2);
2561 yy2 = (int )rint(e->p.line.dim.y1);
2564 printf("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2566 if (((xx1 == x) && (xx2 == x) && (yy1 <= y) && (y <= yy2)) ||
2567 ((yy1 == y) && (yy2 == y) && (xx1 <= x) && (x <= xx2)))
2568 return TRUE;
2569 else
2570 return FALSE;
2573 static int ellipse_detect_collision(struct element *e, int x, int y)
2575 int xx1, yy1, xx2, yy2;
2577 xx1 = (int )rint(e->p.ellipse.dim.x);
2578 xx2 = (int )rint(e->p.ellipse.dim.x + e->p.ellipse.dim.width);
2579 yy1 = (int )rint(e->p.ellipse.dim.y - e->p.ellipse.dim.height);
2580 yy2 = (int )rint(e->p.ellipse.dim.y);
2582 printf("ellipse: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2584 if ((xx1 <= x) && (x <= xx2) && (yy1 <= y) && (y <= yy2))
2585 return TRUE;
2586 else
2587 return FALSE;
2590 static void cross_draw(struct gtk_graph *g, int x, int y)
2592 /* Shouldn't draw twice onto the same position if haven't erased in the
2593 meantime! */
2594 if (g->cross.erase_needed && (g->cross.x == x) && (g->cross.y == y)) {
2595 return;
2598 /* Draw the cross */
2599 if ((x > g->wp.x + 0.5) && (x < g->wp.x+g->wp.width) &&
2600 (y > g->wp.y) && (y < g->wp.y+g->wp.height)) {
2602 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(g->drawing_area));
2603 gdk_cairo_set_source_rgba(cr, &g->s.tseq_tcptrace.seq_color);
2604 cairo_set_line_width(cr, 1.0);
2606 /* Horizonal line */
2607 cairo_move_to(cr, g->wp.x, y);
2608 cairo_line_to(cr, g->wp.x + g->wp.width, y);
2610 /* Vertical line */
2611 cairo_move_to(cr, x, g->wp.y);
2612 cairo_line_to(cr, x, g->wp.y + g->wp.height);
2613 cairo_stroke(cr);
2614 cairo_destroy(cr);
2617 g->cross.x = x;
2618 g->cross.y = y;
2619 g->cross.erase_needed = TRUE;
2622 static void zoomrect_draw(struct gtk_graph *g, int x, int y)
2624 if ((x > g->wp.x + 0.5) && (x < g->wp.x+g->wp.width) &&
2625 (y > g->wp.y) && (y < g->wp.y+g->wp.height)) {
2627 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(g->drawing_area));
2628 gdk_cairo_set_source_rgba(cr, &g->s.tseq_tcptrace.seq_color);
2629 cairo_set_line_width(cr, 1.0);
2631 /* Do outline of rect */
2632 cairo_rectangle(cr, zoomrect.x, zoomrect.y, x-zoomrect.x, y-zoomrect.y);
2633 cairo_stroke(cr);
2634 cairo_destroy(cr);
2637 g->zoomrect_erase_needed = TRUE;
2640 static void zoomrect_erase(struct gtk_graph *g)
2642 /* Just redraw what is in the pixmap buffer */
2643 graph_pixmap_display(g);
2644 g->zoomrect_erase_needed = FALSE;
2647 static void cross_erase(struct gtk_graph *g)
2649 int x = g->cross.x;
2650 int y = g->cross.y;
2652 g->cross.erase_needed = FALSE;
2654 if ((x > g->wp.x) && (x < g->wp.x+g->wp.width) &&
2655 (y >= g->wp.y) && (y < g->wp.y+g->wp.height)) {
2657 /* Just redraw what is in the pixmap buffer */
2658 graph_pixmap_display(g);
2662 static void magnify_move(struct gtk_graph *g, int x, int y)
2664 struct ipoint pos, offsetpos;
2666 get_mouse_position(g->toplevel, &pos.x, &pos.y, NULL);
2667 g->magnify.x = pos.x + x - g->magnify.width/2;
2668 g->magnify.y = pos.y + y - g->magnify.height/2;
2669 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2670 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2671 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2672 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2673 magnify_get_geom(g, x, y);
2674 magnify_draw(g);
2677 static void magnify_create(struct gtk_graph *g, int x, int y)
2679 struct gtk_graph *mg;
2680 struct ipoint pos, offsetpos;
2681 GdkEvent *e = NULL;
2682 struct element_list *list, *new_list;
2684 mg = g->magnify.g = (struct gtk_graph * )g_malloc(sizeof(struct gtk_graph));
2685 memcpy((void * )mg, (void * )g, sizeof(struct gtk_graph));
2687 mg->toplevel = dlg_window_new("tcp graph magnify");
2688 mg->drawing_area = mg->toplevel;
2689 gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2690 gtk_widget_set_events(mg->drawing_area, GDK_EXPOSURE_MASK
2691 /* | GDK_ENTER_NOTIFY_MASK */
2692 /* | GDK_ALL_EVENTS_MASK */
2695 mg->wp.x = 0;
2696 mg->wp.y = 0;
2697 mg->wp.width = g->magnify.width;
2698 mg->wp.height = g->magnify.height;
2699 mg->geom.width = (int )rint(g->geom.width * g->magnify.zoom.x);
2700 mg->geom.height = (int )rint(g->geom.height * g->magnify.zoom.y);
2701 mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2702 mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2704 /* in order to keep original element lists intact we need our own */
2705 graph_element_lists_initialize(mg);
2706 list = g->elists->next;
2707 new_list = mg->elists;
2708 for ( ; list; list=list->next) {
2709 new_list->next = (struct element_list * )g_malloc(sizeof(struct element_list));
2710 new_list = new_list->next;
2711 new_list->next = NULL;
2712 new_list->elements = NULL;
2714 graph_element_lists_make(mg);
2716 get_mouse_position(g->toplevel, &pos.x, &pos.y, NULL);
2717 g->magnify.x = pos.x + x - g->magnify.width/2;
2718 g->magnify.y = pos.y + y - g->magnify.height/2;
2719 offsetpos.x = g->magnify.x + g->magnify.offset.x;
2720 offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
2721 offsetpos.y = g->magnify.y + g->magnify.offset.y;
2722 offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
2723 gtk_window_set_position(GTK_WINDOW(mg->drawing_area), GTK_WIN_POS_NONE);
2724 magnify_get_geom(g, x, y);
2726 gtk_widget_show(mg->drawing_area);
2728 /* we need to wait for the first expose event before we start drawing */
2729 while (!gdk_events_pending());
2730 do {
2731 e = gdk_event_get();
2732 if (e) {
2733 if (e->any.type == GDK_EXPOSE) {
2734 gdk_event_free(e);
2735 break;
2737 gdk_event_free(e);
2739 } while (e);
2741 #if GTK_CHECK_VERSION(2,22,0)
2742 mg->surface[0] = mg->surface[1] = NULL;
2743 #else
2744 mg->pixmap[0] = mg->pixmap[1] = NULL;
2745 #endif /* GTK_CHECK_VERSION(2,22,0) */
2746 graph_pixmaps_create(mg);
2747 magnify_draw(g);
2748 g->magnify.active = 1;
2751 static void magnify_destroy(struct gtk_graph *g)
2753 struct element_list *list;
2754 struct gtk_graph *mg = g->magnify.g;
2756 window_destroy(GTK_WIDGET(mg->drawing_area));
2758 #if GTK_CHECK_VERSION(2,22,0)
2759 if (mg->surface[0]) {
2760 cairo_surface_destroy(mg->surface[0]);
2762 if (mg->surface[1]) {
2763 cairo_surface_destroy(mg->surface[1]);
2765 #else
2766 g_object_unref(mg->pixmap[0]);
2767 g_object_unref(mg->pixmap[1]);
2768 #endif /* GTK_CHECK_VERSION(2,22,0) */
2769 for (list=mg->elists; list; list=list->next)
2770 g_free(list->elements);
2772 if (mg->elists) {
2773 while (mg->elists->next) {
2774 list = mg->elists->next->next;
2775 g_free(mg->elists->next);
2776 mg->elists->next = list;
2779 g_free(g->magnify.g);
2780 g->magnify.active = 0;
2783 static void magnify_get_geom(struct gtk_graph *g, int x, int y)
2785 int posx, posy;
2787 get_mouse_position(g->toplevel, &posx, &posy, NULL);
2789 g->magnify.g->geom.x = g->geom.x;
2790 g->magnify.g->geom.y = g->geom.y;
2792 g->magnify.g->geom.x -= (int )rint((g->magnify.g->geom.width - g->geom.width) *
2793 ((x-g->geom.x)/(double )g->geom.width));
2794 g->magnify.g->geom.y -= (int )rint((g->magnify.g->geom.height - g->geom.height) *
2795 ((y-g->geom.y)/(double )g->geom.height));
2797 /* we have coords of origin of graph relative to origin of g->toplevel.
2798 * now we need them to relate to origin of magnify window */
2799 g->magnify.g->geom.x -= (g->magnify.x - posx);
2800 g->magnify.g->geom.y -= (g->magnify.y - posy);
2803 static void magnify_draw(struct gtk_graph *g)
2805 cairo_t *cr;
2806 int not_disp = 1 ^ g->magnify.g->displayed;
2808 graph_pixmap_draw(g->magnify.g);
2809 /* graph pixmap is almost ready, just add border */
2810 #if GTK_CHECK_VERSION(2,22,0)
2811 cr = cairo_create(g->magnify.g->surface[not_disp]);
2812 #else
2813 cr = gdk_cairo_create(g->magnify.g->pixmap[not_disp]);
2814 #endif /* GTK_CHECK_VERSION(2,22,0) */
2815 cairo_set_line_width(cr, 1.0);
2816 cairo_move_to(cr, 0, 0);
2817 cairo_line_to(cr, g->magnify.width - 1, 0);
2818 cairo_stroke(cr);
2820 cairo_move_to(cr, g->magnify.width - 1, 0);
2821 cairo_line_to(cr, g->magnify.width - 1, g->magnify.height);
2822 cairo_stroke(cr);
2824 cairo_move_to(cr, 0, 0);
2825 cairo_line_to(cr, 0, g->magnify.height - 1);
2826 cairo_stroke(cr);
2828 cairo_move_to(cr, 0, g->magnify.height - 1);
2829 cairo_line_to(cr, g->magnify.width - 1, g->magnify.height - 1);
2830 cairo_stroke(cr);
2831 cairo_destroy(cr);
2833 graph_pixmaps_switch(g->magnify.g);
2834 graph_pixmap_display(g->magnify.g);
2838 static gboolean configure_event(GtkWidget *widget _U_, GdkEventConfigure *event, gpointer user_data)
2840 struct gtk_graph *g = (struct gtk_graph *)user_data;
2841 struct {
2842 double x, y;
2843 } zoom;
2844 int cur_g_width, cur_g_height;
2845 int cur_wp_width, cur_wp_height;
2847 debug(DBS_FENTRY) puts("configure_event()");
2849 cur_wp_width = g->wp.width;
2850 cur_wp_height = g->wp.height;
2851 g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
2852 g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
2853 g->x_axis->s.width = g->wp.width;
2854 g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
2855 g->y_axis->p.height = g->wp.height + g->wp.y;
2856 g->y_axis->s.height = g->wp.height;
2857 g->x_axis->p.y = g->y_axis->p.height;
2858 zoom.x = (double )g->wp.width / cur_wp_width;
2859 zoom.y = (double )g->wp.height / cur_wp_height;
2860 cur_g_width = g->geom.width;
2861 cur_g_height = g->geom.height;
2862 g->geom.width = (int )rint(g->geom.width * zoom.x);
2863 g->geom.height = (int )rint(g->geom.height * zoom.y);
2864 g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
2865 g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
2866 /* g->zoom.initial.x = g->zoom.x; */
2867 /* g->zoom.initial.y = g->zoom.y; */
2869 g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
2870 (g->wp.x - g->geom.x));
2871 g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
2872 (g->wp.y - g->geom.y));
2873 #if 0
2874 printf("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d);"
2875 " zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
2876 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2877 g->zoom.x, g->zoom.y);
2878 #endif
2880 update_zoom_spins(g);
2881 graph_element_lists_make(g);
2882 graph_pixmaps_create(g);
2883 graph_title_pixmap_create(g);
2884 axis_pixmaps_create(g->y_axis);
2885 axis_pixmaps_create(g->x_axis);
2886 /* we don't do actual drawing here; we leave it to expose handler */
2887 graph_pixmap_draw(g);
2888 graph_pixmaps_switch(g);
2889 graph_title_pixmap_draw(g);
2890 h_axis_pixmap_draw(g->x_axis);
2891 axis_pixmaps_switch(g->x_axis);
2892 v_axis_pixmap_draw(g->y_axis);
2893 axis_pixmaps_switch(g->y_axis);
2894 return TRUE;
2896 #if GTK_CHECK_VERSION(3,0,0)
2897 static gboolean
2898 draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data)
2900 struct gtk_graph *g = (struct gtk_graph *)user_data;
2902 debug(DBS_FENTRY) puts("draw_event()");
2904 /* lower left corner */
2905 cairo_set_source_rgb(cr, 1, 1, 1);
2906 cairo_rectangle(cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2907 cairo_fill(cr);
2909 /* right margin */
2910 cairo_rectangle(cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2911 cairo_fill(cr);
2913 /* Should these routines be copied here, or be given the cairo_t ?? */
2914 graph_pixmap_display(g);
2915 graph_title_pixmap_display(g);
2916 axis_pixmap_display(g->x_axis);
2917 axis_pixmap_display(g->y_axis);
2919 return TRUE;
2921 #else
2922 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
2924 struct gtk_graph *g = (struct gtk_graph *)user_data;
2925 cairo_t *cr;
2927 debug(DBS_FENTRY) puts("expose_event()");
2929 if (event->count)
2930 return TRUE;
2932 /* lower left corner */
2933 cr = gdk_cairo_create(gtk_widget_get_window(widget));
2934 cairo_set_source_rgb(cr, 1, 1, 1);
2935 cairo_rectangle(cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
2936 cairo_fill(cr);
2937 cairo_destroy(cr);
2938 cr = NULL;
2940 /* right margin */
2941 cr = gdk_cairo_create(gtk_widget_get_window(widget));
2942 cairo_set_source_rgb(cr, 1, 1, 1);
2943 cairo_rectangle(cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
2944 cairo_fill(cr);
2945 cairo_destroy(cr);
2946 cr = NULL;
2948 graph_pixmap_display(g);
2949 graph_title_pixmap_display(g);
2950 axis_pixmap_display(g->x_axis);
2951 axis_pixmap_display(g->y_axis);
2953 return TRUE;
2955 #endif
2957 #define ZOOM_REDRAW 1
2958 #define ZOOM_NOREDRAW 0
2959 static void
2960 perform_zoom(struct gtk_graph *g, struct zoomfactor *zf,
2961 int origin_x, int origin_y, int redraw)
2963 int cur_width = g->geom.width, cur_height = g->geom.height;
2965 /* Multiply by x and y factors */
2966 g->geom.width = (int )rint(g->geom.width * zf->x);
2967 g->geom.height = (int )rint(g->geom.height * zf->y);
2969 /* If already fully-zoomed out, don't waste time re-drawing */
2970 if ((g->geom.width <= g->wp.width) &&
2971 (g->geom.height <= g->wp.height)) {
2972 return;
2975 if (g->geom.width < g->wp.width) {
2976 g->geom.width = g->wp.width;
2978 if (g->geom.height < g->wp.height) {
2979 g->geom.height = g->wp.height;
2982 /* Divide to work out new zoom */
2983 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
2984 g->zoom.y = (g->geom.height- 1) / g->bounds.height;
2986 /* Move origin to keep mouse position at centre of view */
2987 g->geom.x -= (int)rint((g->geom.width - cur_width) *
2988 ((origin_x - g->geom.x)/(double )cur_width));
2989 g->geom.y -= (int)rint((g->geom.height - cur_height) *
2990 ((origin_y - g->geom.y)/(double )cur_height));
2992 if (g->geom.x > g->wp.x)
2993 g->geom.x = g->wp.x;
2994 if (g->geom.y > g->wp.y)
2995 g->geom.y = g->wp.y;
2996 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
2997 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
2998 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
2999 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3001 if (redraw == ZOOM_NOREDRAW)
3002 return;
3004 graph_element_lists_make(g);
3005 g->cross.erase_needed = FALSE;
3006 graph_display(g);
3007 axis_display(g->y_axis);
3008 axis_display(g->x_axis);
3009 update_zoom_spins(g);
3011 if (g->cross.draw) {
3012 g->cross.erase_needed = FALSE;
3013 cross_draw(g, origin_x, origin_y);
3017 static void
3018 get_zoomfactor(struct gtk_graph *g, struct zoomfactor *zf, double step_x,
3019 double step_y)
3021 if (g->zoom.flags & ZOOM_OUT) {
3023 * If can't zoom out anymore don't waste time redrawing
3024 * the whole graph!
3026 if ((g->geom.height <= g->wp.height) &&
3027 (g->geom.width <= g->wp.width)) {
3028 zf->x = 1.0;
3029 zf->y = 1.0;
3030 return;
3032 if (g->zoom.flags & ZOOM_HLOCK)
3033 zf->x = 1.0;
3034 else
3035 zf->x = 1 / step_x;
3036 if (g->zoom.flags & ZOOM_VLOCK)
3037 zf->y = 1.0;
3038 else
3039 zf->y = 1 / step_y;
3040 } else {
3041 if (g->zoom.flags & ZOOM_HLOCK)
3042 zf->x = 1.0;
3043 else
3044 zf->x = step_x;
3045 if (g->zoom.flags & ZOOM_VLOCK)
3046 zf->y = 1.0;
3047 else
3048 zf->y = step_y;
3052 static void
3053 do_zoom_rectangle(struct gtk_graph *g, struct irect lcl_zoomrect)
3055 int cur_width = g->wp.width, cur_height = g->wp.height;
3056 struct irect geom1 = g->geom;
3057 struct zoomfactor factor;
3059 /* Left hand too much to the right */
3060 if (lcl_zoomrect.x > g->wp.x + g->wp.width)
3061 return;
3062 /* Right hand not far enough */
3063 if (lcl_zoomrect.x + lcl_zoomrect.width < g->wp.x)
3064 return;
3065 /* Left hand too much to the left */
3066 if (lcl_zoomrect.x < g->wp.x) {
3067 int dx = g->wp.x - lcl_zoomrect.x;
3068 lcl_zoomrect.x += dx;
3069 lcl_zoomrect.width -= dx;
3071 /* Right hand too much to the right */
3072 if (lcl_zoomrect.x + lcl_zoomrect.width > g->wp.x + g->wp.width) {
3073 int dx = lcl_zoomrect.width + lcl_zoomrect.x - g->wp.x - g->wp.width;
3074 lcl_zoomrect.width -= dx;
3077 /* Top too low */
3078 if (lcl_zoomrect.y > g->wp.y + g->wp.height)
3079 return;
3080 /* Bottom too high */
3081 if (lcl_zoomrect.y + lcl_zoomrect.height < g->wp.y)
3082 return;
3083 /* Top too high */
3084 if (lcl_zoomrect.y < g->wp.y) {
3085 int dy = g->wp.y - lcl_zoomrect.y;
3086 lcl_zoomrect.y += dy;
3087 lcl_zoomrect.height -= dy;
3089 /* Bottom too low */
3090 if (lcl_zoomrect.y + lcl_zoomrect.height > g->wp.y + g->wp.height) {
3091 int dy = lcl_zoomrect.height + lcl_zoomrect.y - g->wp.y - g->wp.height;
3092 lcl_zoomrect.height -= dy;
3096 printf("before:\n"
3097 "\tgeom: (%d, %d)+(%d x %d)\n"
3100 get_zoomfactor(g, &factor, (double)cur_width / lcl_zoomrect.width,
3101 (double)cur_height / lcl_zoomrect.height);
3103 printf("Zoomfactor: %f x %f\n", factor.x, factor.y);
3105 perform_zoom(g, &factor,
3106 lcl_zoomrect.x, lcl_zoomrect.y,
3107 ZOOM_NOREDRAW);
3110 printf("middle:\n"
3111 "\tgeom: (%d, %d)+(%d x %d)\n"
3112 "\twp: (%d, %d)+(%d x %d)\n"
3113 "\tzoomrect: (%d, %d)+(%d x %d)\n",
3114 g->geom.x, g->geom.y,
3115 g->geom.width, g->geom.height,
3116 g->wp.x, g->wp.y, g->wp.width, g->wp.height,
3117 lcl_zoomrect.x, lcl_zoomrect.y, lcl_zoomrect.width, lcl_zoomrect.height);
3119 g->geom.x = (int)(geom1.x * (1 + factor.x) -
3120 lcl_zoomrect.x * factor.x - (geom1.x - g->wp.x));
3121 g->geom.y = (int)(geom1.y * (1 + factor.y) -
3122 lcl_zoomrect.y * factor.y - (geom1.y - g->wp.y));
3125 printf("after:\n"
3126 "\tgeom: (%d, %d)+(%d x %d)\n"
3127 "\twp: (%d, %d)+(%d x %d)\n"
3128 "\tzoomrect: (%d, %d)+(%d x %d)\n",
3129 g->geom.x, g->geom.y,
3130 g->geom.width, g->geom.height,
3131 g->wp.x, g->wp.y, g->wp.width, g->wp.height,
3132 lcl_zoomrect.x, lcl_zoomrect.y, lcl_zoomrect.width, lcl_zoomrect.height);
3135 graph_element_lists_make(g);
3136 g->cross.erase_needed = FALSE;
3137 graph_display(g);
3138 axis_display(g->y_axis);
3139 axis_display(g->x_axis);
3140 update_zoom_spins(g);
3145 static void do_zoom_mouse(struct gtk_graph *g, GdkEventButton *event)
3147 struct zoomfactor factor;
3149 get_zoomfactor(g, &factor, g->zoom.step_x, g->zoom.step_y);
3150 perform_zoom(g, &factor, (int)event->x, (int)event->y, ZOOM_REDRAW);
3153 static void do_zoom_keyboard(struct gtk_graph *g)
3155 int pointer_x, pointer_y;
3156 struct zoomfactor factor;
3158 get_mouse_position(g->drawing_area, &pointer_x, &pointer_y, NULL);
3159 get_zoomfactor(g, &factor, g->zoom.step_x, g->zoom.step_y);
3160 perform_zoom(g, &factor, pointer_x, pointer_y, ZOOM_REDRAW);
3163 static void do_zoom_in_keyboard(struct gtk_graph *g)
3165 gtk_toggle_button_set_active(g->zoom.widget.in_toggle, TRUE);
3166 do_zoom_keyboard(g);
3169 static void do_zoom_out_keyboard(struct gtk_graph *g)
3171 gtk_toggle_button_set_active(g->zoom.widget.out_toggle, TRUE);
3172 do_zoom_keyboard(g);
3173 gtk_toggle_button_set_active(g->zoom.widget.in_toggle, TRUE);
3176 static void do_select_segment(struct gtk_graph *g)
3178 int pointer_x, pointer_y;
3180 get_mouse_position(g->drawing_area, &pointer_x, &pointer_y, NULL);
3181 graph_select_segment(g, pointer_x, pointer_y);
3184 static void do_wscale_graph(struct gtk_graph *g)
3186 gtk_toggle_button_set_active(g->gt.graph_wscale, TRUE);
3189 static void do_rtt_graph(struct gtk_graph *g)
3191 gtk_toggle_button_set_active(g->gt.graph_rtt, TRUE);
3194 static void do_throughput_graph(struct gtk_graph *g)
3196 gtk_toggle_button_set_active(g->gt.graph_tput, TRUE);
3199 static void do_ts_graph_stevens(struct gtk_graph *g)
3201 gtk_toggle_button_set_active(g->gt.graph_tseqstevens, TRUE);
3204 static void do_ts_graph_tcptrace(struct gtk_graph *g)
3206 gtk_toggle_button_set_active(g->gt.graph_tseqttrace, TRUE);
3209 static void do_magnify_create(struct gtk_graph *g)
3211 int pointer_x, pointer_y;
3213 get_mouse_position(g->drawing_area, &pointer_x, &pointer_y, NULL);
3214 magnify_create(g, (int )rint(pointer_x), (int )rint(pointer_y));
3217 static void do_key_motion(struct gtk_graph *g)
3219 if (g->geom.x > g->wp.x)
3220 g->geom.x = g->wp.x;
3221 if (g->geom.y > g->wp.y)
3222 g->geom.y = g->wp.y;
3223 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3224 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3225 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3226 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3227 g->cross.erase_needed = FALSE;
3228 graph_display(g);
3229 axis_display(g->y_axis);
3230 axis_display(g->x_axis);
3231 if (g->cross.draw) {
3232 int pointer_x, pointer_y;
3234 get_mouse_position(g->drawing_area, &pointer_x, &pointer_y, NULL);
3235 g->cross.erase_needed = FALSE;
3236 cross_draw(g, pointer_x, pointer_y);
3240 static void do_key_motion_up(struct gtk_graph *g, int step)
3242 g->geom.y += step;
3243 do_key_motion(g);
3246 static void do_key_motion_down(struct gtk_graph *g, int step)
3248 g->geom.y -= step;
3249 do_key_motion(g);
3252 static void do_key_motion_left(struct gtk_graph *g, int step)
3254 g->geom.x += step;
3255 do_key_motion(g);
3258 static void do_key_motion_right(struct gtk_graph *g, int step)
3260 g->geom.x -= step;
3261 do_key_motion(g);
3264 static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
3266 struct gtk_graph *g = (struct gtk_graph *)user_data;
3268 debug(DBS_FENTRY) puts("button_press_event()");
3270 if (event->button == MOUSE_BUTTON_RIGHT) {
3271 if (event->state & GDK_CONTROL_MASK) {
3272 magnify_create(g, (int )rint(event->x), (int )rint(event->y));
3273 } else {
3274 g->grab.x = (int )rint(event->x) - g->geom.x;
3275 g->grab.y = (int )rint(event->y) - g->geom.y;
3276 g->grab.grabbed = TRUE;
3278 #ifdef ORIGINAL_WIN32_BUTTONS
3279 /* Windows mouse control: */
3280 /* [<ctrl>-left] - select packet */
3281 /* [left] - zoom in */
3282 /* [<shift>-left] - zoom out */
3283 } else if (event->button == MOUSE_BUTTON_LEFT) {
3284 if (event->state & GDK_CONTROL_MASK) {
3285 graph_select_segment(g, (int)event->x, (int)event->y);
3286 } else {
3287 #else /* !ORIGINAL_WIN32_BUTTONS */
3288 } else if (event->button == MOUSE_BUTTON_MIDDLE) {
3289 #endif
3290 /* Shift means we should zoom out */
3291 if (event->state & GDK_SHIFT_MASK) {
3292 gtk_toggle_button_set_active(g->zoom.widget.out_toggle, TRUE);
3294 else {
3295 gtk_toggle_button_set_active(g->zoom.widget.in_toggle, TRUE);
3297 do_zoom_mouse(g, event);
3298 #ifndef ORIGINAL_WIN32_BUTTONS
3299 } else if (event->button == MOUSE_BUTTON_LEFT) {
3300 /* See if we're on an element that links to a frame */
3301 graph_select_segment(g, (int )event->x, (int )event->y);
3303 /* Record the origin of the zoom rectangle */
3304 zoomrect.x = (int)event->x;
3305 zoomrect.y = (int)event->y;
3306 #else /* ORIGINAL_WIN32_BUTTONS*/
3308 #endif
3310 return TRUE;
3313 static gboolean motion_notify_event(GtkWidget *widget _U_, GdkEventMotion *event, gpointer user_data)
3315 struct gtk_graph *g = (struct gtk_graph *)user_data;
3316 int x, y;
3317 GdkModifierType state;
3319 /* debug(DBS_FENTRY) puts("motion_notify_event()"); */
3321 if (event->is_hint)
3322 get_mouse_position(g->drawing_area, &x, &y, &state);
3323 else {
3324 x = (int) event->x;
3325 y = (int) event->y;
3326 state = (GdkModifierType)event->state;
3329 /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
3330 * is pressed while pointer is in motion, we will receive one more motion
3331 * notify *before* we get the button press. This last motion notify works
3332 * with stale grab coordinates */
3333 if (state & GDK_BUTTON3_MASK) {
3334 if (g->grab.grabbed) {
3335 g->geom.x = x-g->grab.x;
3336 g->geom.y = y-g->grab.y;
3338 if (g->geom.x > g->wp.x)
3339 g->geom.x = g->wp.x;
3340 if (g->geom.y > g->wp.y)
3341 g->geom.y = g->wp.y;
3342 if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3343 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3344 if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3345 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3346 g->cross.erase_needed = FALSE;
3347 graph_display(g);
3348 axis_display(g->y_axis);
3349 axis_display(g->x_axis);
3350 if (g->cross.draw) {
3351 cross_draw(g, x, y);
3353 } else if (g->magnify.active)
3354 magnify_move(g, x, y);
3355 } else if (state & GDK_BUTTON1_MASK) {
3357 /* TODO: not sure we really want to jump to frames unless we release? */
3358 graph_select_segment(g, x, y);
3360 /* Update cross if necessary */
3361 if (g->cross.erase_needed)
3362 cross_erase(g);
3363 if (g->cross.draw)
3364 cross_draw(g, x, y);
3366 /* Draw bounded box for zoomrect being chosen! */
3367 if (g->zoomrect_erase_needed) {
3368 zoomrect_erase(g);
3370 zoomrect_draw(g, x, y);
3371 } else {
3372 if (g->cross.erase_needed)
3373 cross_erase(g);
3374 if (g->cross.draw) {
3375 cross_draw(g, x, y);
3379 return TRUE;
3382 static gboolean button_release_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
3384 struct gtk_graph *g = (struct gtk_graph *)user_data;
3386 debug(DBS_FENTRY) puts("button_release_event()");
3388 if (event->button == MOUSE_BUTTON_RIGHT)
3389 g->grab.grabbed = FALSE;
3391 if (event->button == MOUSE_BUTTON_LEFT) {
3392 int xx1 = zoomrect.x;
3393 int xx2 = (int)event->x;
3394 int yy1 = zoomrect.y;
3395 int yy2 = (int)event->y;
3396 zoomrect.x = MIN(xx1, xx2);
3397 zoomrect.width = abs(xx1 - xx2);
3398 zoomrect.y = MIN(yy1, yy2);
3399 zoomrect.height = abs(yy1 - yy2);
3401 /* Finish selecting a region to zoom in on.
3402 Take care not to choose a too-small area (by accident?) */
3403 if ((zoomrect.width > 3) && (zoomrect.height > 3)) {
3404 int oldflags = g->zoom.flags;
3406 debug(DBS_GRAPH_DRAWING) printf("Zoom in from (%d, %d) - (%d, %d)\n",
3407 zoomrect.x, zoomrect.y,
3408 zoomrect.width, zoomrect.height);
3410 g->zoom.flags &= ~ZOOM_OUT;
3411 do_zoom_rectangle(g, zoomrect);
3412 g->zoom.flags = oldflags;
3416 if (g->magnify.active)
3417 magnify_destroy(g);
3418 return TRUE;
3421 static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data)
3423 struct gtk_graph *g = (struct gtk_graph *)user_data;
3424 int step;
3426 debug(DBS_FENTRY) puts("key_press_event()");
3428 if ((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK))
3429 step = 0;
3430 else if (event->state & GDK_CONTROL_MASK)
3431 step = 1;
3432 else if (event->state & GDK_SHIFT_MASK)
3433 step = 10;
3434 else
3435 step = 100;
3437 switch (event->keyval) {
3438 case ' ':
3439 toggle_crosshairs(g);
3440 break;
3441 case 't':
3442 toggle_time_origin(g);
3443 break;
3444 case 's':
3445 toggle_seq_origin(g);
3446 break;
3447 case 'r':
3448 case GDK_Home:
3449 restore_initial_graph_view(g);
3450 break;
3451 case 'i':
3452 case '+':
3453 do_zoom_in_keyboard(g);
3454 break;
3455 case 'o':
3456 case '-':
3457 do_zoom_out_keyboard(g);
3458 break;
3459 case 'm':
3460 do_magnify_create(g);
3461 break;
3462 case 'g':
3463 do_select_segment(g);
3464 break;
3465 case '1':
3466 do_rtt_graph(g);
3467 break;
3468 case '2':
3469 do_throughput_graph(g);
3470 break;
3471 case '3':
3472 do_ts_graph_stevens(g);
3473 break;
3474 case '4':
3475 do_ts_graph_tcptrace(g);
3476 break;
3477 case '5':
3478 do_wscale_graph(g);
3479 break;
3480 case GDK_Left:
3481 do_key_motion_left(g, step);
3482 break;
3483 case GDK_Up:
3484 do_key_motion_up(g, step);
3485 break;
3486 case GDK_Right:
3487 do_key_motion_right(g, step);
3488 break;
3489 case GDK_Down:
3490 do_key_motion_down(g, step);
3491 break;
3492 case GDK_F1:
3493 callback_create_help(NULL, NULL);
3494 break;
3495 default:
3496 break;
3499 return TRUE;
3502 static gboolean key_release_event(GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data)
3504 struct gtk_graph *g = (struct gtk_graph *)user_data;
3506 debug(DBS_FENTRY) puts("key_release_event()");
3508 if ((event->keyval == GDK_Shift_L) || (event->keyval == GDK_ISO_Prev_Group)) {
3509 /* g->zoom.flags &= ~ZOOM_OUT; */
3510 gtk_toggle_button_set_active(g->zoom.widget.in_toggle, TRUE);
3512 return TRUE;
3516 static gboolean leave_notify_event(GtkWidget *widget _U_, GdkEventCrossing *event _U_, gpointer user_data)
3518 struct gtk_graph *g = (struct gtk_graph *)user_data;
3520 if (g->cross.erase_needed)
3521 cross_erase(g);
3523 return TRUE;
3526 static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data)
3528 struct gtk_graph *g = (struct gtk_graph *)user_data;
3530 graph_pixmap_display(g);
3531 if (g->cross.draw) {
3532 int x, y;
3533 get_mouse_position(widget, &x, &y, NULL);
3534 cross_draw(g, x, y);
3536 return TRUE;
3539 static void toggle_crosshairs(struct gtk_graph *g)
3541 g->cross.draw ^= 1;
3542 if (g->cross.draw) {
3543 int x, y;
3544 get_mouse_position(g->drawing_area, &x, &y, NULL);
3545 cross_draw(g, x, y);
3546 } else if (g->cross.erase_needed) {
3547 cross_erase(g);
3549 /* toggle buttons emit their "toggled" signals so don't bother doing
3550 * any real work here, it will be done in signal handlers */
3551 if (g->cross.draw)
3552 gtk_toggle_button_set_active(g->cross.on_toggle, TRUE);
3553 else
3554 gtk_toggle_button_set_active(g->cross.off_toggle, TRUE);
3557 static void toggle_time_origin(struct gtk_graph *g)
3559 switch (g->tg.type) {
3560 case GRAPH_TSEQ_STEVENS:
3561 tseq_stevens_toggle_time_origin(g);
3562 break;
3563 case GRAPH_TSEQ_TCPTRACE:
3564 tseq_tcptrace_toggle_time_origin(g);
3565 break;
3566 case GRAPH_THROUGHPUT:
3567 tput_toggle_time_origin(g);
3568 break;
3569 default:
3570 break;
3572 axis_display(g->x_axis);
3575 static void toggle_seq_origin(struct gtk_graph *g)
3577 switch (g->tg.type) {
3578 case GRAPH_TSEQ_STEVENS:
3579 tseq_stevens_toggle_seq_origin(g);
3580 axis_display(g->y_axis);
3581 break;
3582 case GRAPH_TSEQ_TCPTRACE:
3583 tseq_tcptrace_toggle_seq_origin(g);
3584 axis_display(g->y_axis);
3585 break;
3586 case GRAPH_RTT:
3587 rtt_toggle_seq_origin(g);
3588 axis_display(g->x_axis);
3589 break;
3590 default:
3591 break;
3595 static void restore_initial_graph_view(struct gtk_graph *g)
3597 g->geom.width = g->wp.width;
3598 g->geom.height = g->wp.height;
3599 g->geom.x = g->wp.x;
3600 g->geom.y = g->wp.y;
3601 graph_init_sequence(g);
3603 if (g->cross.draw) {
3604 g->cross.erase_needed = FALSE;
3609 * Stevens-style time-sequence grapH
3612 static void tseq_stevens_read_config(struct gtk_graph *g)
3614 debug(DBS_FENTRY) puts("tseq_stevens_read_config()");
3616 g->s.tseq_stevens.seq_width = 4;
3617 g->s.tseq_stevens.seq_height = 4;
3618 g->s.tseq_stevens.flags = 0;
3620 g->title = (const char ** )g_malloc(2 * sizeof(char *));
3621 g->title[0] = "Time/Sequence Graph (Stevens)";
3622 g->title[1] = NULL;
3623 g->y_axis->label = (const char ** )g_malloc(3 * sizeof(char * ));
3624 g->y_axis->label[0] = "number[B]";
3625 g->y_axis->label[1] = "Sequence";
3626 g->y_axis->label[2] = NULL;
3627 g->x_axis->label = (const char ** )g_malloc(2 * sizeof(char * ));
3628 g->x_axis->label[0] = "Time[s]";
3629 g->x_axis->label[1] = NULL;
3632 /* Used by both 'stevens' and 'tcptrace' */
3633 static void tseq_initialize(struct gtk_graph *g)
3635 debug(DBS_FENTRY) puts("tseq_initialize()");
3636 tseq_get_bounds(g);
3638 g->x_axis->min = 0;
3639 g->y_axis->min = 0;
3641 switch (g->tg.type) {
3642 case GRAPH_TSEQ_STEVENS:
3643 tseq_stevens_read_config(g);
3644 break;
3645 case GRAPH_TSEQ_TCPTRACE:
3646 tseq_tcptrace_read_config(g);
3647 break;
3648 default:
3649 break;
3654 /* Determine "bounds"
3655 * Essentially: look for lowest/highest time and seq in the list of segments
3656 * Note that for tcptrace the "(ack + window) sequence number" would normally be expected
3657 * to be the upper bound; However, just to be safe, include the data seg sequence numbers
3658 * in the comparison for tcptrace
3659 * (e.g. to handle the case of only data segments).
3662 /* ToDo: worry about handling cases such as trying to plot seq of just 1 frame */
3664 static void tseq_get_bounds(struct gtk_graph *g)
3666 struct segment *tmp;
3667 double tim;
3668 gboolean data_frame_seen = FALSE;
3669 double data_tim_low = 0;
3670 double data_tim_high = 0;
3671 guint32 data_seq_cur;
3672 guint32 data_seq_nxt;
3673 guint32 data_seq_low = 0;
3674 guint32 data_seq_high = 0;
3675 gboolean ack_frame_seen = FALSE;
3676 double ack_tim_low = 0;
3677 double ack_tim_high = 0;
3678 guint32 ack_seq_cur;
3679 guint32 ack_seq_low = 0;
3680 guint32 win_seq_cur;
3681 guint32 win_seq_high = 0;
3683 /* go thru all segments to determine "bounds" */
3684 for (tmp=g->tg.segments; tmp; tmp=tmp->next) {
3685 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
3686 g->tg.src_port, g->tg.dst_port,
3687 &tmp->ip_src, &tmp->ip_dst,
3688 tmp->th_sport, tmp->th_dport,
3689 COMPARE_CURR_DIR)) {
3691 /* "data" seg */
3692 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3693 data_seq_cur = tmp->th_seq;
3694 data_seq_nxt = data_seq_cur + tmp->th_seglen;
3695 if (! data_frame_seen) {
3696 data_tim_low = data_tim_high = tim;
3697 data_seq_low = data_seq_cur;
3698 data_seq_high = data_seq_nxt;
3699 data_frame_seen = TRUE;
3701 if (tim < data_tim_low) data_tim_low = tim;
3702 if (tim > data_tim_high) data_tim_high = tim;
3703 if (data_seq_cur < data_seq_low) data_seq_low = data_seq_cur;
3704 if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
3706 else { /* ack seg */
3707 /* skip ack processing if no ACK (e.g. in RST) */
3708 if (TCP_ACK(tmp->th_flags)) {
3709 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3710 ack_seq_cur = tmp->th_ack;
3711 win_seq_cur = ack_seq_cur + tmp->th_win;
3712 if (! ack_frame_seen) {
3713 ack_tim_low = ack_tim_high = tim;
3714 ack_seq_low = ack_seq_cur;
3715 win_seq_high = win_seq_cur;
3716 ack_frame_seen = TRUE;
3718 if (tim < ack_tim_low) ack_tim_low = tim;
3719 if (tim > ack_tim_high) ack_tim_high = tim;
3720 if (ack_seq_cur < ack_seq_low) ack_seq_low = ack_seq_cur;
3721 if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
3726 /* if 'stevens': use only data segments to determine bounds */
3727 /* if 'tcptrace': use both data and ack segments to determine bounds */
3728 switch (g->tg.type) {
3729 case GRAPH_TSEQ_STEVENS:
3730 g->bounds.x0 = data_tim_low;
3731 g->bounds.width = data_tim_high - data_tim_low;
3732 g->bounds.y0 = data_seq_low;
3733 g->bounds.height = data_seq_high - data_seq_low;
3734 break;
3735 case GRAPH_TSEQ_TCPTRACE:
3736 /* If (ack_frame_seen == false) -> use 'data' segments.
3737 * Else If (data_frame_seen == false) -> use 'ack' segments.
3738 * Else -> use both data and ack segments.
3740 g->bounds.x0 = (((data_tim_low <= ack_tim_low) && data_frame_seen) || (! ack_frame_seen))
3741 ? data_tim_low : ack_tim_low;
3742 g->bounds.width = ((((data_tim_high >= ack_tim_high) && data_frame_seen) || (! ack_frame_seen))
3743 ? data_tim_high : ack_tim_high) - g->bounds.x0;
3744 g->bounds.y0 = (((data_seq_low <= ack_seq_low) && data_frame_seen) || (! ack_frame_seen))
3745 ? data_seq_low : ack_seq_low;
3746 g->bounds.height = ((((data_seq_high >= win_seq_high) && data_frame_seen) || (! ack_frame_seen))
3747 ? data_seq_high : win_seq_high) - g->bounds.y0;
3748 break;
3749 default:
3750 break;
3753 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3754 g->zoom.y = (g->geom.height -1) / g->bounds.height;
3758 static void tseq_stevens_make_elmtlist(struct gtk_graph *g)
3760 struct segment *tmp;
3761 struct element *elements, *e;
3762 double xx0 = g->bounds.x0, yy0 = g->bounds.y0;
3763 guint32 seq_base = (guint32) yy0;
3764 guint32 seq_cur;
3766 debug(DBS_FENTRY) puts("tseq_stevens_make_elmtlist()");
3767 if (g->elists->elements == NULL) {
3768 int n = 1 + get_num_dsegs(&g->tg);
3769 e = elements = (struct element * )g_malloc(n*sizeof(struct element));
3770 } else
3771 e = elements = g->elists->elements;
3773 for (tmp = g->tg.segments; tmp; tmp = tmp->next) {
3774 double secs, seqno;
3776 if (!compare_headers(&g->tg.src_address, &g->tg.dst_address,
3777 g->tg.src_port, g->tg.dst_port,
3778 &tmp->ip_src, &tmp->ip_dst,
3779 tmp->th_sport, tmp->th_dport,
3780 COMPARE_CURR_DIR)) {
3781 continue;
3783 /* data seg */
3784 seq_cur = tmp->th_seq - seq_base;
3785 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - xx0);
3786 seqno = g->zoom.y * seq_cur;
3788 e->type = ELMT_ELLIPSE;
3789 e->parent = tmp;
3790 e->p.ellipse.dim.width = g->s.tseq_stevens.seq_width;
3791 e->p.ellipse.dim.height = g->s.tseq_stevens.seq_height;
3792 e->p.ellipse.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3793 e->p.ellipse.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3794 e++;
3796 e->type = ELMT_NONE;
3797 g->elists->elements = elements;
3800 static void tseq_stevens_toggle_seq_origin(struct gtk_graph *g)
3802 g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3804 if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3805 g->y_axis->min = g->bounds.y0;
3806 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
3807 g->y_axis->min = 0;
3810 static void tseq_stevens_toggle_time_origin(struct gtk_graph *g)
3812 g->s.tseq_stevens.flags ^= TIME_ORIGIN;
3814 if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
3815 g->x_axis->min = g->bounds.x0;
3816 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
3817 g->x_axis->min = 0;
3821 * tcptrace-style time-sequence graph
3824 static void tseq_tcptrace_read_config(struct gtk_graph *g)
3826 /* Black */
3827 g->s.tseq_tcptrace.seq_color.red = (double)0 / 65535.0;
3828 g->s.tseq_tcptrace.seq_color.green = (double)0 / 65535.0;
3829 g->s.tseq_tcptrace.seq_color.blue = (double)0 / 65535.0;
3830 g->s.tseq_tcptrace.seq_color.alpha = 1.0;
3832 /* LightSlateGray */
3833 g->s.tseq_tcptrace.ack_color[0].red = (double)0x7777 / 65535.0;
3834 g->s.tseq_tcptrace.ack_color[0].green = (double)0x8888 / 65535.0;
3835 g->s.tseq_tcptrace.ack_color[0].blue = (double)0x9999 / 65535.0;
3836 g->s.tseq_tcptrace.ack_color[0].alpha = 1.0;
3838 /* LightGray */
3839 g->s.tseq_tcptrace.ack_color[1].red = (double)0xd3d3 / 65535.0;
3840 g->s.tseq_tcptrace.ack_color[1].green = (double)0xd3d3 / 65535.0;
3841 g->s.tseq_tcptrace.ack_color[1].blue = (double)0xd3d3 / 65535.0;
3842 g->s.tseq_tcptrace.ack_color[1].alpha = 1.0;
3844 /* Light blue */
3845 g->s.tseq_tcptrace.sack_color[0].red = (double)0x0 / 65535.0;
3846 g->s.tseq_tcptrace.sack_color[0].green = (double)0x0 / 65535.0;
3847 g->s.tseq_tcptrace.sack_color[0].blue = (double)0xffff / 65535.0;
3848 g->s.tseq_tcptrace.sack_color[0].alpha = 1.0;
3850 /* Darker blue */
3851 g->s.tseq_tcptrace.sack_color[1].red = (double)0x0 / 65535.0;
3852 g->s.tseq_tcptrace.sack_color[1].green = (double)0x0 / 65535.0;
3853 g->s.tseq_tcptrace.sack_color[1].blue = (double)0x9888 / 65535.0;
3854 g->s.tseq_tcptrace.sack_color[1].alpha = 1.0;
3856 g->s.tseq_tcptrace.flags = 0;
3858 /* Allocate first list, but not elements */
3859 g->elists->next = (struct element_list * )
3860 g_malloc(sizeof(struct element_list));
3861 g->elists->next->next = NULL;
3862 g->elists->next->elements = NULL;
3864 g->title = (const char ** )g_malloc(2 * sizeof(char *));
3865 g->title[0] = "Time/Sequence Graph (tcptrace)";
3866 g->title[1] = NULL;
3867 g->y_axis->label = (const char ** )g_malloc(3 * sizeof(char * ));
3868 g->y_axis->label[0] = "number[B]";
3869 g->y_axis->label[1] = "Sequence";
3870 g->y_axis->label[2] = NULL;
3871 g->x_axis->label = (const char ** )g_malloc(2 * sizeof(char * ));
3872 g->x_axis->label[0] = "Time[s]";
3873 g->x_axis->label[1] = NULL;
3876 static void tseq_tcptrace_make_elmtlist(struct gtk_graph *g)
3878 struct segment *tmp;
3879 struct element *elements0, *e0; /* list of elmts with prio 0 */
3880 struct element *elements1, *e1; /* list of elmts with prio 1 */
3881 double xx0, yy0;
3882 double p_t = 0; /* ackno, window and time of previous segment */
3883 double p_ackno = 0, p_win = 0;
3884 gboolean ack_seen = FALSE;
3885 int toggle = 0;
3886 guint32 seq_base;
3887 guint32 seq_cur;
3888 int num_sack_ranges = 0;
3890 debug(DBS_FENTRY) puts("tseq_tcptrace_make_elmtlist()");
3892 if (g->elists->elements == NULL ) {
3893 /* 3 elements per data segment */
3894 int n = 1 + 3*get_num_dsegs(&g->tg);
3895 e0 = elements0 = (struct element * )g_malloc(n * sizeof(struct element));
3896 } else {
3897 /* Existing array */
3898 e0 = elements0 = g->elists->elements;
3901 if (g->elists->next->elements == NULL) {
3902 /* 4 elements per ACK, but only one for each SACK range */
3903 int n = 1 + 4*get_num_acks(&g->tg, &num_sack_ranges);
3904 n += num_sack_ranges;
3905 e1 = elements1 = (struct element * )g_malloc(n * sizeof(struct element));
3906 } else {
3907 /* Existing array */
3908 e1 = elements1 = g->elists->next->elements;
3911 xx0 = g->bounds.x0;
3912 yy0 = g->bounds.y0;
3913 seq_base = (guint32) yy0;
3915 for (tmp = g->tg.segments; tmp; tmp = tmp->next) {
3916 double secs, data;
3917 double x;
3919 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3920 x = secs - xx0;
3921 x *= g->zoom.x;
3922 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
3923 g->tg.src_port, g->tg.dst_port,
3924 &tmp->ip_src, &tmp->ip_dst,
3925 tmp->th_sport, tmp->th_dport,
3926 COMPARE_CURR_DIR)) {
3927 /* forward direction (data) -> we need seqno and amount of data */
3928 double yy1, yy2;
3930 seq_cur = tmp->th_seq - seq_base;
3931 if (TCP_SYN(tmp->th_flags) || TCP_FIN(tmp->th_flags))
3932 data = 1;
3933 else
3934 data = tmp->th_seglen;
3936 yy1 = g->zoom.y * (seq_cur);
3937 yy2 = g->zoom.y * (seq_cur + data);
3938 e0->type = ELMT_LINE;
3939 e0->parent = tmp;
3940 /* Set the drawing color */
3941 e0->elment_color_p = &g->s.tseq_tcptrace.seq_color;
3942 e0->p.line.dim.x1 = e0->p.line.dim.x2 = x;
3943 e0->p.line.dim.y1 = yy1;
3944 e0->p.line.dim.y2 = yy2;
3945 e0++;
3946 e0->type = ELMT_LINE;
3947 e0->parent = tmp;
3948 /* Set the drawing color */
3949 e0->elment_color_p = &g->s.tseq_tcptrace.seq_color;
3950 e0->p.line.dim.x1 = x - 1;
3951 e0->p.line.dim.x2 = x + 1;
3952 e0->p.line.dim.y1 = e0->p.line.dim.y2 = yy1;
3953 e0++;
3954 e0->type = ELMT_LINE;
3955 e0->parent = tmp;
3956 /* Set the drawing color */
3957 e0->elment_color_p = &g->s.tseq_tcptrace.seq_color;
3958 e0->p.line.dim.x1 = x + 1;
3959 e0->p.line.dim.x2 = x - 1;
3960 e0->p.line.dim.y1 = e0->p.line.dim.y2 = yy2;
3961 e0++;
3962 } else {
3963 double ackno, win;
3964 if (! TCP_ACK(tmp->th_flags))
3965 /* SYN's and RST's do not necessarily have ACK's*/
3966 continue;
3967 /* backward direction -> we need ackno and window */
3968 seq_cur = tmp->th_ack - seq_base;
3969 ackno = seq_cur * g->zoom.y;
3970 win = tmp->th_win * g->zoom.y;
3972 /* ack line */
3973 if (ack_seen == TRUE) { /* don't plot the first ack */
3975 /* Horizonal: time of previous ACK to now (at new ACK) */
3976 e1->type = ELMT_LINE;
3977 e1->parent = tmp;
3978 /* Set the drawing color */
3979 e1->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
3980 e1->p.line.dim.x1 = p_t;
3981 e1->p.line.dim.y1 = p_ackno;
3982 e1->p.line.dim.x2 = x;
3983 e1->p.line.dim.y2 = p_ackno;
3984 e1++;
3986 /* Vertical: from previous ACKNO to current one (at current time) */
3987 e1->type = ELMT_LINE;
3988 e1->parent = tmp;
3989 /* Set the drawing color */
3990 e1->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
3991 e1->p.line.dim.x1 = x;
3992 e1->p.line.dim.y1 = p_ackno;
3993 e1->p.line.dim.x2 = x;
3994 e1->p.line.dim.y2 = ((ackno != p_ackno) || (ackno < 4)) ? ackno : ackno - 4;
3995 e1++;
3997 /* Horizontal: window line */
3998 e1->type = ELMT_LINE;
3999 e1->parent = tmp;
4000 /* Set the drawing color */
4001 e1->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4002 e1->p.line.dim.x1 = p_t;
4003 e1->p.line.dim.y1 = p_win + p_ackno;
4004 e1->p.line.dim.x2 = x;
4005 e1->p.line.dim.y2 = p_win + p_ackno;
4006 e1++;
4008 /* Vertical: old window to new window */
4009 e1->type = ELMT_LINE;
4010 e1->parent = tmp;
4011 /* Set the drawing color */
4012 e1->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4013 e1->p.line.dim.x1 = x;
4014 e1->p.line.dim.y1 = p_win + p_ackno;
4015 e1->p.line.dim.x2 = x;
4016 e1->p.line.dim.y2 = win + ackno;
4017 e1++;
4019 /* Toggle color to use for ACKs... */
4020 toggle = 1^toggle;
4022 ack_seen = TRUE;
4023 p_ackno = ackno;
4024 p_win = win;
4025 p_t = x;
4027 /* Now any SACK entries */
4028 if (tmp->num_sack_ranges) {
4029 int n;
4031 for (n=0; n < tmp->num_sack_ranges; n++) {
4032 double left_edge = (tmp->sack_left_edge[n] - seq_base) * g->zoom.y;
4033 double right_edge = (tmp->sack_right_edge[n] - seq_base) * g->zoom.y;
4035 /* Vertical: just show range of SACK.
4036 Have experimented with sorting ranges and showing in red regions
4037 between SACKs, but when TCP is limited by option bytes and needs to
4038 miss out ranges, this can be pretty confusing as we end up apparently
4039 NACKing what has been received... */
4040 e1->type = ELMT_LINE;
4041 e1->parent = tmp;
4042 /* Set the drawing color. First range is significant, so use
4043 separate colour */
4044 e1->elment_color_p = (n==0) ? &g->s.tseq_tcptrace.sack_color[0] :
4045 &g->s.tseq_tcptrace.sack_color[1];
4046 e1->p.line.dim.x1 = x;
4047 e1->p.line.dim.y1 = right_edge;
4048 e1->p.line.dim.x2 = x;
4049 e1->p.line.dim.y2 = left_edge;
4050 e1++;
4056 /* Terminate both lists */
4057 e0->type = ELMT_NONE;
4058 e1->type = ELMT_NONE;
4060 g->elists->elements = elements0;
4061 g->elists->next->elements = elements1;
4062 g->elists->next->next = NULL;
4065 static void tseq_tcptrace_toggle_seq_origin(struct gtk_graph *g)
4067 g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
4069 if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
4070 g->y_axis->min = g->bounds.y0;
4071 else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
4072 g->y_axis->min = 0;
4075 static void tseq_tcptrace_toggle_time_origin(struct gtk_graph *g)
4077 g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
4079 if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
4080 g->x_axis->min = g->bounds.x0;
4081 else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4082 g->x_axis->min = 0;
4086 * throughput graph
4089 static void tput_make_elmtlist(struct gtk_graph *g)
4091 struct segment *tmp, *oldest;
4092 struct element *elements, *e;
4093 int i, sum = 0;
4094 double dtime, tput;
4095 int num_sack_ranges;
4097 if (g->elists->elements == NULL) {
4098 int n = 1 + get_num_dsegs(&g->tg) + get_num_acks(&g->tg, &num_sack_ranges);
4099 e = elements = (struct element * )g_malloc(n * sizeof(struct element));
4100 } else
4101 e = elements = g->elists->elements;
4103 for (oldest=g->tg.segments, tmp=g->tg.segments->next, i=1; tmp; tmp=tmp->next, i++) {
4104 double time_val = tmp->rel_secs + tmp->rel_usecs/1000000.0;
4105 if (i>g->s.tput.ma_size) {
4106 oldest = oldest->next;
4107 sum -= oldest->th_seglen;
4109 dtime = time_val - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
4110 sum += tmp->th_seglen;
4111 tput = sum / dtime;
4112 /* debug(DBS_TPUT_ELMTS) printf("tput=%f\n", tput); */
4114 e->type = ELMT_ELLIPSE;
4115 e->parent = tmp;
4116 e->p.ellipse.dim.width = g->s.tput.width;
4117 e->p.ellipse.dim.height = g->s.tput.height;
4118 e->p.ellipse.dim.x = g->zoom.x*(time_val - g->bounds.x0) - g->s.tput.width/2.0;
4119 e->p.ellipse.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
4120 e++;
4122 e->type = ELMT_NONE;
4123 g->elists->elements = elements;
4126 /* Purpose of <graph_type>_initialize functions:
4127 * - find maximum and minimum for both axes
4128 * - call setup routine for style struct */
4129 static void tput_initialize(struct gtk_graph *g)
4131 struct segment *tmp, *oldest = g->tg.segments/*, *last*/;
4132 int i, sum = oldest->th_seglen;
4133 double dtime, tput, tputmax = 0;
4134 double t, t0, tmax = 0, yy0, ymax;
4136 debug(DBS_FENTRY) puts("tput_initialize()");
4138 tput_read_config(g);
4140 for (tmp=g->tg.segments->next, i=1; tmp; tmp=tmp->next, i++) {
4141 if (i > g->s.tput.ma_size) {
4142 oldest = oldest->next;
4143 sum -= oldest->th_seglen;
4145 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
4146 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
4147 sum += tmp->th_seglen;
4148 tput = sum / dtime;
4149 debug(DBS_TPUT_ELMTS) printf("tput=%f\n", tput);
4150 if (tput > tputmax)
4151 tputmax = tput;
4152 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4153 if (t > tmax)
4154 tmax = t;
4157 t0 = g->tg.segments->rel_secs + g->tg.segments->rel_usecs / 1000000.0;
4158 yy0 = 0;
4159 ymax = tputmax;
4161 g->bounds.x0 = t0;
4162 g->bounds.y0 = yy0;
4163 g->bounds.width = tmax - t0;
4164 g->bounds.height = ymax - yy0;
4165 g->zoom.x = (g->geom.width - 1) / g->bounds.width;
4166 g->zoom.y = (g->geom.height -1) / g->bounds.height;
4169 static void tput_read_config(struct gtk_graph *g)
4171 debug(DBS_FENTRY) puts("tput_read_config()");
4173 g->s.tput.width = 4;
4174 g->s.tput.height = 4;
4175 g->s.tput.ma_size = 20;
4177 g->title = (const char ** )g_malloc(2 * sizeof(char *));
4178 g->title[0] = "Throughput (20 segment MA)";
4179 g->title[1] = NULL;
4180 g->y_axis->label = (const char ** )g_malloc(3 * sizeof(char * ));
4181 g->y_axis->label[0] = "[B/s]";
4182 g->y_axis->label[1] = "Throughput";
4183 g->y_axis->label[2] = NULL;
4184 g->x_axis->label = (const char ** )g_malloc(2 * sizeof(char * ));
4185 g->x_axis->label[0] = "Time[s]";
4186 g->x_axis->label[1] = NULL;
4187 g->s.tput.flags = 0;
4190 static void tput_toggle_time_origin(struct gtk_graph *g)
4192 g->s.tput.flags ^= TIME_ORIGIN;
4194 if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
4195 g->x_axis->min = g->bounds.x0;
4196 else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4197 g->x_axis->min = 0;
4200 /* RTT graph */
4202 static void rtt_read_config(struct gtk_graph *g)
4204 debug(DBS_FENTRY) puts("rtt_read_config()");
4206 g->s.rtt.width = 4;
4207 g->s.rtt.height = 4;
4208 g->s.rtt.flags = 0;
4210 g->title = (const char ** )g_malloc(2 * sizeof(char *));
4211 g->title[0] = "Round Trip Time Graph";
4212 g->title[1] = NULL;
4213 g->y_axis->label = (const char ** )g_malloc(3 * sizeof(char * ));
4214 g->y_axis->label[0] = "RTT [s]";
4215 g->y_axis->label[1] = NULL;
4216 g->x_axis->label = (const char ** )g_malloc(2 * sizeof(char * ));
4217 g->x_axis->label[0] = "Sequence Number[B]";
4218 g->x_axis->label[1] = NULL;
4221 static void rtt_initialize(struct gtk_graph *g)
4223 struct segment *tmp, *first = NULL;
4224 struct unack *unack = NULL, *u;
4225 double rttmax = 0;
4226 double xx0, yy0, ymax;
4227 guint32 xmax = 0;
4228 guint32 seq_base = 0;
4230 debug(DBS_FENTRY) puts("rtt_initialize()");
4232 rtt_read_config(g);
4234 for (tmp=g->tg.segments; tmp; tmp=tmp->next) {
4235 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
4236 g->tg.src_port, g->tg.dst_port,
4237 &tmp->ip_src, &tmp->ip_dst,
4238 tmp->th_sport, tmp->th_dport,
4239 COMPARE_CURR_DIR)) {
4240 guint32 seqno = tmp->th_seq;
4242 if (!first) {
4243 first = tmp;
4244 seq_base = seqno;
4246 seqno -= seq_base;
4247 if (tmp->th_seglen && !rtt_is_retrans(unack, seqno)) {
4248 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4249 u = rtt_get_new_unack(time_val, seqno);
4250 if (!u) return;
4251 rtt_put_unack_on_list(&unack, u);
4254 if (seqno + tmp->th_seglen > xmax)
4255 xmax = seqno + tmp->th_seglen;
4256 } else if (first) {
4257 guint32 ackno = tmp->th_ack -seq_base;
4258 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4259 struct unack *v;
4261 for (u=unack; u; u=v) {
4262 if (ackno > u->seqno) {
4263 double rtt = time_val - u->time;
4264 if (rtt > rttmax)
4265 rttmax = rtt;
4266 v = u->next;
4267 rtt_delete_unack_from_list(&unack, u);
4268 } else
4269 v = u->next;
4274 xx0 = seq_base;
4275 yy0 = 0;
4276 ymax = rttmax;
4278 g->bounds.x0 = xx0;
4279 g->bounds.y0 = yy0;
4280 g->bounds.width = xmax;
4281 g->bounds.height = ymax - yy0;
4282 g->zoom.x = g->geom.width / g->bounds.width;
4283 g->zoom.y = g->geom.height / g->bounds.height;
4286 static void rtt_make_elmtlist(struct gtk_graph *g)
4288 struct segment *tmp;
4289 struct unack *unack = NULL, *u;
4290 struct element *elements, *e;
4291 guint32 seq_base = (guint32) g->bounds.x0;
4293 debug(DBS_FENTRY) puts("rtt_make_elmtlist()");
4295 if (g->elists->elements == NULL) {
4296 int n = 1 + get_num_dsegs(&g->tg);
4297 e = elements = (struct element * )g_malloc(n * sizeof(struct element));
4298 } else {
4299 e = elements = g->elists->elements;
4302 for (tmp=g->tg.segments; tmp; tmp=tmp->next) {
4303 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
4304 g->tg.src_port, g->tg.dst_port,
4305 &tmp->ip_src, &tmp->ip_dst,
4306 tmp->th_sport, tmp->th_dport,
4307 COMPARE_CURR_DIR)) {
4308 guint32 seqno = tmp->th_seq -seq_base;
4310 if (tmp->th_seglen && !rtt_is_retrans(unack, seqno)) {
4311 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4312 u = rtt_get_new_unack(time_val, seqno);
4313 if (!u) return;
4314 rtt_put_unack_on_list(&unack, u);
4316 } else {
4317 guint32 ackno = tmp->th_ack -seq_base;
4318 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4319 struct unack *v;
4321 for (u=unack; u; u=v) {
4322 if (ackno > u->seqno) {
4323 double rtt = time_val - u->time;
4325 e->type = ELMT_ELLIPSE;
4326 e->parent = tmp;
4327 e->p.ellipse.dim.width = g->s.rtt.width;
4328 e->p.ellipse.dim.height = g->s.rtt.height;
4329 e->p.ellipse.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
4330 e->p.ellipse.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
4331 e++;
4333 v = u->next;
4334 rtt_delete_unack_from_list(&unack, u);
4335 } else
4336 v = u->next;
4340 e->type = ELMT_NONE;
4341 g->elists->elements = elements;
4344 static void rtt_toggle_seq_origin(struct gtk_graph *g)
4346 g->s.rtt.flags ^= SEQ_ORIGIN;
4348 if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
4349 g->x_axis->min = g->bounds.x0;
4350 else
4351 g->x_axis->min = 0;
4354 /* WSCALE Graph */
4356 static void wscale_read_config(struct gtk_graph *g)
4358 debug(DBS_FENTRY) puts("wscale_read_config()");
4360 g->s.wscale.win_width = 4;
4361 g->s.wscale.win_height = 4;
4362 g->s.wscale.flags = 0;
4364 g->title = (const char ** )g_malloc(2 * sizeof(char *));
4365 g->title[0] = "Window Scaling Graph";
4366 g->title[1] = NULL;
4367 g->y_axis->label = (const char ** )g_malloc(3 * sizeof(char * ));
4368 g->y_axis->label[0] = "[bytes]";
4369 g->y_axis->label[1] = "Windowsize";
4370 g->y_axis->label[2] = NULL;
4371 g->x_axis->label = (const char ** )g_malloc(2 * sizeof(char * ));
4372 g->x_axis->label[0] = "Time [s]";
4373 g->x_axis->label[1] = NULL;
4377 (1) Find maximum and minimum values for Window-Size(scaled) and seconds
4378 (2) call function to define window related values
4380 static void wscale_initialize(struct gtk_graph *g)
4383 struct segment *segm = NULL;
4384 guint32 wsize_max = 0;
4385 guint32 wsize_min = 0;
4386 gdouble sec_max = 0.0;
4387 gdouble sec_base = -1.0;
4389 wscale_read_config(g);
4391 debug(DBS_FENTRY) puts("wscale_initialize()");
4393 for (segm=g->tg.segments; segm; segm=segm->next) {
4394 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
4395 g->tg.src_port, g->tg.dst_port,
4396 &segm->ip_src, &segm->ip_dst,
4397 segm->th_sport, segm->th_dport,
4398 COMPARE_CURR_DIR))
4400 gdouble sec = segm->rel_secs + ( segm->rel_usecs / 1000000.0 );
4401 guint16 flags = segm->th_flags;
4402 guint32 wsize = segm->th_win;
4404 /* only data segments */
4405 if ( (flags & (TH_SYN|TH_RST)) == 0 )
4406 if ( wsize > wsize_max )
4407 wsize_max = wsize;
4409 /* remind time of first probe */
4410 if ((sec_base < 0) && (sec > 0))
4411 sec_base = sec;
4413 if ( sec_max < sec )
4414 sec_max = sec;
4420 g->bounds.x0 = 0;
4421 g->bounds.y0 = wsize_min;
4422 g->bounds.width = sec_max - sec_base + 5;
4423 g->bounds.height = wsize_max + 5;
4424 g->zoom.x = g->geom.width / g->bounds.width;
4425 g->zoom.y = g->geom.height / g->bounds.height;
4430 (1) Fill & allocate memory for segments times elements,
4432 static void wscale_make_elmtlist(struct gtk_graph *g)
4434 struct segment *segm = NULL;
4435 struct element *elements = NULL;
4436 struct element *e = NULL;
4437 gdouble sec_base = -1.0;
4439 debug(DBS_FENTRY) puts("wscale_make_elmtlist()");
4441 /* Allocate memory for elements if not already done */
4442 if (g->elists->elements == NULL)
4444 int n = 1 + get_num_dsegs(&g->tg);
4445 e = elements = (struct element*)g_malloc(n*sizeof(struct element));
4447 else
4448 e = elements = g->elists->elements;
4451 for ( segm=g->tg.segments; segm; segm=segm->next ) {
4452 if (compare_headers(&g->tg.src_address, &g->tg.dst_address,
4453 g->tg.src_port, g->tg.dst_port,
4454 &segm->ip_src, &segm->ip_dst,
4455 segm->th_sport, segm->th_dport,
4456 COMPARE_CURR_DIR))
4458 gdouble sec = segm->rel_secs + (segm->rel_usecs / 1000000.0);
4459 guint16 flags = segm->th_flags;
4460 guint32 wsize = segm->th_win;
4462 /* remind time of first probe */
4463 if ((sec_base < 0) && (sec > 0))
4464 sec_base = sec;
4466 /* only data or ack segments */
4467 if ( (flags & (TH_SYN|TH_RST)) == 0 )
4469 e->type = ELMT_ELLIPSE;
4470 e->parent = segm;
4471 e->p.ellipse.dim.width = g->s.wscale.win_width;
4472 e->p.ellipse.dim.height = g->s.wscale.win_height;
4473 e->p.ellipse.dim.x = g->zoom.x * (sec - sec_base) - g->s.wscale.win_width / 2.0;
4474 e->p.ellipse.dim.y = g->zoom.y * wsize - g->s.wscale.win_height / 2.0;
4475 e++;
4479 /* finished populating element list */
4480 e->type = ELMT_NONE;
4481 g->elists->elements = elements;
4484 #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
4485 /* Starting VS2103, rint already defined in math.h. No need to redefine */
4486 /* replacement of Unix rint() for Windows */
4487 static int rint(double x)
4489 char *buf;
4490 int i, dec, sig;
4492 buf = _fcvt(x, 0, &dec, &sig);
4493 i = atoi(buf);
4494 if (sig == 1) {
4495 i = i * -1;
4497 return(i);
4499 #endif