2 * TCP graph drawing code
3 * By Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
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.
35 #include <gdk/gdkkeysyms.h>
36 #if GTK_CHECK_VERSION(3,0,0)
37 # include <gdk/gdkkeysyms-compat.h>
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 )
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
78 double x
, y
, width
, height
;
82 double x1
, y1
, x2
, y2
;
86 int x
, y
, width
, height
;
113 struct ellipse_params
{
119 GdkRGBA
*elment_color_p
;
120 struct segment
*parent
;
122 struct ellipse_params ellipse
;
123 struct rect_params rect
;
124 struct line_params line
;
128 struct element_list
{
129 struct element_list
*next
;
130 struct element
*elements
;
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];
139 GdkPixmap
*pixmap
[2];
142 #define AXIS_ORIENTATION 1 << 0
144 /* dim and orig (relative to origin of window) of axis' pixmap */
146 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
149 gdouble major
, minor
; /* major and minor ticks */
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
{
160 GdkRGBA ack_color
[2];
161 GdkRGBA sack_color
[2];
165 struct style_tseq_stevens
{
182 struct style_wscale
{
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
201 int draw
; /* indicates whether we should draw cross at all */
203 GtkToggleButton
*on_toggle
;
204 GtkToggleButton
*off_toggle
;
208 double x0
, y0
, width
, height
;
217 double step_x
, step_y
;
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)
225 /* unfortunately, we need them both because gtk_toggle_button_set_active()
226 * with second argument FALSE doesn't do anything, somehow */
228 GtkToggleButton
*in_toggle
;
229 GtkToggleButton
*out_toggle
;
232 GtkSpinButton
*h_step
;
233 GtkSpinButton
*v_step
;
245 struct ipoint offset
;
249 #define MAGZOOMS_SAME (1U << 0)
250 #define MAGZOOMS_SAME_RATIO (1U << 1)
251 #define MAGZOOMS_IGNORE (1U << 31)
254 GtkSpinButton
*h_zoom
, *v_zoom
;
259 #define GRAPH_DESTROYED (1 << 0)
260 #define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
262 GtkWidget
*toplevel
; /* keypress handler needs this */
263 GtkWidget
*drawing_area
;
264 GtkWidget
*text
; /* text widget for seg list - probably
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];
272 GdkPixmap
*title_pixmap
;
273 GdkPixmap
*pixmap
[2];
275 int displayed
; /* which of both pixmaps is on screen right now */
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
;
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 */
296 /* viewport (=graph window area which is reserved for graph itself), its
297 * size and position relative to origin of the graph window */
300 /* whether and where the graph has been 'grabbed' and may now be moved */
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.*/
309 gboolean zoomrect_erase_needed
;
310 struct magnify magnify
;
311 struct axis
*x_axis
, *y_axis
;
315 struct element_list
*elists
; /* element lists */
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
;
323 /* This allows keyboard to set the radio button */
325 GtkToggleButton
*graph_rtt
, *graph_tput
, *graph_tseqstevens
, *graph_tseqttrace
;
326 GtkToggleButton
*graph_wscale
;
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
* );
348 static void create_text_widget(struct gtk_graph
* );
349 static void display_text(struct gtk_graph
* );
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
);
435 static gboolean
expose_event(GtkWidget
* , GdkEventExpose
* , gpointer
);
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 */
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"
487 #ifdef ORIGINAL_WIN32_BUTTONS
488 " <Ctrl>-Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
490 " Left Mouse Button zooms in (towards area under mouse pointer)\n"
491 " <Shift>-Left Mouse Button zooms out\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"
499 " Middle Mouse Button zooms in (towards area under cursor)\n"
500 " <Shift>-Middle Mouse Button zooms out\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"
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"
513 " <Space bar> toggles crosshairs on/off\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"
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"
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"
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"
539 static void debug_coord(struct gtk_graph
*g
, const char *c
)
541 static guint count
= 0;
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");
558 static void set_busy_cursor(GdkWindow
*w
)
562 cursor
= gdk_cursor_new(GDK_WATCH
);
563 gdk_window_set_cursor(w
, cursor
);
565 #if GTK_CHECK_VERSION(3,0,0)
566 g_object_unref(cursor
);
568 gdk_cursor_unref(cursor
);
572 static void unset_busy_cursor(GdkWindow
*w
, gboolean cross
)
577 cursor
= gdk_cursor_new(GDK_CROSSHAIR
);
578 gdk_window_set_cursor(w
, cursor
);
580 #if GTK_CHECK_VERSION(3,0,0)
581 g_object_unref(cursor
);
583 gdk_cursor_unref(cursor
);
586 gdk_window_set_cursor(w
, NULL
);
591 void tcp_graph_cb(GtkAction
*action
, gpointer user_data _U_
)
593 struct segment current
;
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
;
613 debug(DBS_FENTRY
) puts("tcp_graph_cb()");
615 if (!select_tcpip_session(&cfile
, ¤t
)) {
619 if (! (g
= graph_new()))
623 graph_initialize_values(g
);
625 g
->tg
.type
= graph_type
;
627 graph_segment_list_get(&cfile
, &g
->tg
, FALSE
);
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
)
639 if (!(g
= graph_new())) {
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
);
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
;
682 char window_title
[WINDOW_TITLE_LENGTH
];
683 GtkAllocation widget_alloc
;
685 /* Prep. to include the controls in the graph window */
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",
697 ep_address_to_str(&g
->tg
.src_address
),
699 ep_address_to_str(&g
->tg
.dst_address
),
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
);
718 g_signal_connect(g
->drawing_area
, "expose_event", G_CALLBACK(expose_event
), g
);
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
,
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
);
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
);
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
,
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
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()? !!!
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
;
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);
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
;
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
)),
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()" */
970 callback_delete_event(GtkWidget
*widget _U_
, GdkEvent
*event _U_
,
973 callback_close(NULL
, data
);
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
;
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
),
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
);
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
);
1033 gdk_window_get_pointer(gtk_widget_get_window(widget
), pointer_x
, pointer_y
, mask
);
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
;
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
;
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
;
1165 double *zoom_this
, *zoom_other
;
1166 GtkSpinButton
*widget_this
, *widget_other
;
1169 direction
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(spin
), "direction"));
1170 value
= gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin
));
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
;
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
;
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) {
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) {
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
;
1213 g
->zoom
.flags
&= ~flag
;
1216 static void update_zoom_spins(struct gtk_graph
*g
)
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
);
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
;
1361 double *zoom_this
, *zoom_other
;
1362 GtkSpinButton
*widget_this
, *widget_other
;
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
;
1371 direction
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(spin
), "direction"));
1372 value
= gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin
));
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
;
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
;
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) {
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) {
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
;
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
)),
1431 zoom_lock_v
= gtk_radio_button_new_with_label(
1432 gtk_radio_button_get_group(GTK_RADIO_BUTTON(zoom_lock_none
)),
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
;
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
;
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
;
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
))) {
1500 g
->cross
.draw
= TRUE
;
1501 get_mouse_position(g
->drawing_area
, &x
, &y
, 0);
1502 cross_draw(g
, x
, y
);
1504 g
->cross
.draw
= FALSE
;
1505 if (g
->cross
.erase_needed
) {
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
)),
1528 graph_wscale
= gtk_radio_button_new_with_label(
1529 gtk_radio_button_get_group(GTK_RADIO_BUTTON(graph_tput
)),
1532 switch (g
->tg
.type
) {
1533 case GRAPH_TSEQ_STEVENS
:
1534 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens
), TRUE
);
1536 case GRAPH_TSEQ_TCPTRACE
:
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqttrace
), TRUE
);
1539 case GRAPH_THROUGHPUT
:
1540 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tput
), TRUE
);
1543 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_rtt
), TRUE
);
1546 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_wscale
), TRUE
);
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
);
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
)))
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
));
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
;
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
;
1651 g
->y_axis
->s
.y
= TITLEBAR_HEIGHT
;
1652 g
->y_axis
->s
.width
= VAXIS_INIT_WIDTH
;
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
;
1664 /* g->zoom.x = g->zoom.y = 1.0; */
1665 g
->zoom
.step_x
= g
->zoom
.step_y
= 1.2;
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
);
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
:
1709 case GRAPH_THROUGHPUT
:
1716 wscale_initialize(g
);
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]);
1744 g_object_unref(g
->pixmap
[0]);
1745 g_object_unref(g
->pixmap
[1]);
1746 #endif /* GTK_CHECK_VERSION(2,22,0) */
1749 g_free((gpointer
)(g
->title
));
1750 graph_segment_list_free(&g
->tg
);
1751 graph_element_lists_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
);
1769 case GRAPH_TSEQ_TCPTRACE
:
1770 tseq_tcptrace_make_elmtlist(g
);
1772 case GRAPH_THROUGHPUT
:
1773 tput_make_elmtlist(g
);
1776 rtt_make_elmtlist(g
);
1779 wscale_make_elmtlist(g
);
1782 printf("graph_element_lists_make: unknown graph type: %d\n", g
->tg
.type
);
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
;
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
,
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);
1821 static void graph_title_pixmap_draw(struct gtk_graph
*g
)
1826 #if GTK_CHECK_VERSION(2,22,0)
1827 cr
= cairo_create(g
->title_surface
);
1829 cr
= gdk_cairo_create(g
->title_pixmap
);
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
);
1834 cairo_set_source_rgb(cr
, 0, 0, 0); /* set text color */
1836 for (i
=0; g
->title
[i
]; i
++) {
1838 PangoLayout
*layout
;
1839 layout
= gtk_widget_create_pango_layout(g
->drawing_area
,
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
));
1849 static void graph_title_pixmap_display(struct gtk_graph
*g
)
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);
1857 gdk_cairo_set_source_pixmap(cr
, g
->title_pixmap
, g
->wp
.x
, 0);
1859 cairo_rectangle(cr
, g
->wp
.x
, 0, g
->x_axis
->p
.width
, g
->wp
.y
);
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
,
1883 g
->surface
[1] = gdk_window_create_similar_surface(gtk_widget_get_window(g
->drawing_area
),
1884 CAIRO_CONTENT_COLOR
,
1891 g_object_unref(g
->pixmap
[0]);
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);
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
)
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
);
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
);
1926 if (g
->cross
.erase_needed
) {
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
;
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
]);
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
);
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
++) {
1965 current_line_color
= NULL
;
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
;
1975 /* Changing colour */
1976 current_line_color
= color_to_set
= e
->elment_color_p
;
1980 draw_element_line(g
, e
, cr
, color_to_set
);
1981 line_stroked
= FALSE
;
1985 if (!line_stroked
) {
1987 line_stroked
= TRUE
;
1989 draw_element_ellipse(g
, e
, cr
);
1998 /* Make sure any remaining lines get drawn */
2005 static void draw_element_line(struct gtk_graph
*g
, struct element
*e
, cairo_t
*cr
,
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 */
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
);
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
);
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
);
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
,
2080 axis
->surface
[1] = gdk_window_create_similar_surface(gtk_widget_get_window(axis
->drawing_area
),
2081 CAIRO_CONTENT_COLOR
,
2085 axis
->displayed
= 0;
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;
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
;
2113 g_object_unref(axis
->pixmap
[0]);
2114 g_object_unref(axis
->pixmap
[1]);
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
);
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
;
2134 int not_disp
, rdigits
, offset
, imin
, imax
;
2135 double bottom
, top
, j
, fl
, corr
;
2136 PangoLayout
*layout
;
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
;
2146 axis_compute_ticks(axis
, bottom
, top
, AXIS_VERTICAL
);
2148 j
= axis
->major
- floor(axis
->major
);
2149 for (rdigits
=0; rdigits
<= 6; rdigits
++) {
2156 not_disp
= 1 ^ axis
->displayed
;
2158 #if GTK_CHECK_VERSION(2,22,0)
2159 cr
= cairo_create(axis
->surface
[not_disp
]);
2161 cr
= gdk_cairo_create(axis
->pixmap
[not_disp
]);
2163 cairo_set_source_rgb(cr
, 1, 1, 1);
2164 cairo_rectangle(cr
, 0, 0, axis
->p
.width
, axis
->p
.height
);
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
);
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
++) {
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
))
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
));
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
++) {
2220 layout
= gtk_widget_create_pango_layout(g
->drawing_area
,
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
));
2231 static void h_axis_pixmap_draw(struct axis
*axis
)
2233 struct gtk_graph
*g
= axis
->g
;
2235 double major_tick
, minor_tick
;
2236 int not_disp
, rdigits
, offset
, imin
, imax
;
2237 double left
, right
, j
, fl
, corr
;
2238 PangoLayout
*layout
;
2241 debug(DBS_FENTRY
) puts("h_axis_pixmap_draw()");
2242 left
= (g
->wp
.x
-g
->geom
.x
) / (double )g
->geom
.width
* g
->bounds
.width
;
2244 right
= (g
->wp
.x
- g
->geom
.x
+ g
->wp
.width
) / (double )g
->geom
.width
* g
->bounds
.width
;
2246 axis_compute_ticks(axis
, left
, right
, AXIS_HORIZONTAL
);
2248 j
= axis
->major
- floor(axis
->major
);
2249 for (rdigits
=0; rdigits
<= 6; rdigits
++) {
2256 not_disp
= 1 ^ axis
->displayed
;
2258 #if GTK_CHECK_VERSION(2,22,0)
2259 cr
= cairo_create(axis
->surface
[not_disp
]);
2261 cr
= gdk_cairo_create(axis
->pixmap
[not_disp
]);
2263 cairo_set_source_rgb(cr
, 1, 1, 1);
2264 cairo_rectangle(cr
, 0, 0, axis
->p
.width
, axis
->p
.height
);
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
);
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
++) {
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
))
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) {
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
++) {
2315 layout
= gtk_widget_create_pango_layout(g
->drawing_area
,
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
));
2326 static void axis_pixmaps_switch(struct axis
*axis
)
2328 axis
->displayed
= 1 ^ axis
->displayed
;
2331 static void axis_pixmap_display(struct axis
*axis
)
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
);
2339 gdk_cairo_set_source_pixmap(cr
, axis
->pixmap
[axis
->displayed
], axis
->p
.x
, axis
->p
.y
);
2341 cairo_rectangle(cr
, axis
->p
.x
, axis
->p
.y
, axis
->p
.width
, axis
->p
.height
);
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
);
2360 for (i
=-9; i
<= 12; i
++) {
2361 if (x
/ pow(10, i
) < 1)
2365 ms
= (int )(x
/ pow(10, i
));
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 */
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
);
2392 check_needed
= TRUE
;
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",
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
;
2423 debug(DBS_AXES_TICKS
) printf("axis->minor diminished to %.1f\n",
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
);
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
)
2448 static void axis_ticks_down(int *i
, int *j
)
2457 static int get_label_dim(struct axis
*axis
, int dir
, double label
)
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
++) {
2473 g_snprintf(str
, sizeof(str
), "%.*f", rdigits
, label
);
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
));
2481 layout
= gtk_widget_create_pango_layout(axis
->g
->drawing_area
,
2483 pango_layout_get_pixel_size(layout
, NULL
, &dim
);
2484 g_object_unref(G_OBJECT(layout
));
2487 puts("initialize axis: an axis must be either horizontal or vertical");
2493 static double axis_zoom_get(struct axis
*axis
, int dir
)
2496 case AXIS_HORIZONTAL
:
2497 return axis
->g
->zoom
.x
;
2499 return axis
->g
->zoom
.y
;
2505 static void graph_select_segment(struct gtk_graph
*g
, int x
, int y
)
2507 struct element_list
*list
;
2511 debug(DBS_FENTRY
) puts("graph_select_segment()");
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
++) {
2524 if (line_detect_collision(e
, x
, y
)) {
2525 num
= e
->parent
->num
;
2529 if (ellipse_detect_collision(e
, x
, y
)) {
2530 num
= e
->parent
->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
);
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
);
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
)))
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
))
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
2594 if (g
->cross
.erase_needed
&& (g
->cross
.x
== x
) && (g
->cross
.y
== y
)) {
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
);
2611 cairo_move_to(cr
, x
, g
->wp
.y
);
2612 cairo_line_to(cr
, x
, g
->wp
.y
+ g
->wp
.height
);
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
);
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
)
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
);
2677 static void magnify_create(struct gtk_graph
*g
, int x
, int y
)
2679 struct gtk_graph
*mg
;
2680 struct ipoint pos
, offsetpos
;
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 */
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());
2731 e
= gdk_event_get();
2733 if (e
->any
.type
== GDK_EXPOSE
) {
2741 #if GTK_CHECK_VERSION(2,22,0)
2742 mg
->surface
[0] = mg
->surface
[1] = NULL
;
2744 mg
->pixmap
[0] = mg
->pixmap
[1] = NULL
;
2745 #endif /* GTK_CHECK_VERSION(2,22,0) */
2746 graph_pixmaps_create(mg
);
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]);
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
);
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
)
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
)
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
]);
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);
2820 cairo_move_to(cr
, g
->magnify
.width
- 1, 0);
2821 cairo_line_to(cr
, g
->magnify
.width
- 1, g
->magnify
.height
);
2824 cairo_move_to(cr
, 0, 0);
2825 cairo_line_to(cr
, 0, g
->magnify
.height
- 1);
2828 cairo_move_to(cr
, 0, g
->magnify
.height
- 1);
2829 cairo_line_to(cr
, g
->magnify
.width
- 1, g
->magnify
.height
- 1);
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
;
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
));
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
);
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
);
2896 #if GTK_CHECK_VERSION(3,0,0)
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
);
2910 cairo_rectangle(cr
, g
->wp
.x
+ g
->wp
.width
, g
->wp
.y
, RMARGIN_WIDTH
, g
->wp
.height
);
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
);
2922 static gboolean
expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
2924 struct gtk_graph
*g
= (struct gtk_graph
*)user_data
;
2927 debug(DBS_FENTRY
) puts("expose_event()");
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
);
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
);
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
);
2957 #define ZOOM_REDRAW 1
2958 #define ZOOM_NOREDRAW 0
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
)) {
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
)
3004 graph_element_lists_make(g
);
3005 g
->cross
.erase_needed
= FALSE
;
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
);
3018 get_zoomfactor(struct gtk_graph
*g
, struct zoomfactor
*zf
, double step_x
,
3021 if (g
->zoom
.flags
& ZOOM_OUT
) {
3023 * If can't zoom out anymore don't waste time redrawing
3026 if ((g
->geom
.height
<= g
->wp
.height
) &&
3027 (g
->geom
.width
<= g
->wp
.width
)) {
3032 if (g
->zoom
.flags
& ZOOM_HLOCK
)
3036 if (g
->zoom
.flags
& ZOOM_VLOCK
)
3041 if (g
->zoom
.flags
& ZOOM_HLOCK
)
3045 if (g
->zoom
.flags
& ZOOM_VLOCK
)
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
)
3062 /* Right hand not far enough */
3063 if (lcl_zoomrect
.x
+ lcl_zoomrect
.width
< g
->wp
.x
)
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
;
3078 if (lcl_zoomrect
.y
> g
->wp
.y
+ g
->wp
.height
)
3080 /* Bottom too high */
3081 if (lcl_zoomrect
.y
+ lcl_zoomrect
.height
< g
->wp
.y
)
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
;
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
,
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
));
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
;
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
;
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
)
3246 static void do_key_motion_down(struct gtk_graph
*g
, int step
)
3252 static void do_key_motion_left(struct gtk_graph
*g
, int step
)
3258 static void do_key_motion_right(struct gtk_graph
*g
, int step
)
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
));
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
);
3287 #else /* !ORIGINAL_WIN32_BUTTONS */
3288 } else if (event
->button
== MOUSE_BUTTON_MIDDLE
) {
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
);
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*/
3313 static gboolean
motion_notify_event(GtkWidget
*widget _U_
, GdkEventMotion
*event
, gpointer user_data
)
3315 struct gtk_graph
*g
= (struct gtk_graph
*)user_data
;
3317 GdkModifierType state
;
3319 /* debug(DBS_FENTRY) puts("motion_notify_event()"); */
3322 get_mouse_position(g
->drawing_area
, &x
, &y
, &state
);
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
;
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
)
3364 cross_draw(g
, x
, y
);
3366 /* Draw bounded box for zoomrect being chosen! */
3367 if (g
->zoomrect_erase_needed
) {
3370 zoomrect_draw(g
, x
, y
);
3372 if (g
->cross
.erase_needed
)
3374 if (g
->cross
.draw
) {
3375 cross_draw(g
, x
, y
);
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
)
3421 static gboolean
key_press_event(GtkWidget
*widget _U_
, GdkEventKey
*event
, gpointer user_data
)
3423 struct gtk_graph
*g
= (struct gtk_graph
*)user_data
;
3426 debug(DBS_FENTRY
) puts("key_press_event()");
3428 if ((event
->state
& GDK_CONTROL_MASK
) && (event
->state
& GDK_SHIFT_MASK
))
3430 else if (event
->state
& GDK_CONTROL_MASK
)
3432 else if (event
->state
& GDK_SHIFT_MASK
)
3437 switch (event
->keyval
) {
3439 toggle_crosshairs(g
);
3442 toggle_time_origin(g
);
3445 toggle_seq_origin(g
);
3449 restore_initial_graph_view(g
);
3453 do_zoom_in_keyboard(g
);
3457 do_zoom_out_keyboard(g
);
3460 do_magnify_create(g
);
3463 do_select_segment(g
);
3469 do_throughput_graph(g
);
3472 do_ts_graph_stevens(g
);
3475 do_ts_graph_tcptrace(g
);
3481 do_key_motion_left(g
, step
);
3484 do_key_motion_up(g
, step
);
3487 do_key_motion_right(g
, step
);
3490 do_key_motion_down(g
, step
);
3493 callback_create_help(NULL
, NULL
);
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
);
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
)
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
) {
3533 get_mouse_position(widget
, &x
, &y
, NULL
);
3534 cross_draw(g
, x
, y
);
3539 static void toggle_crosshairs(struct gtk_graph
*g
)
3542 if (g
->cross
.draw
) {
3544 get_mouse_position(g
->drawing_area
, &x
, &y
, NULL
);
3545 cross_draw(g
, x
, y
);
3546 } else if (g
->cross
.erase_needed
) {
3549 /* toggle buttons emit their "toggled" signals so don't bother doing
3550 * any real work here, it will be done in signal handlers */
3552 gtk_toggle_button_set_active(g
->cross
.on_toggle
, TRUE
);
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
);
3563 case GRAPH_TSEQ_TCPTRACE
:
3564 tseq_tcptrace_toggle_time_origin(g
);
3566 case GRAPH_THROUGHPUT
:
3567 tput_toggle_time_origin(g
);
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
);
3582 case GRAPH_TSEQ_TCPTRACE
:
3583 tseq_tcptrace_toggle_seq_origin(g
);
3584 axis_display(g
->y_axis
);
3587 rtt_toggle_seq_origin(g
);
3588 axis_display(g
->x_axis
);
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)";
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()");
3641 switch (g
->tg
.type
) {
3642 case GRAPH_TSEQ_STEVENS
:
3643 tseq_stevens_read_config(g
);
3645 case GRAPH_TSEQ_TCPTRACE
:
3646 tseq_tcptrace_read_config(g
);
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
;
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
)) {
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
;
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
;
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
;
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
));
3771 e
= elements
= g
->elists
->elements
;
3773 for (tmp
= g
->tg
.segments
; tmp
; tmp
= tmp
->next
) {
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
)) {
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
;
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;
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 */
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 */
3821 * tcptrace-style time-sequence graph
3824 static void tseq_tcptrace_read_config(struct gtk_graph
*g
)
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;
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;
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;
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)";
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 */
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
;
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
));
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
));
3907 /* Existing array */
3908 e1
= elements1
= g
->elists
->next
->elements
;
3913 seq_base
= (guint32
) yy0
;
3915 for (tmp
= g
->tg
.segments
; tmp
; tmp
= tmp
->next
) {
3919 secs
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
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 */
3930 seq_cur
= tmp
->th_seq
- seq_base
;
3931 if (TCP_SYN(tmp
->th_flags
) || TCP_FIN(tmp
->th_flags
))
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
;
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
;
3946 e0
->type
= ELMT_LINE
;
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
;
3954 e0
->type
= ELMT_LINE
;
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
;
3964 if (! TCP_ACK(tmp
->th_flags
))
3965 /* SYN's and RST's do not necessarily have ACK's*/
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
;
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
;
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
;
3986 /* Vertical: from previous ACKNO to current one (at current time) */
3987 e1
->type
= ELMT_LINE
;
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;
3997 /* Horizontal: window line */
3998 e1
->type
= ELMT_LINE
;
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
;
4008 /* Vertical: old window to new window */
4009 e1
->type
= ELMT_LINE
;
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
;
4019 /* Toggle color to use for ACKs... */
4027 /* Now any SACK entries */
4028 if (tmp
->num_sack_ranges
) {
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
;
4042 /* Set the drawing color. First range is significant, so use
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
;
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 */
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 */
4089 static void tput_make_elmtlist(struct gtk_graph
*g
)
4091 struct segment
*tmp
, *oldest
;
4092 struct element
*elements
, *e
;
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
));
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
;
4112 /* debug(DBS_TPUT_ELMTS) printf("tput=%f\n", tput); */
4114 e
->type
= ELMT_ELLIPSE
;
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;
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
;
4149 debug(DBS_TPUT_ELMTS
) printf("tput=%f\n", tput
);
4152 t
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
4157 t0
= g
->tg
.segments
->rel_secs
+ g
->tg
.segments
->rel_usecs
/ 1000000.0;
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)";
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 */
4202 static void rtt_read_config(struct gtk_graph
*g
)
4204 debug(DBS_FENTRY
) puts("rtt_read_config()");
4207 g
->s
.rtt
.height
= 4;
4210 g
->title
= (const char ** )g_malloc(2 * sizeof(char *));
4211 g
->title
[0] = "Round Trip Time Graph";
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
;
4226 double xx0
, yy0
, ymax
;
4228 guint32 seq_base
= 0;
4230 debug(DBS_FENTRY
) puts("rtt_initialize()");
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
;
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
);
4251 rtt_put_unack_on_list(&unack
, u
);
4254 if (seqno
+ tmp
->th_seglen
> xmax
)
4255 xmax
= seqno
+ tmp
->th_seglen
;
4257 guint32 ackno
= tmp
->th_ack
-seq_base
;
4258 double time_val
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
4261 for (u
=unack
; u
; u
=v
) {
4262 if (ackno
> u
->seqno
) {
4263 double rtt
= time_val
- u
->time
;
4267 rtt_delete_unack_from_list(&unack
, u
);
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
));
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
);
4314 rtt_put_unack_on_list(&unack
, u
);
4317 guint32 ackno
= tmp
->th_ack
-seq_base
;
4318 double time_val
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
4321 for (u
=unack
; u
; u
=v
) {
4322 if (ackno
> u
->seqno
) {
4323 double rtt
= time_val
- u
->time
;
4325 e
->type
= ELMT_ELLIPSE
;
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;
4334 rtt_delete_unack_from_list(&unack
, u
);
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
;
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";
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
,
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
)
4409 /* remind time of first probe */
4410 if ((sec_base
< 0) && (sec
> 0))
4413 if ( sec_max
< sec
)
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
));
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
,
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))
4466 /* only data or ack segments */
4467 if ( (flags
& (TH_SYN
|TH_RST
)) == 0 )
4469 e
->type
= ELMT_ELLIPSE
;
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;
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
)
4492 buf
= _fcvt(x
, 0, &dec
, &sig
);