3 * Based upon tcp_graph.c
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <gdk/gdkkeysyms.h>
33 #if GTK_CHECK_VERSION(3,0,0)
34 # include <gdk/gdkkeysyms-compat.h>
37 #include <epan/packet.h>
38 #include <epan/epan_dissect.h>
39 #include <epan/dissectors/packet-rlc-lte.h>
42 #include "../globals.h"
43 #include "../frame_tvbuff.h"
44 #include "ui/simple_dialog.h"
45 #include "../stat_menu.h"
47 #include "ui/gtk/gui_utils.h"
48 #include "ui/gtk/dlg_utils.h"
49 #include "ui/gtk/gui_stat_menu.h"
50 #include "ui/gtk/tap_param_dlg.h"
52 #include "ui/gtk/old-gtk-compat.h"
54 #define AXIS_HORIZONTAL 0
55 #define AXIS_VERTICAL 1
57 #define WINDOW_TITLE_LENGTH 256
59 #define MOUSE_BUTTON_LEFT 1
60 #define MOUSE_BUTTON_MIDDLE 2
61 #define MOUSE_BUTTON_RIGHT 3
63 #define MAX_PIXELS_PER_SN 90
64 #define MAX_PIXELS_PER_SECOND 50000
66 extern int proto_rlc_lte
;
70 guint32 num
; /* framenum */
76 gboolean isControlPDU
;
78 guint16 isResegmented
;
82 guint16 NACKs
[MAX_NACKs
];
92 double x1
, y1
, x2
, y2
;
96 int x
, y
, width
, height
;
114 double x
, y
, width
, height
;
117 struct ellipse_params
{
123 GdkRGBA
*elment_color_p
;
124 struct segment
*parent
;
126 struct line_params line
;
127 struct ellipse_params ellipse
;
131 struct element_list
{
132 struct element_list
*next
;
133 struct element
*elements
;
137 struct graph
*g
; /* which graph we belong to */
138 GtkWidget
*drawing_area
;
139 /* Double-buffering to avoid flicker */
140 #if GTK_CHECK_VERSION(2,22,0)
141 cairo_surface_t
*surface
[2];
143 GdkPixmap
*pixmap
[2];
145 /* Which of the 2 buffers we are currently showing */
147 #define AXIS_ORIENTATION 1 << 0
149 /* dim and orig (relative to origin of window) of axis' pixmap */
151 /* dim and orig (relative to origin of axis' pixmap) of scale itself */
154 gdouble major
, minor
; /* major and minor ticks */
158 #define HAXIS_INIT_HEIGHT 70
159 #define VAXIS_INIT_WIDTH 100
160 #define TITLEBAR_HEIGHT 50
161 #define RMARGIN_WIDTH 30
163 struct style_rlc_lte
{
165 GdkRGBA seq_resegmented_color
;
166 GdkRGBA ack_color
[2];
171 #define TIME_ORIGIN 0x10
172 /* show time from beginning of capture as opposed to time from beginning
173 * of the connection */
174 #define TIME_ORIGIN_CAP 0x10
175 #define TIME_ORIGIN_CONN 0x00
179 int draw
; /* indicates whether we should draw cross at all */
180 int erase_needed
; /* indicates whether currently drawn at recorded position */
184 double x0
, y0
, width
, height
;
193 double step_x
, step_y
;
195 #define ZOOM_OUT (1 << 0)
206 #define GRAPH_DESTROYED (1 << 0)
208 GtkWidget
*toplevel
; /* keypress handler needs this */
209 GtkWidget
*drawing_area
;
210 PangoFontDescription
*font
; /* font used for annotations etc. */
212 /* Double-buffering */
213 #if GTK_CHECK_VERSION(2,22,0)
214 cairo_surface_t
*title_surface
;
215 cairo_surface_t
*surface
[2];
217 GdkPixmap
*title_pixmap
;
218 GdkPixmap
*pixmap
[2];
220 int displayed
; /* which of both pixmaps is on screen right now */
222 /* Next 4 attribs describe the graph in natural units, before any scaling.
223 * For example, if we want to display graph of TCP conversation that
224 * started 112.309845 s after beginning of the capture and ran until
225 * 479.093582 s, 237019 B went through the connection (in one direction)
226 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
227 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
228 struct bounds bounds
;
229 /* dimensions and position of the graph, both expressed already in pixels.
230 * x and y give the position of upper left corner of the graph relative
231 * to origin of the graph window, size is basically bounds*zoom */
233 /* viewport (=graph window area which is reserved for graph itself), its
234 * size and position relative to origin of the graph window */
237 /* If we need to display 237019 sequence numbers (=bytes) onto say 500
238 * pixels, we have to scale the graph down by factor of 0.002109. This
239 * number would be zoom.y. Obviously, both directions have separate zooms.*/
241 gboolean zoomrect_erase_needed
;
243 struct axis
*x_axis
, *y_axis
;
245 /* List of segments to show */
246 struct segment
*segments
;
248 /* These are filled in with the channel/direction this graph is showing */
255 /* Lists of elements to draw */
256 struct element_list
*elists
; /* element lists */
258 /* Colours, etc to be used in drawing */
259 struct style_rlc_lte style
;
262 static int refnum
= 0;
264 #define debug(section) if (debugging & section)
265 /* print function entry points */
266 #define DBS_FENTRY (1 << 0)
267 #define DBS_AXES_TICKS (1 << 1)
268 #define DBS_AXES_DRAWING (1 << 2)
269 #define DBS_GRAPH_DRAWING (1 << 3)
270 #define DBS_TPUT_ELMTS (1 << 4)
271 /*static int debugging = DBS_FENTRY;*/
272 /*static int debugging = DBS_AXES_TICKS;*/
273 /*static int debugging = DBS_AXES_DRAWING;*/
274 /*static int debugging = DBS_GRAPH_DRAWING;*/
275 /*static int debugging = DBS_TPUT_ELMTS;*/
276 static int debugging
= 0;
278 static void create_gui(struct graph
* );
279 static void create_drawing_area(struct graph
* );
280 static void callback_toplevel_destroy(GtkWidget
* , gpointer
);
281 static void callback_create_help(GtkWidget
* , gpointer
);
282 static void get_mouse_position(GtkWidget
*, int *pointer_x
, int *pointer_y
, GdkModifierType
*mask
);
283 static rlc_lte_tap_info
*select_rlc_lte_session(capture_file
*, struct segment
* );
284 static int compare_headers(guint16 ueid1
, guint16 channelType1
, guint16 channelId1
, guint8 rlcMode1
, guint8 direction1
,
285 guint16 ueid2
, guint16 channelType2
, guint16 channelId2
, guint8 rlcMode2
, guint8 direction2
,
286 gboolean isControlFrame
);
287 static void get_data_control_counts(struct graph
*g
, int *data
, int *acks
, int *nacks
);
289 static struct graph
*graph_new(void);
290 static void graph_destroy(struct graph
* );
291 static void graph_initialize_values(struct graph
* );
292 static void graph_init_sequence(struct graph
* );
293 static void draw_element_line(struct graph
* , struct element
* , cairo_t
* , GdkRGBA
*new_color
);
294 static void draw_element_ellipse(struct graph
* , struct element
* , cairo_t
*cr
, GdkRGBA
*new_color
);
295 static void graph_display(struct graph
* );
296 static void graph_pixmaps_create(struct graph
* );
297 static void graph_pixmaps_switch(struct graph
* );
298 static void graph_pixmap_draw(struct graph
* );
299 static void graph_pixmap_display(struct graph
* );
300 static void graph_element_lists_make(struct graph
* );
301 static void graph_element_lists_free(struct graph
* );
302 static void graph_element_lists_initialize(struct graph
* );
303 static void graph_title_pixmap_create(struct graph
* );
304 static void graph_title_pixmap_draw(struct graph
* );
305 static void graph_title_pixmap_display(struct graph
* );
306 static void graph_segment_list_get(struct graph
*, gboolean channel_known
);
307 static void graph_segment_list_free(struct graph
* );
308 static void graph_select_segment(struct graph
* , int , int );
309 static int line_detect_collision(struct element
* , int , int );
310 static int ellipse_detect_collision(struct element
*e
, int x
, int y
);
311 static void axis_pixmaps_create(struct axis
* );
312 static void axis_pixmaps_switch(struct axis
* );
313 static void axis_display(struct axis
* );
314 static void v_axis_pixmap_draw(struct axis
* );
315 static void h_axis_pixmap_draw(struct axis
* );
316 static void axis_pixmap_display(struct axis
* );
317 static void axis_compute_ticks(struct axis
* , double , double , int );
318 static double axis_zoom_get(struct axis
* , int );
319 static void axis_ticks_up(int * , int * );
320 static void axis_ticks_down(int * , int * );
321 static void axis_destroy(struct axis
* );
322 static int get_label_dim(struct axis
* , int , double );
324 static void toggle_crosshairs(struct graph
*);
325 static void cross_draw(struct graph
* , int x
, int y
);
326 static void cross_erase(struct graph
* );
327 static void zoomrect_draw(struct graph
* , int , int );
328 static void zoomrect_erase(struct graph
* );
329 static gboolean
motion_notify_event(GtkWidget
* , GdkEventMotion
* , gpointer
);
331 static void toggle_time_origin(struct graph
* );
332 static void restore_initial_graph_view(struct graph
*g
);
333 static gboolean
configure_event(GtkWidget
* , GdkEventConfigure
* , gpointer
);
334 #if GTK_CHECK_VERSION(3,0,0)
335 static gboolean
draw_event(GtkWidget
*widget
, cairo_t
*cr
, gpointer user_data
);
337 static gboolean
expose_event(GtkWidget
* , GdkEventExpose
* , gpointer
);
339 static gboolean
button_press_event(GtkWidget
* , GdkEventButton
* , gpointer
);
340 static gboolean
button_release_event(GtkWidget
* , GdkEventButton
* , gpointer
);
341 static gboolean
key_press_event(GtkWidget
* , GdkEventKey
* , gpointer
);
342 static void graph_initialize(struct graph
*);
343 static void graph_get_bounds(struct graph
*);
344 static void graph_read_config(struct graph
*);
345 static void rlc_lte_make_elmtlist(struct graph
*);
347 #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
348 /* Starting VS2013, rint already defined in math.h. No need to redefine */
349 static int rint(double ); /* compiler template for Windows */
352 /* This should arguably be part of the graph, but in practice you can
353 only click on one graph at a time, so this is probably OK */
354 static struct irect zoomrect
;
357 /* XXX - what about OS X? */
358 static char helptext
[] =
359 "Here's what you can do:\n"
361 " Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
362 " can also drag to zoom in on a rectangular region\n"
363 " Middle Mouse Button zooms in (towards area under cursor)\n"
364 " Right Mouse Button moves the graph (if zoomed in)\n"
366 " <Space bar> toggles crosshairs on/off\n"
368 " 'i' or '+' zoom in (towards area under mouse pointer)\n"
369 " 'o' or '-' zoom out\n"
370 " (add shift to lock Y axis, control to lock X axis)\n"
371 " 'r' or <Home> restore graph to initial state (zoom out max)\n"
372 " 't' toggle time axis to being at zero, or to use time in capture\n"
374 " <Left> move view left by 100 pixels (if zoomed in)\n"
375 " <Right> move view right 100 pixels (if zoomed in)\n"
376 " <Up> move view up by 100 pixels (if zoomed in)\n"
377 " <Down> move view down by 100 pixels (if zoomed in)\n"
379 " <Shift><Left> move view left by 10 pixels (if zoomed in)\n"
380 " <Shift><Right> move view right 10 pixels (if zoomed in)\n"
381 " <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
382 " <Shift><Down> move view down by 10 pixels (if zoomed in)\n"
384 " <Ctrl><Left> move view left by 1 pixel (if zoomed in)\n"
385 " <Ctrl><Right> move view right 1 pixel (if zoomed in)\n"
386 " <Ctrl><Up> move view up by 1 pixel (if zoomed in)\n"
387 " <Ctrl><Down> move view down by 1 pixel (if zoomed in)\n"
389 " <Page_Up> move up by a large number of pixels (if zoomed in)\n"
390 " <Page_Down> move down by a large number of pixels (if zoomed in)\n"
393 static void set_busy_cursor(GdkWindow
*w
)
395 GdkCursor
* cursor
= gdk_cursor_new(GDK_WATCH
);
396 gdk_window_set_cursor(w
, cursor
);
398 #if GTK_CHECK_VERSION(3,0,0)
399 g_object_unref(cursor
);
401 gdk_cursor_unref(cursor
);
405 static void unset_busy_cursor(GdkWindow
*w
)
407 gdk_window_set_cursor(w
, NULL
);
411 void rlc_lte_graph_cb(GtkAction
*action _U_
, gpointer user_data _U_
)
413 struct segment current
;
416 debug(DBS_FENTRY
) puts("rlc_lte_graph_cb()");
418 /* Can we choose an RLC channel from the selected frame? */
419 if (!select_rlc_lte_session(&cfile
, ¤t
)) {
423 if (!(g
= graph_new())) {
428 graph_initialize_values(g
);
430 /* Get our list of segments from the packet list */
431 graph_segment_list_get(g
, FALSE
);
434 graph_init_sequence(g
);
437 void rlc_lte_graph_known_channel_launch(guint16 ueid
, guint8 rlcMode
,
438 guint16 channelType
, guint16 channelId
,
443 debug(DBS_FENTRY
) puts("rlc_lte_graph_known_channel()");
445 if (!(g
= graph_new())) {
450 graph_initialize_values(g
);
452 /* Can set channel info for graph now */
454 g
->rlcMode
= rlcMode
;
455 g
->channelType
= channelType
;
456 g
->channelId
= channelId
;
457 g
->direction
= direction
;
459 /* Get our list of segments from the packet list */
460 graph_segment_list_get(g
, TRUE
);
463 graph_init_sequence(g
);
467 static void create_gui(struct graph
*g
)
469 debug(DBS_FENTRY
) puts("create_gui()");
470 /* create_text_widget(g); */
471 create_drawing_area(g
);
474 static void create_drawing_area(struct graph
*g
)
476 #if GTK_CHECK_VERSION(3,0,0)
477 GtkStyleContext
*context
;
480 char window_title
[WINDOW_TITLE_LENGTH
];
481 GtkAllocation widget_alloc
;
483 debug(DBS_FENTRY
) puts("create_drawing_area()");
484 display_name
= cf_get_display_name(&cfile
);
485 /* Set channel details in title */
486 g_snprintf(window_title
, WINDOW_TITLE_LENGTH
, "LTE RLC Graph %d: %s (UE-%u, chan=%s%u %s - %s)",
487 refnum
, display_name
,
488 g
->ueid
, (g
->channelType
== CHANNEL_TYPE_SRB
) ? "SRB" : "DRB",
490 (g
->direction
== DIRECTION_UPLINK
) ? "UL" : "DL",
491 (g
->rlcMode
== RLC_UM_MODE
) ? "UM" : "AM");
492 g_free(display_name
);
493 g
->toplevel
= dlg_window_new("RLC Graph");
494 gtk_window_set_title(GTK_WINDOW(g
->toplevel
), window_title
);
495 gtk_widget_set_name(g
->toplevel
, "Test Graph");
497 /* Create the drawing area */
498 g
->drawing_area
= gtk_drawing_area_new();
499 g
->x_axis
->drawing_area
= g
->y_axis
->drawing_area
= g
->drawing_area
;
500 gtk_widget_set_size_request(g
->drawing_area
,
501 g
->wp
.width
+ g
->wp
.x
+ RMARGIN_WIDTH
,
502 g
->wp
.height
+ g
->wp
.y
+ g
->x_axis
->s
.height
);
503 gtk_widget_show(g
->drawing_area
);
505 #if GTK_CHECK_VERSION(3,0,0)
506 g_signal_connect(g
->drawing_area
, "draw", G_CALLBACK(draw_event
), g
);
508 g_signal_connect(g
->drawing_area
, "expose_event", G_CALLBACK(expose_event
), g
);
511 g_signal_connect(g
->drawing_area
, "button_press_event",
512 G_CALLBACK(button_press_event
), g
);
513 g_signal_connect(g
->drawing_area
, "button_release_event",
514 G_CALLBACK(button_release_event
), g
);
515 g_signal_connect(g
->toplevel
, "destroy", G_CALLBACK(callback_toplevel_destroy
), g
);
516 g_signal_connect(g
->drawing_area
, "motion_notify_event",
517 G_CALLBACK(motion_notify_event
), g
);
519 /* why doesn't drawing area send key_press_signals? */
520 g_signal_connect(g
->toplevel
, "key_press_event", G_CALLBACK(key_press_event
), g
);
521 gtk_widget_set_events(g
->toplevel
,
522 GDK_KEY_PRESS_MASK
|GDK_KEY_RELEASE_MASK
);
524 gtk_widget_set_events(g
->drawing_area
,
526 | GDK_LEAVE_NOTIFY_MASK
527 | GDK_ENTER_NOTIFY_MASK
528 | GDK_BUTTON_PRESS_MASK
529 | GDK_BUTTON_RELEASE_MASK
530 | GDK_POINTER_MOTION_MASK
531 | GDK_POINTER_MOTION_HINT_MASK
);
533 gtk_container_add(GTK_CONTAINER(g
->toplevel
), g
->drawing_area
);
534 gtk_widget_show(g
->toplevel
);
536 /* In case we didn't get what we asked for */
537 gtk_widget_get_allocation(GTK_WIDGET(g
->drawing_area
), &widget_alloc
);
538 g
->wp
.width
= widget_alloc
.width
- g
->wp
.x
- RMARGIN_WIDTH
;
539 g
->wp
.height
= widget_alloc
.height
- g
->wp
.y
- g
->x_axis
->s
.height
;
541 #if GTK_CHECK_VERSION(3,0,0)
542 context
= gtk_widget_get_style_context(g
->drawing_area
);
543 gtk_style_context_get(context
, GTK_STATE_FLAG_NORMAL
,
544 GTK_STYLE_PROPERTY_FONT
, &g
->font
,
547 g
->font
= gtk_widget_get_style(g
->drawing_area
)->font_desc
;
551 g_signal_connect(g
->drawing_area
, "configure_event", G_CALLBACK(configure_event
), g
);
554 static void callback_toplevel_destroy(GtkWidget
*widget _U_
, gpointer data
)
556 struct graph
*g
= (struct graph
* )data
;
558 if (!(g
->flags
& GRAPH_DESTROYED
)) {
559 g
->flags
|= GRAPH_DESTROYED
;
560 graph_destroy((struct graph
* )data
);
564 static void callback_create_help(GtkWidget
*widget _U_
, gpointer data _U_
)
566 GtkWidget
*toplevel
, *vbox
, *text
, *scroll
, *bbox
, *close_bt
;
569 toplevel
= dlg_window_new("Help for LTE RLC graphing");
570 gtk_window_set_default_size(GTK_WINDOW(toplevel
), 540, 540);
572 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
573 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
574 gtk_container_add(GTK_CONTAINER(toplevel
), vbox
);
576 scroll
= scrolled_window_new(NULL
, NULL
);
577 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll
),
579 gtk_box_pack_start(GTK_BOX(vbox
), scroll
, TRUE
, TRUE
, 0);
580 text
= gtk_text_view_new();
581 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
582 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
583 gtk_text_buffer_set_text(buf
, helptext
, -1);
584 gtk_container_add(GTK_CONTAINER(scroll
), text
);
587 bbox
= dlg_button_row_new(GTK_STOCK_CLOSE
, NULL
);
588 gtk_box_pack_start(GTK_BOX(vbox
), bbox
, FALSE
, FALSE
, 0);
589 gtk_widget_show(bbox
);
591 close_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_CLOSE
);
592 window_set_cancel_button(toplevel
, close_bt
, window_cancel_button_cb
);
594 g_signal_connect(toplevel
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
596 gtk_widget_show_all(toplevel
);
597 window_present(toplevel
);
600 static void get_mouse_position(GtkWidget
*widget
, int *pointer_x
, int *pointer_y
, GdkModifierType
*mask
)
602 #if GTK_CHECK_VERSION(3,0,0)
603 gdk_window_get_device_position (gtk_widget_get_window(widget
),
604 gdk_device_manager_get_client_pointer(
605 gdk_display_get_device_manager(
606 gtk_widget_get_display(GTK_WIDGET(widget
)))),
607 pointer_x
, pointer_y
, mask
);
610 gdk_window_get_pointer (gtk_widget_get_window(widget
), pointer_x
, pointer_y
, mask
);
614 static struct graph
*graph_new(void)
618 g
= (struct graph
* )g_malloc0(sizeof(struct graph
));
619 graph_element_lists_initialize(g
);
621 g
->x_axis
= (struct axis
* )g_malloc0(sizeof(struct axis
));
622 g
->y_axis
= (struct axis
* )g_malloc0(sizeof(struct axis
));
625 g
->x_axis
->flags
= 0;
626 g
->x_axis
->flags
|= AXIS_ORIENTATION
;
627 g
->x_axis
->s
.x
= g
->x_axis
->s
.y
= 0;
628 g
->x_axis
->s
.height
= HAXIS_INIT_HEIGHT
;
629 g
->x_axis
->p
.x
= VAXIS_INIT_WIDTH
;
630 g
->x_axis
->p
.height
= HAXIS_INIT_HEIGHT
;
633 g
->y_axis
->flags
= 0;
634 g
->y_axis
->flags
&= ~AXIS_ORIENTATION
;
635 g
->y_axis
->p
.x
= g
->y_axis
->p
.y
= 0;
636 g
->y_axis
->p
.width
= VAXIS_INIT_WIDTH
;
638 g
->y_axis
->s
.y
= TITLEBAR_HEIGHT
;
639 g
->y_axis
->s
.width
= VAXIS_INIT_WIDTH
;
644 static void graph_initialize_values(struct graph
*g
)
646 g
->geom
.width
= g
->wp
.width
= 750;
647 g
->geom
.height
= g
->wp
.height
= 550;
648 g
->geom
.x
= g
->wp
.x
= VAXIS_INIT_WIDTH
;
649 g
->geom
.y
= g
->wp
.y
= TITLEBAR_HEIGHT
;
651 g
->zoom
.x
= g
->zoom
.y
= 1.0;
653 /* Zooming in step - set same for both dimensions */
654 g
->zoom
.step_x
= g
->zoom
.step_y
= 1.15;
657 g
->cross
.draw
= g
->cross
.erase_needed
= FALSE
;
658 g
->zoomrect_erase_needed
= FALSE
;
662 static void graph_init_sequence(struct graph
*g
)
664 debug(DBS_FENTRY
) puts("graph_init_sequence()");
667 g
->zoom
.initial
.x
= g
->zoom
.x
;
668 g
->zoom
.initial
.y
= g
->zoom
.y
;
669 graph_element_lists_make(g
);
670 g
->x_axis
->s
.width
= g
->wp
.width
;
671 g
->x_axis
->p
.width
= g
->x_axis
->s
.width
+ RMARGIN_WIDTH
;
672 g
->x_axis
->p
.y
= TITLEBAR_HEIGHT
+ g
->wp
.height
;
673 g
->x_axis
->s
.height
= g
->x_axis
->p
.height
= HAXIS_INIT_HEIGHT
;
674 g
->y_axis
->s
.height
= g
->wp
.height
;
675 g
->y_axis
->p
.height
= g
->wp
.height
+ TITLEBAR_HEIGHT
;
676 graph_pixmaps_create(g
);
677 axis_pixmaps_create(g
->y_axis
);
678 axis_pixmaps_create(g
->x_axis
);
679 graph_title_pixmap_create(g
);
680 graph_title_pixmap_draw(g
);
681 graph_title_pixmap_display(g
);
683 axis_display(g
->y_axis
);
684 axis_display(g
->x_axis
);
687 static void graph_initialize(struct graph
*g
)
689 debug(DBS_FENTRY
) puts("graph_initialize()");
692 /* Want to start with absolute times, rather than being relative to 0 */
693 g
->x_axis
->min
= g
->bounds
.x0
;
696 graph_read_config(g
);
699 static void graph_destroy(struct graph
*g
)
701 debug(DBS_FENTRY
) puts("graph_destroy()");
703 axis_destroy(g
->x_axis
);
704 axis_destroy(g
->y_axis
);
705 /* window_destroy(g->drawing_area); */
706 window_destroy(g
->toplevel
);
707 /* window_destroy(g->text); */
708 #if GTK_CHECK_VERSION(2,22,0)
709 if (g
->title_surface
){
710 cairo_surface_destroy(g
->title_surface
);
713 cairo_surface_destroy(g
->surface
[0]);
716 cairo_surface_destroy(g
->surface
[1]);
719 g_object_unref(g
->pixmap
[0]);
720 g_object_unref(g
->pixmap
[1]);
721 #endif /* GTK_CHECK_VERSION(2,22,0) */
724 graph_segment_list_free(g
);
725 graph_element_lists_free(g
);
731 typedef struct rlc_scan_t
{
733 struct segment
*last
;
738 tapall_rlc_lte_packet(void *pct
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *vip
)
740 rlc_scan_t
*ts
= (rlc_scan_t
*)pct
;
741 struct graph
*g
= ts
->g
;
742 const rlc_lte_tap_info
*rlchdr
= (const rlc_lte_tap_info
*)vip
;
744 /* See if this one matches current channel */
745 if (compare_headers(g
->ueid
, g
->channelType
, g
->channelId
, g
->rlcMode
, g
->direction
,
746 rlchdr
->ueid
, rlchdr
->channelType
, rlchdr
->channelId
, rlchdr
->rlcMode
, rlchdr
->direction
,
747 rlchdr
->isControlPDU
)) {
749 struct segment
*segment
= (struct segment
*)g_malloc(sizeof(struct segment
));
751 /* It matches. Add to end of segment list */
752 segment
->next
= NULL
;
753 segment
->num
= pinfo
->fd
->num
;
754 segment
->rel_secs
= (guint32
) pinfo
->rel_ts
.secs
;
755 segment
->rel_usecs
= pinfo
->rel_ts
.nsecs
/1000;
756 segment
->abs_secs
= (guint32
) pinfo
->fd
->abs_ts
.secs
;
757 segment
->abs_usecs
= pinfo
->fd
->abs_ts
.nsecs
/1000;
759 segment
->ueid
= rlchdr
->ueid
;
760 segment
->channelType
= rlchdr
->channelType
;
761 segment
->channelId
= rlchdr
->channelId
;
762 segment
->direction
= rlchdr
->direction
;
764 segment
->isControlPDU
= rlchdr
->isControlPDU
;
766 if (!rlchdr
->isControlPDU
) {
768 segment
->SN
= rlchdr
->sequenceNumber
;
769 segment
->isResegmented
= rlchdr
->isResegmented
;
774 segment
->ACKNo
= rlchdr
->ACKNo
;
775 segment
->noOfNACKs
= rlchdr
->noOfNACKs
;
776 for (n
=0; n
< rlchdr
->noOfNACKs
; n
++) {
777 segment
->NACKs
[n
] = rlchdr
->NACKs
[n
];
782 if (ts
->g
->segments
) {
783 /* Add to end of existing last element */
784 ts
->last
->next
= segment
;
786 /* Make this the first (only) segment */
787 ts
->g
->segments
= segment
;
790 /* This one is now the last one */
798 /* Here we collect all the external data we will ever need */
799 static void graph_segment_list_get(struct graph
*g
, gboolean channel_known
)
801 struct segment current
;
802 GString
*error_string
;
805 debug(DBS_FENTRY
) puts("graph_segment_list_get()");
807 if (!channel_known
) {
808 select_rlc_lte_session(&cfile
, ¤t
);
810 g
->ueid
= current
.ueid
;
811 g
->rlcMode
= current
.rlcMode
;
812 g
->channelType
= current
.channelType
;
813 g
->channelId
= current
.channelId
;
814 g
->direction
= (!current
.isControlPDU
) ? current
.direction
: !current
.direction
;
817 /* rescan all the packets and pick up all frames for this channel.
818 * we only filter for LTE RLC here for speed and do the actual compare
819 * in the tap listener
824 error_string
= register_tap_listener("rlc-lte", &ts
, "rlc-lte", 0, NULL
, tapall_rlc_lte_packet
, NULL
);
826 fprintf(stderr
, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
828 g_string_free(error_string
, TRUE
);
831 cf_retap_packets(&cfile
);
832 remove_tap_listener(&ts
);
836 typedef struct _th_t
{
838 #define MAX_SUPPORTED_CHANNELS 8
839 rlc_lte_tap_info
*rlchdrs
[MAX_SUPPORTED_CHANNELS
];
844 tap_lte_rlc_packet(void *pct
, packet_info
*pinfo _U_
, epan_dissect_t
*edt _U_
, const void *vip
)
847 gboolean is_unique
= TRUE
;
848 th_t
*th
= (th_t
*)pct
;
849 const rlc_lte_tap_info
*header
= (const rlc_lte_tap_info
*)vip
;
851 /* Check new header details against any/all stored ones */
852 for (n
=0; n
< th
->num_hdrs
; n
++) {
853 rlc_lte_tap_info
*stored
= th
->rlchdrs
[n
];
855 if (compare_headers(stored
->ueid
, stored
->channelType
, stored
->channelId
, stored
->rlcMode
, stored
->direction
,
856 header
->ueid
, header
->channelType
, header
->channelId
, header
->rlcMode
, header
->direction
,
857 header
->isControlPDU
)) {
863 /* Add address if unique and have space for it */
864 if (is_unique
&& (th
->num_hdrs
< MAX_SUPPORTED_CHANNELS
)) {
865 /* Copy the tap stuct in as next header */
866 /* Need to take a deep copy of the tap struct, it may not be valid
867 to read after this function returns? */
868 th
->rlchdrs
[th
->num_hdrs
] = g_new(rlc_lte_tap_info
,1);
869 *(th
->rlchdrs
[th
->num_hdrs
]) = *header
;
871 /* Store in direction of data though... */
872 if (th
->rlchdrs
[th
->num_hdrs
]->isControlPDU
) {
873 th
->rlchdrs
[th
->num_hdrs
]->direction
= !th
->rlchdrs
[th
->num_hdrs
]->direction
;
882 /* XXX should be enhanced so that if we have multiple RLC channels in the same MAC frame
883 * then present the user with a dialog where the user can select WHICH RLC
886 static rlc_lte_tap_info
*select_rlc_lte_session(capture_file
*cf
, struct segment
*hdrs
)
891 GString
*error_string
;
893 th_t th
= {0, {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
}};
895 if (cf
->state
== FILE_CLOSED
) {
899 fdata
= cf
->current_frame
;
901 /* no real filter yet */
902 if (!dfilter_compile("rlc-lte", &sfcode
)) {
903 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "%s", dfilter_error_msg
);
907 /* dissect the current frame */
908 if (!cf_read_frame(cf
, fdata
)) {
909 return NULL
; /* error reading the frame */
912 error_string
= register_tap_listener("rlc-lte", &th
, NULL
, 0, NULL
, tap_lte_rlc_packet
, NULL
);
914 fprintf(stderr
, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
916 g_string_free(error_string
, TRUE
);
920 epan_dissect_init(&edt
, cf
->epan
, TRUE
, FALSE
);
921 epan_dissect_prime_dfilter(&edt
, sfcode
);
922 epan_dissect_run_with_taps(&edt
, &cf
->phdr
, frame_tvbuff_new_buffer(fdata
, &cf
->buf
), fdata
, NULL
);
923 rel_ts
= edt
.pi
.rel_ts
;
924 epan_dissect_cleanup(&edt
);
925 remove_tap_listener(&th
);
927 if (th
.num_hdrs
== 0){
928 /* This "shouldn't happen", as our menu items shouldn't
929 * even be enabled if the selected packet isn't an RLC PDU
930 * as rlc_lte_graph_selected_packet_enabled() is used
931 * to determine whether to enable any of our menu items. */
932 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
933 "Selected packet doesn't have an RLC PDU");
936 /* XXX fix this later, we should show a dialog allowing the user
937 to select which session he wants here
940 /* can only handle a single RLC channel yet */
941 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
942 "The selected packet has more than one LTE RLC channel "
947 /* For now, still always choose the first/only one */
948 hdrs
->num
= fdata
->num
;
949 hdrs
->rel_secs
= (guint32
) rel_ts
.secs
;
950 hdrs
->rel_usecs
= rel_ts
.nsecs
/1000;
951 hdrs
->abs_secs
= (guint32
) fdata
->abs_ts
.secs
;
952 hdrs
->abs_usecs
= fdata
->abs_ts
.nsecs
/1000;
954 hdrs
->ueid
= th
.rlchdrs
[0]->ueid
;
955 hdrs
->channelType
= th
.rlchdrs
[0]->channelType
;
956 hdrs
->channelId
= th
.rlchdrs
[0]->channelId
;
957 hdrs
->rlcMode
= th
.rlchdrs
[0]->rlcMode
;
958 hdrs
->isControlPDU
= th
.rlchdrs
[0]->isControlPDU
;
959 hdrs
->direction
= !hdrs
->isControlPDU
? th
.rlchdrs
[0]->direction
: !th
.rlchdrs
[0]->direction
;
961 return th
.rlchdrs
[0];
964 static int compare_headers(guint16 ueid1
, guint16 channelType1
, guint16 channelId1
, guint8 rlcMode1
, guint8 direction1
,
965 guint16 ueid2
, guint16 channelType2
, guint16 channelId2
, guint8 rlcMode2
, guint8 direction2
,
966 gboolean frameIsControl
)
968 /* Same direction, data - OK. */
969 if (!frameIsControl
) {
970 return (direction1
== direction2
) &&
972 (channelType1
== channelType2
) &&
973 (channelId1
== channelId2
) &&
974 (rlcMode1
== rlcMode2
);
977 if (frameIsControl
&& (rlcMode1
== RLC_AM_MODE
) && (rlcMode2
== RLC_AM_MODE
)) {
978 return ((direction1
!= direction2
) &&
980 (channelType1
== channelType2
) &&
981 (channelId1
== channelId2
));
989 /* Free all segments in the graph */
990 static void graph_segment_list_free(struct graph
*g
)
992 struct segment
*segment
;
994 while (g
->segments
) {
995 segment
= g
->segments
->next
;
997 g
->segments
= segment
;
1002 static void graph_element_lists_initialize(struct graph
*g
)
1004 g
->elists
= (struct element_list
*)g_malloc0(sizeof(struct element_list
));
1005 g
->elists
->elements
= NULL
;
1006 g
->elists
->next
= NULL
;
1009 static void graph_element_lists_make(struct graph
*g
)
1011 debug(DBS_FENTRY
) puts("graph_element_lists_make()");
1012 rlc_lte_make_elmtlist(g
);
1015 static void graph_element_lists_free(struct graph
*g
)
1017 struct element_list
*list
, *next_list
;
1019 for (list
=g
->elists
; list
; list
=next_list
) {
1020 g_free(list
->elements
);
1021 next_list
= list
->next
;
1024 g
->elists
= NULL
; /* just to make debugging easier */
1027 static void graph_title_pixmap_create(struct graph
*g
)
1029 #if GTK_CHECK_VERSION(2,22,0)
1030 if (g
->title_surface
){
1031 cairo_surface_destroy(g
->title_surface
);
1032 g
->title_surface
= NULL
;
1035 g
->title_surface
= gdk_window_create_similar_surface(gtk_widget_get_window(g
->drawing_area
),
1036 CAIRO_CONTENT_COLOR
,
1041 if (g
->title_pixmap
)
1042 g_object_unref(g
->title_pixmap
);
1044 g
->title_pixmap
= gdk_pixmap_new(gtk_widget_get_window(g
->drawing_area
),
1045 g
->x_axis
->p
.width
, g
->wp
.y
, -1);
1049 static void graph_title_pixmap_draw(struct graph
*g
)
1052 PangoLayout
*layout
;
1055 #if GTK_CHECK_VERSION(2,22,0)
1056 cr
= cairo_create(g
->title_surface
);
1058 cr
= gdk_cairo_create(g
->title_pixmap
);
1060 cairo_set_source_rgb(cr
, 1, 1, 1);
1061 cairo_rectangle(cr
, 0, 0, g
->x_axis
->p
.width
, g
->wp
.y
);
1064 layout
= gtk_widget_create_pango_layout(g
->drawing_area
, "");
1065 pango_layout_get_pixel_size(layout
, &w
, &h
);
1066 cairo_move_to(cr
, g
->wp
.width
/2 - w
/2, 20);
1067 pango_cairo_show_layout(cr
, layout
);
1068 g_object_unref(G_OBJECT(layout
));
1073 static void graph_title_pixmap_display(struct graph
*g
)
1077 cr
= gdk_cairo_create(gtk_widget_get_window(g
->drawing_area
));
1078 #if GTK_CHECK_VERSION(2,22,0)
1079 cairo_set_source_surface(cr
, g
->title_surface
, g
->wp
.x
, 0);
1081 gdk_cairo_set_source_pixmap(cr
, g
->title_pixmap
, g
->wp
.x
, 0);
1083 cairo_rectangle(cr
, g
->wp
.x
, 0, g
->x_axis
->p
.width
, g
->wp
.y
);
1088 static void graph_pixmaps_create(struct graph
*g
)
1090 debug(DBS_FENTRY
) puts("graph_pixmaps_create()");
1091 #if GTK_CHECK_VERSION(2,22,0)
1093 cairo_surface_destroy(g
->surface
[0]);
1094 g
->surface
[0] = NULL
;
1098 cairo_surface_destroy(g
->surface
[1]);
1099 g
->surface
[1] = NULL
;
1102 g
->surface
[0] = gdk_window_create_similar_surface(gtk_widget_get_window(g
->drawing_area
),
1103 CAIRO_CONTENT_COLOR
,
1107 g
->surface
[1] = gdk_window_create_similar_surface(gtk_widget_get_window(g
->drawing_area
),
1108 CAIRO_CONTENT_COLOR
,
1115 g_object_unref(g
->pixmap
[0]);
1117 g_object_unref(g
->pixmap
[1]);
1119 g
->pixmap
[0] = gdk_pixmap_new(gtk_widget_get_window(g
->drawing_area
),
1120 g
->wp
.width
, g
->wp
.height
, -1);
1121 g
->pixmap
[1] = gdk_pixmap_new(gtk_widget_get_window(g
->drawing_area
),
1122 g
->wp
.width
, g
->wp
.height
, -1);
1125 #endif /* GTK_CHECK_VERSION(2,22,0) */
1129 static void graph_display(struct graph
*g
)
1131 set_busy_cursor(gtk_widget_get_window(g
->drawing_area
));
1132 graph_pixmap_draw(g
);
1133 unset_busy_cursor(gtk_widget_get_window(g
->drawing_area
));
1134 graph_pixmaps_switch(g
);
1135 graph_pixmap_display(g
);
1138 static void graph_pixmap_display(struct graph
*g
)
1142 cr
= gdk_cairo_create(gtk_widget_get_window(g
->drawing_area
));
1143 #if GTK_CHECK_VERSION(2,22,0)
1144 cairo_set_source_surface(cr
, g
->surface
[g
->displayed
], g
->wp
.x
, g
->wp
.y
);
1146 gdk_cairo_set_source_pixmap(cr
, g
->pixmap
[g
->displayed
], g
->wp
.x
, g
->wp
.y
);
1147 #endif /* GTK_CHECK_VERSION(2,22,0) */
1148 cairo_rectangle(cr
, g
->wp
.x
, g
->wp
.y
, g
->wp
.width
, g
->wp
.height
);
1153 static void graph_pixmaps_switch(struct graph
*g
)
1155 g
->displayed
= 1 ^ g
->displayed
;
1158 static void graph_pixmap_draw(struct graph
*g
)
1160 struct element_list
*list
;
1164 cairo_t
*cr_elements
;
1165 GdkRGBA
*current_color
= NULL
;
1166 GdkRGBA
*color_to_set
= NULL
;
1167 gboolean line_stroked
= TRUE
;
1169 debug(DBS_FENTRY
) puts("graph_display()");
1170 not_disp
= 1 ^ g
->displayed
;
1172 #if GTK_CHECK_VERSION(2,22,0)
1173 cr
= cairo_create(g
->surface
[not_disp
]);
1175 cr
= gdk_cairo_create(g
->pixmap
[not_disp
]);
1176 #endif /* GTK_CHECK_VERSION(2,22,0) */
1177 cairo_set_source_rgb(cr
, 1, 1, 1);
1178 cairo_rectangle(cr
, 0, 0, g
->wp
.width
, g
->wp
.height
);
1182 /* Create one cairo_t for use with all of the lines, rather than continually
1183 creating and destroying one for each line */
1184 #if GTK_CHECK_VERSION(2,22,0)
1185 cr_elements
= cairo_create(g
->surface
[not_disp
]);
1187 cr_elements
= gdk_cairo_create(g
->pixmap
[not_disp
]);
1190 /* N.B. This makes drawing circles take half the time of the default setting.
1191 Changing from the default fill rule didn't make any noticeable difference
1193 cairo_set_tolerance(cr_elements
, 1.0);
1195 /* Line width is always 1 pixel */
1196 cairo_set_line_width(cr_elements
, 1.0);
1198 /* Draw all elements */
1199 for (list
=g
->elists
; list
; list
=list
->next
) {
1200 for (e
=list
->elements
; e
->type
!= ELMT_NONE
; e
++) {
1202 /* Work out if we need to change colour */
1203 if (current_color
== e
->elment_color_p
) {
1204 /* No change needed */
1205 color_to_set
= NULL
;
1208 /* Changing colour */
1209 current_color
= color_to_set
= e
->elment_color_p
;
1210 cairo_stroke(cr_elements
);
1216 draw_element_line(g
, e
, cr_elements
, color_to_set
);
1217 line_stroked
= FALSE
;
1221 if (!line_stroked
) {
1222 cairo_stroke(cr_elements
);
1223 line_stroked
= TRUE
;
1225 /* Draw the ellipse */
1226 draw_element_ellipse(g
, e
, cr_elements
, color_to_set
);
1230 /* No other element types supported at the moment */
1235 /* Make sure any remaining lines get drawn */
1236 if (!line_stroked
) {
1237 cairo_stroke(cr_elements
);
1241 cairo_destroy(cr_elements
);
1244 static void draw_element_line(struct graph
*g
, struct element
*e
, cairo_t
*cr
,
1247 int xx1
, xx2
, yy1
, yy2
;
1249 debug(DBS_GRAPH_DRAWING
)
1250 printf("\nline element: (%.2f,%.2f)->(%.2f,%.2f), seg %d ...\n",
1251 e
->p
.line
.dim
.x1
, e
->p
.line
.dim
.y1
,
1252 e
->p
.line
.dim
.x2
, e
->p
.line
.dim
.y2
, e
->parent
->num
);
1254 /* Set our new colour (if changed) */
1255 if (new_color
!= NULL
) {
1256 gdk_cairo_set_source_rgba(cr
, new_color
);
1259 /* Map point into graph area, and round to nearest int */
1260 xx1
= (int)rint(e
->p
.line
.dim
.x1
+ g
->geom
.x
- g
->wp
.x
);
1261 xx2
= (int)rint(e
->p
.line
.dim
.x2
+ g
->geom
.x
- g
->wp
.x
);
1262 yy1
= (int)rint((g
->geom
.height
-1-e
->p
.line
.dim
.y1
) + g
->geom
.y
-g
->wp
.y
);
1263 yy2
= (int)rint((g
->geom
.height
-1-e
->p
.line
.dim
.y2
) + g
->geom
.y
-g
->wp
.y
);
1265 /* If line completely out of the area, we won't show it */
1266 if (((xx1
< 0) && (xx2
< 0)) || ((xx1
>= g
->wp
.width
) && (xx2
>= g
->wp
.width
)) ||
1267 ((yy1
< 0) && (yy2
< 0)) || ((yy1
>= g
->wp
.height
) && (yy2
>= g
->wp
.height
))) {
1268 debug(DBS_GRAPH_DRAWING
) printf(" refusing: (%d,%d)->(%d,%d)\n", xx1
, yy1
, xx2
, yy2
);
1272 /* If one end of the line is out of bounds, don't worry. Cairo will
1273 clip the line to the outside of g->wp at the correct angle! */
1275 debug(DBS_GRAPH_DRAWING
) printf("line: (%d,%d)->(%d,%d)\n", xx1
, yy1
, xx2
, yy2
);
1277 /* Draw from first position to second */
1278 cairo_move_to(cr
, xx1
+0.5, yy1
+0.5);
1279 cairo_line_to(cr
, xx2
+0.5, yy2
+0.5);
1282 static void draw_element_ellipse(struct graph
*g
, struct element
*e
, cairo_t
*cr
,
1285 gdouble w
= e
->p
.ellipse
.dim
.width
;
1286 gdouble h
= e
->p
.ellipse
.dim
.height
;
1287 gdouble x
= e
->p
.ellipse
.dim
.x
+ g
->geom
.x
- g
->wp
.x
;
1288 gdouble y
= g
->geom
.height
-1 - e
->p
.ellipse
.dim
.y
+ g
->geom
.y
- g
->wp
.y
;
1290 debug(DBS_GRAPH_DRAWING
) printf ("ellipse: (x, y) -> (w, h): (%f, %f) -> (%f, %f)\n", x
, y
, w
, h
);
1292 /* Set our new colour (if changed) */
1293 if (new_color
!= NULL
) {
1294 gdk_cairo_set_source_rgba(cr
, new_color
);
1298 cairo_translate(cr
, x
+ w
/2.0, y
+ h
/2.0);
1299 cairo_scale(cr
, w
/2.0, h
/2.0);
1300 cairo_arc(cr
, 0.0, 0.0, 1.0, 0.0, 2*G_PI
);
1306 static void axis_pixmaps_create(struct axis
*axis
)
1308 debug(DBS_FENTRY
) puts("axis_pixmaps_create()");
1309 #if GTK_CHECK_VERSION(2,22,0)
1310 if (axis
->surface
[0]){
1311 cairo_surface_destroy(axis
->surface
[0]);
1312 axis
->surface
[0] = NULL
;
1314 if (axis
->surface
[1]){
1315 cairo_surface_destroy(axis
->surface
[1]);
1316 axis
->surface
[1] = NULL
;
1318 axis
->surface
[0] = gdk_window_create_similar_surface(gtk_widget_get_window(axis
->drawing_area
),
1319 CAIRO_CONTENT_COLOR
,
1323 axis
->surface
[1] = gdk_window_create_similar_surface(gtk_widget_get_window(axis
->drawing_area
),
1324 CAIRO_CONTENT_COLOR
,
1328 axis
->displayed
= 0;
1330 if (axis
->pixmap
[0])
1331 g_object_unref(axis
->pixmap
[0]);
1332 if (axis
->pixmap
[1])
1333 g_object_unref(axis
->pixmap
[1]);
1335 axis
->pixmap
[0] = gdk_pixmap_new(gtk_widget_get_window(axis
->drawing_area
),
1336 axis
->p
.width
, axis
->p
.height
, -1);
1337 axis
->pixmap
[1] = gdk_pixmap_new(gtk_widget_get_window(axis
->drawing_area
),
1338 axis
->p
.width
, axis
->p
.height
, -1);
1340 axis
->displayed
= 0;
1344 static void axis_destroy(struct axis
*axis
)
1346 #if GTK_CHECK_VERSION(2,22,0)
1347 if (axis
->surface
[0]){
1348 cairo_surface_destroy(axis
->surface
[0]);
1349 axis
->surface
[0] = NULL
;
1351 if (axis
->surface
[1]){
1352 cairo_surface_destroy(axis
->surface
[1]);
1353 axis
->surface
[1] = NULL
;
1356 g_object_unref(axis
->pixmap
[0]);
1357 g_object_unref(axis
->pixmap
[1]);
1359 g_free((gpointer
)(axis
->label
) );
1362 static void axis_display(struct axis
*axis
)
1364 if (axis
->flags
& AXIS_ORIENTATION
)
1365 h_axis_pixmap_draw(axis
);
1367 v_axis_pixmap_draw(axis
);
1369 axis_pixmaps_switch(axis
);
1370 axis_pixmap_display(axis
);
1373 /* These show sequence numbers. Avoid subdividing whole numbers. */
1374 static void v_axis_pixmap_draw(struct axis
*axis
)
1376 struct graph
*g
= axis
->g
;
1379 int not_disp
, offset
, imin
, imax
;
1380 double bottom
, top
, fl
, corr
;
1381 PangoLayout
*layout
;
1384 debug(DBS_FENTRY
) puts("v_axis_pixmap_draw()");
1386 /* Work out extent of axis */
1387 bottom
= (g
->geom
.height
- (g
->wp
.height
+ g
->wp
.y
+ (-g
->geom
.y
))) /
1388 (double )g
->geom
.height
* g
->bounds
.height
;
1389 bottom
+= axis
->min
;
1390 top
= (g
->geom
.height
- (g
->wp
.y
+ (-g
->geom
.y
))) /
1391 (double )g
->geom
.height
* g
->bounds
.height
;
1393 axis_compute_ticks(axis
, bottom
, top
, AXIS_VERTICAL
);
1395 not_disp
= 1 ^ axis
->displayed
;
1397 #if GTK_CHECK_VERSION(2,22,0)
1398 cr
= cairo_create(axis
->surface
[not_disp
]);
1400 cr
= gdk_cairo_create(axis
->pixmap
[not_disp
]);
1402 cairo_set_source_rgb(cr
, 1, 1, 1);
1403 cairo_rectangle(cr
, 0, 0, axis
->p
.width
, axis
->p
.height
);
1407 cairo_set_source_rgb(cr
, 0, 0, 0);
1408 cairo_set_line_width(cr
, 1.0);
1409 cairo_move_to(cr
, axis
->p
.width
- 1.5, (axis
->p
.height
-axis
->s
.height
)/2.0);
1410 cairo_line_to(cr
, axis
->s
.width
- 1.5, axis
->p
.height
);
1412 offset
= g
->wp
.y
+ (-g
->geom
.y
);
1413 fl
= floor(axis
->min
/ axis
->major
) * axis
->major
;
1414 corr
= rint((axis
->min
- fl
) * g
->zoom
.y
);
1417 major_tick
= axis
->major
* g
->zoom
.y
;
1418 imin
= (int) ((g
->geom
.height
- offset
+ corr
- g
->wp
.height
) / major_tick
+ 1);
1419 imax
= (int) ((g
->geom
.height
- offset
+ corr
) / major_tick
);
1420 for (i
=imin
; i
<= imax
; i
++) {
1423 int y
= (int) (g
->geom
.height
-1 - (int )rint(i
* major_tick
) -
1424 offset
+ corr
+ axis
->s
.y
);
1426 debug(DBS_AXES_DRAWING
) printf("%f @ %d\n",
1427 i
*axis
->major
+ fl
, y
);
1428 if ((y
< 0) || (y
> axis
->p
.height
))
1431 cairo_move_to(cr
, axis
->p
.width
- 15, y
+0.5);
1432 cairo_line_to(cr
, axis
->s
.width
- 1, y
+0.5);
1434 /* Won't be showing any decimal places here... */
1435 g_snprintf(desc
, sizeof(desc
), "%u", (unsigned int)(i
*axis
->major
+ fl
));
1436 layout
= gtk_widget_create_pango_layout(g
->drawing_area
, desc
);
1437 pango_layout_get_pixel_size(layout
, &w
, &h
);
1438 cairo_move_to(cr
, axis
->s
.width
-14-4-w
, y
- h
/2);
1439 pango_cairo_show_layout(cr
, layout
);
1440 g_object_unref(G_OBJECT(layout
));
1444 double minor_tick
= axis
->minor
* g
->zoom
.y
;
1445 imin
= (int) ((g
->geom
.height
- offset
+ corr
- g
->wp
.height
)/minor_tick
+ 1);
1446 imax
= (int) ((g
->geom
.height
- offset
+ corr
) / minor_tick
);
1447 for (i
=imin
; i
<= imax
; i
++) {
1448 int y
= (int) (g
->geom
.height
-1 - (int )rint(i
*minor_tick
) -
1449 offset
+ corr
+ axis
->s
.y
);
1451 if ((y
> 0) && (y
< axis
->p
.height
)) {
1452 cairo_set_line_width(cr
, 1.0);
1453 cairo_move_to(cr
, axis
->s
.width
- 8, y
+0.5);
1454 cairo_line_to(cr
, axis
->s
.width
- 1, y
+0.5);
1458 for (i
=0; axis
->label
[i
]; i
++) {
1460 layout
= gtk_widget_create_pango_layout(g
->drawing_area
,
1462 pango_layout_get_pixel_size(layout
, &w
, &h
);
1463 cairo_move_to(cr
, (axis
->p
.width
- w
)/2, TITLEBAR_HEIGHT
-10 - i
*(h
+3) - h
);
1464 pango_cairo_show_layout(cr
, layout
);
1465 g_object_unref(G_OBJECT(layout
));
1472 /* TODO: natural time units are subframes (ms), so might be good to always
1473 show 3 decimal places? */
1474 static void h_axis_pixmap_draw(struct axis
*axis
)
1476 struct graph
*g
= axis
->g
;
1478 double major_tick
, minor_tick
;
1479 int not_disp
, rdigits
, offset
, imin
, imax
;
1480 double left
, right
, j
, fl
, corr
;
1481 PangoLayout
*layout
;
1484 debug(DBS_FENTRY
) puts("h_axis_pixmap_draw()");
1485 left
= (g
->wp
.x
-g
->geom
.x
) / (double)g
->geom
.width
* g
->bounds
.width
;
1487 right
= (g
->wp
.x
-g
->geom
.x
+g
->wp
.width
) / (double)g
->geom
.width
* g
->bounds
.width
;
1489 axis_compute_ticks(axis
, left
, right
, AXIS_HORIZONTAL
);
1491 /* Work out how many decimal places should be shown */
1492 j
= axis
->major
- floor(axis
->major
);
1493 for (rdigits
=0; rdigits
<= 6; rdigits
++) {
1500 not_disp
= 1 ^ axis
->displayed
;
1502 #if GTK_CHECK_VERSION(2,22,0)
1503 cr
= cairo_create(axis
->surface
[not_disp
]);
1505 cr
= gdk_cairo_create(axis
->pixmap
[not_disp
]);
1507 cairo_set_source_rgb(cr
, 1, 1, 1);
1508 cairo_rectangle(cr
, 0, 0, axis
->p
.width
, axis
->p
.height
);
1512 cairo_set_source_rgb(cr
, 0, 0, 0);
1513 cairo_set_line_width(cr
, 1.0);
1514 cairo_move_to(cr
, 0, 0.5);
1515 cairo_line_to(cr
, axis
->s
.width
+ (axis
->p
.width
-axis
->s
.width
)/2.0, 0.5);
1517 offset
= g
->wp
.x
- g
->geom
.x
;
1519 fl
= floor(axis
->min
/ axis
->major
) * axis
->major
;
1520 corr
= rint((axis
->min
- fl
) * g
->zoom
.x
);
1523 major_tick
= axis
->major
*g
->zoom
.x
;
1524 imin
= (int) ((offset
+ corr
) / major_tick
+ 1);
1525 imax
= (int) ((offset
+ corr
+ axis
->s
.width
) / major_tick
);
1526 for (i
=imin
; i
<= imax
; i
++) {
1529 int x
= (int) (rint(i
* major_tick
) - offset
- corr
);
1531 /* printf("%f @ %d\n", i*axis->major + fl, x); */
1532 if ((x
< 0) || (x
> axis
->s
.width
))
1534 cairo_move_to(cr
, x
+0.5, 0);
1535 cairo_line_to(cr
, x
+0.5, 15);
1537 g_snprintf(desc
, sizeof(desc
), "%.*f", rdigits
, i
*axis
->major
+ fl
);
1538 layout
= gtk_widget_create_pango_layout(g
->drawing_area
, desc
);
1539 pango_layout_get_pixel_size(layout
, &w
, &h
);
1540 cairo_move_to(cr
, x
- w
/2, 15+4);
1541 pango_cairo_show_layout(cr
, layout
);
1542 g_object_unref(G_OBJECT(layout
));
1544 if (axis
->minor
> 0) {
1546 minor_tick
= axis
->minor
*g
->zoom
.x
;
1547 imin
= (int) ((offset
+ corr
) / minor_tick
+ 1);
1548 imax
= (int) ((offset
+ corr
+ g
->wp
.width
) / minor_tick
);
1549 for (i
=imin
; i
<= imax
; i
++) {
1550 int x
= (int) (rint(i
* minor_tick
) - offset
- corr
);
1551 if ((x
> 0) && (x
< axis
->s
.width
)){
1552 cairo_move_to(cr
, x
+0.5, 0);
1553 cairo_line_to(cr
, x
+0.5, 8);
1557 for (i
=0; axis
->label
[i
]; i
++) {
1559 layout
= gtk_widget_create_pango_layout(g
->drawing_area
,
1561 pango_layout_get_pixel_size(layout
, &w
, &h
);
1562 cairo_move_to(cr
, axis
->s
.width
- w
- 50, 15+h
+15 + i
*(h
+3));
1563 pango_cairo_show_layout(cr
, layout
);
1564 g_object_unref(G_OBJECT(layout
));
1571 static void axis_pixmaps_switch(struct axis
*axis
)
1573 axis
->displayed
= 1 ^ axis
->displayed
;
1576 static void axis_pixmap_display(struct axis
*axis
)
1580 cr
= gdk_cairo_create(gtk_widget_get_window(axis
->drawing_area
));
1581 #if GTK_CHECK_VERSION(2,22,0)
1582 cairo_set_source_surface(cr
, axis
->surface
[axis
->displayed
], axis
->p
.x
, axis
->p
.y
);
1584 gdk_cairo_set_source_pixmap(cr
, axis
->pixmap
[axis
->displayed
], axis
->p
.x
, axis
->p
.y
);
1586 cairo_rectangle(cr
, axis
->p
.x
, axis
->p
.y
, axis
->p
.width
, axis
->p
.height
);
1591 static void axis_compute_ticks(struct axis
*axis
, double x0
, double xmax
, int dir
)
1593 int i
, j
, ii
, jj
, ms
;
1594 double zoom
, x
, steps
[3] = { 0.1, 0.5 };
1595 int dim
, check_needed
, diminished
;
1596 double majthresh
[2] = {2.0, 3.0};
1598 debug((DBS_FENTRY
| DBS_AXES_TICKS
)) puts("axis_compute_ticks()");
1599 debug(DBS_AXES_TICKS
)
1600 printf("x0=%f xmax=%f dir=%s\n", x0
,xmax
, dir
? "VERTICAL" : "HORIZONTAL");
1602 zoom
= axis_zoom_get(axis
, dir
);
1604 for (i
=-9; i
<= 12; i
++) {
1605 if (x
/ pow(10, i
) < 1)
1609 ms
= (int )(x
/ pow(10, i
));
1619 axis
->major
= steps
[j
] * pow(10, i
);
1620 if (dir
== AXIS_VERTICAL
) {
1621 /* But don't divide further than whole sequence numbers */
1622 axis
->major
= MAX(axis
->major
, 1.0);
1625 debug(DBS_AXES_TICKS
) printf("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
1626 " axis->major=%f\n", zoom
, x
, i
, ms
, j
, axis
->major
);
1628 /* Compute minor ticks */
1631 axis_ticks_down(&ii
, &jj
);
1633 if ((dir
== AXIS_VERTICAL
) && (axis
->major
<= 1)) {
1634 /* Don't subdivide whole sequence numbers */
1638 axis
->minor
= steps
[jj
] * pow(10, ii
);
1639 /* We don't want minors if they would be less than 10 pixels apart */
1640 if (axis
->minor
*zoom
< 10) {
1641 debug(DBS_AXES_TICKS
) printf("refusing axis->minor of %f: "
1642 "axis->minor*zoom == %f\n",
1643 axis
->minor
, axis
->minor
*zoom
);
1648 check_needed
= TRUE
;
1650 while (check_needed
) {
1651 check_needed
= FALSE
;
1652 dim
= get_label_dim(axis
, dir
, xmax
);
1653 debug(DBS_AXES_TICKS
) printf("axis->major==%.1f, axis->minor==%.1f =>"
1654 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
1655 axis
->major
, axis
->minor
, axis
->major
*zoom
/dim
,
1656 axis
->minor
*zoom
/dim
);
1658 /* corrections: if majors are less than majthresh[dir] times label
1659 * dimension apart, we need to use bigger ones */
1660 if (axis
->major
*zoom
/ dim
< majthresh
[dir
]) {
1661 axis_ticks_up(&ii
, &jj
);
1662 axis
->minor
= axis
->major
;
1663 axis_ticks_up(&i
, &j
);
1664 axis
->major
= steps
[j
] * pow(10, i
);
1665 check_needed
= TRUE
;
1666 debug(DBS_AXES_TICKS
) printf("axis->major enlarged to %.1f\n",
1669 /* if minor ticks are bigger than majthresh[dir] times label dimension,
1670 * we could promote them to majors as well */
1671 if ((axis
->minor
*zoom
/ dim
> majthresh
[dir
]) && !diminished
) {
1672 axis_ticks_down(&i
, &j
);
1673 axis
->major
= axis
->minor
;
1674 axis_ticks_down(&ii
, &jj
);
1675 axis
->minor
= steps
[jj
] * pow(10, ii
);
1676 check_needed
= TRUE
;
1679 debug(DBS_AXES_TICKS
) printf("axis->minor diminished to %.1f\n",
1682 if (axis
->minor
*zoom
< 10) {
1683 debug(DBS_AXES_TICKS
) printf("refusing axis->minor of %f: "
1684 "axis->minor*zoom == %f\n", axis
->minor
, axis
->minor
*zoom
);
1690 debug(DBS_AXES_TICKS
) printf("corrected: axis->major == %.1f -> "
1691 "axis->minor == %.1f\n", axis
->major
, axis
->minor
);
1694 static void axis_ticks_up(int *i
, int *j
)
1703 static void axis_ticks_down(int *i
, int *j
)
1712 static int get_label_dim(struct axis
*axis
, int dir
, double label
)
1717 PangoLayout
*layout
;
1719 /* First, let's compute how many digits to the right of radix
1720 * we need to print */
1721 y
= axis
->major
- floor(axis
->major
);
1722 for (rdigits
=0; rdigits
<= 6; rdigits
++) {
1728 g_snprintf(str
, sizeof(str
), "%.*f", rdigits
, label
);
1730 case AXIS_HORIZONTAL
:
1731 layout
= gtk_widget_create_pango_layout(axis
->g
->drawing_area
, str
);
1732 pango_layout_get_pixel_size(layout
, &dim
, NULL
);
1733 g_object_unref(G_OBJECT(layout
));
1736 layout
= gtk_widget_create_pango_layout(axis
->g
->drawing_area
, str
);
1737 pango_layout_get_pixel_size(layout
, NULL
, &dim
);
1738 g_object_unref(G_OBJECT(layout
));
1741 puts("initialize axis: an axis must be either horizontal or vertical");
1747 static double axis_zoom_get(struct axis
*axis
, int dir
)
1750 case AXIS_HORIZONTAL
:
1751 return axis
->g
->zoom
.x
;
1753 return axis
->g
->zoom
.y
;
1759 static void graph_select_segment(struct graph
*g
, int x
, int y
)
1761 struct element_list
*list
;
1765 debug(DBS_FENTRY
) puts("graph_select_segment()");
1768 y
= g
->geom
.height
-1 - (y
- g
->geom
.y
);
1770 set_busy_cursor(gtk_widget_get_window(g
->drawing_area
));
1772 for (list
=g
->elists
; list
; list
=list
->next
) {
1773 for (e
=list
->elements
; e
->type
!= ELMT_NONE
; e
++) {
1776 if (line_detect_collision(e
, x
, y
)) {
1777 num
= e
->parent
->num
;
1781 if (ellipse_detect_collision(e
, x
, y
)) {
1782 num
= e
->parent
->num
;
1794 cf_goto_frame(&cfile
, num
);
1798 static int line_detect_collision(struct element
*e
, int x
, int y
)
1800 int xx1
, yy1
, xx2
, yy2
;
1802 /* Get sorted x, y co-ordinates for line */
1803 if (e
->p
.line
.dim
.x1
< e
->p
.line
.dim
.x2
) {
1804 xx1
= (int)rint(e
->p
.line
.dim
.x1
);
1805 xx2
= (int)rint(e
->p
.line
.dim
.x2
);
1807 xx1
= (int)rint(e
->p
.line
.dim
.x2
);
1808 xx2
= (int)rint(e
->p
.line
.dim
.x1
);
1810 if (e
->p
.line
.dim
.y1
< e
->p
.line
.dim
.y2
) {
1811 yy1
= (int)rint(e
->p
.line
.dim
.y1
);
1812 yy2
= (int)rint(e
->p
.line
.dim
.y2
);
1814 yy1
= (int)rint(e
->p
.line
.dim
.y2
);
1815 yy2
= (int)rint(e
->p
.line
.dim
.y1
);
1818 printf("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
1821 /* N.B. won't match with diagonal lines... */
1822 if (((xx1
== x
) && (xx2
== x
) && (yy1
<= y
) && (y
<= yy2
) )| /* lies along vertical line */
1823 ((yy1
== y
) && (yy2
== y
) && (xx1
<= x
) && (x
<= xx2
))) { /* lies along horizontal line */
1831 static int ellipse_detect_collision(struct element
*e
, int x
, int y
)
1833 int xx1
, yy1
, xx2
, yy2
;
1835 xx1
= (int )rint (e
->p
.ellipse
.dim
.x
);
1836 xx2
= (int )rint (e
->p
.ellipse
.dim
.x
+ e
->p
.ellipse
.dim
.width
);
1837 yy1
= (int )rint (e
->p
.ellipse
.dim
.y
- e
->p
.ellipse
.dim
.height
);
1838 yy2
= (int )rint (e
->p
.ellipse
.dim
.y
);
1840 printf ("ellipse: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
1842 if ((xx1
<= x
) && (x
<= xx2
) && (yy1
<= y
) && (y
<= yy2
)) {
1852 static gboolean
configure_event(GtkWidget
*widget _U_
, GdkEventConfigure
*event
, gpointer user_data
)
1854 struct graph
*g
= (struct graph
*)user_data
;
1855 struct zoom new_zoom
;
1856 int cur_g_width
, cur_g_height
;
1857 int cur_wp_width
, cur_wp_height
;
1859 debug(DBS_FENTRY
) puts("configure_event()");
1861 cur_wp_width
= g
->wp
.width
;
1862 cur_wp_height
= g
->wp
.height
;
1863 g
->wp
.width
= event
->width
- g
->y_axis
->p
.width
- RMARGIN_WIDTH
;
1864 g
->wp
.height
= event
->height
- g
->x_axis
->p
.height
- g
->wp
.y
;
1865 g
->x_axis
->s
.width
= g
->wp
.width
;
1866 g
->x_axis
->p
.width
= g
->wp
.width
+ RMARGIN_WIDTH
;
1867 g
->y_axis
->p
.height
= g
->wp
.height
+ g
->wp
.y
;
1868 g
->y_axis
->s
.height
= g
->wp
.height
;
1869 g
->x_axis
->p
.y
= g
->y_axis
->p
.height
;
1870 new_zoom
.x
= (double)g
->wp
.width
/ cur_wp_width
;
1871 new_zoom
.y
= (double)g
->wp
.height
/ cur_wp_height
;
1872 cur_g_width
= g
->geom
.width
;
1873 cur_g_height
= g
->geom
.height
;
1874 g
->geom
.width
= (int)rint(g
->geom
.width
* new_zoom
.x
);
1875 g
->geom
.height
= (int)rint(g
->geom
.height
* new_zoom
.y
);
1876 g
->zoom
.x
= (double)(g
->geom
.width
- 1) / g
->bounds
.width
;
1877 g
->zoom
.y
= (double)(g
->geom
.height
-1) / g
->bounds
.height
;
1879 g
->geom
.x
= (int)(g
->wp
.x
- (double)g
->geom
.width
/cur_g_width
* (g
->wp
.x
- g
->geom
.x
));
1880 g
->geom
.y
= (int)(g
->wp
.y
- (double)g
->geom
.height
/cur_g_height
* (g
->wp
.y
- g
->geom
.y
));
1882 printf("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
1883 "zooms: (%f,%f)\n", g
->geom
.x
, g
->geom
.y
, g
->geom
.width
,
1884 g
->geom
.height
, g
->wp
.x
, g
->wp
.y
, g
->wp
.width
, g
->wp
.height
,
1885 g
->zoom
.x
, g
->zoom
.y
);
1888 graph_element_lists_make(g
);
1889 graph_pixmaps_create(g
);
1890 graph_title_pixmap_create(g
);
1891 axis_pixmaps_create(g
->y_axis
);
1892 axis_pixmaps_create(g
->x_axis
);
1893 /* we don't do actual drawing here; we leave it to expose handler */
1894 graph_pixmap_draw(g
);
1895 graph_pixmaps_switch(g
);
1896 graph_title_pixmap_draw(g
);
1897 h_axis_pixmap_draw(g
->x_axis
);
1898 axis_pixmaps_switch(g
->x_axis
);
1899 v_axis_pixmap_draw(g
->y_axis
);
1900 axis_pixmaps_switch(g
->y_axis
);
1906 #if GTK_CHECK_VERSION(3,0,0)
1908 draw_event(GtkWidget
*widget _U_
, cairo_t
*cr
, gpointer user_data
)
1910 struct graph
*g
= (struct graph
*)user_data
;
1912 debug(DBS_FENTRY
) puts("draw_event()");
1914 /* lower left corner */
1915 cairo_set_source_rgb(cr
, 1, 1, 1);
1916 cairo_rectangle(cr
, 0, g
->wp
.y
+ g
->wp
.height
, g
->y_axis
->p
.width
, g
->x_axis
->p
.height
);
1920 cairo_rectangle(cr
, g
->wp
.x
+ g
->wp
.width
, g
->wp
.y
, RMARGIN_WIDTH
, g
->wp
.height
);
1923 /* Should these routines be copied here, or be given the cairo_t ?? */
1924 graph_pixmap_display(g
);
1925 graph_title_pixmap_display(g
);
1926 axis_pixmap_display(g
->x_axis
);
1927 axis_pixmap_display(g
->y_axis
);
1932 static gboolean
expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
1934 struct graph
*g
= (struct graph
*)user_data
;
1937 debug(DBS_FENTRY
) puts("expose_event()");
1942 /* lower left corner */
1943 cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
1944 cairo_set_source_rgb(cr
, 1, 1, 1);
1945 cairo_rectangle(cr
, 0, g
->wp
.y
+ g
->wp
.height
, g
->y_axis
->p
.width
, g
->x_axis
->p
.height
);
1951 cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
1952 cairo_set_source_rgb(cr
, 1, 1, 1);
1953 cairo_rectangle(cr
, g
->wp
.x
+ g
->wp
.width
, g
->wp
.y
, RMARGIN_WIDTH
, g
->wp
.height
);
1958 graph_pixmap_display(g
);
1959 graph_title_pixmap_display(g
);
1960 axis_pixmap_display(g
->x_axis
);
1961 axis_pixmap_display(g
->y_axis
);
1969 perform_zoom(struct graph
*g
, struct zoomfactor
*zf
,
1970 int origin_x
, int origin_y
)
1972 int cur_width
= g
->geom
.width
, cur_height
= g
->geom
.height
;
1974 /* Multiply by x and y factors */
1975 g
->geom
.width
= (int )rint(g
->geom
.width
* zf
->x
);
1976 g
->geom
.height
= (int )rint(g
->geom
.height
* zf
->y
);
1978 /* If already fully-zoomed out, don't waste time re-drawing */
1979 if ((g
->geom
.width
<= g
->wp
.width
) &&
1980 (g
->geom
.height
<= g
->wp
.height
)) {
1984 /* Don't go out of bounds */
1985 if (g
->geom
.width
< g
->wp
.width
) {
1986 g
->geom
.width
= g
->wp
.width
;
1988 if (g
->geom
.height
< g
->wp
.height
) {
1989 g
->geom
.height
= g
->wp
.height
;
1992 /* Divide to work out new zoom */
1993 g
->zoom
.x
= (g
->geom
.width
- 1) / g
->bounds
.width
;
1994 g
->zoom
.y
= (g
->geom
.height
- 1) / g
->bounds
.height
;
1996 /* Move origin to keep mouse position at centre of view */
1997 g
->geom
.x
-= (int)rint((g
->geom
.width
- cur_width
) *
1998 ((origin_x
- g
->geom
.x
)/(double )cur_width
));
1999 g
->geom
.y
-= (int)rint((g
->geom
.height
- cur_height
) *
2000 ((origin_y
- g
->geom
.y
)/(double )cur_height
));
2002 /* Again, don't go out of bounds */
2003 if (g
->geom
.x
> g
->wp
.x
)
2004 g
->geom
.x
= g
->wp
.x
;
2005 if (g
->geom
.y
> g
->wp
.y
)
2006 g
->geom
.y
= g
->wp
.y
;
2007 if (g
->wp
.x
+ g
->wp
.width
> g
->geom
.x
+ g
->geom
.width
)
2008 g
->geom
.x
= g
->wp
.width
+ g
->wp
.x
- g
->geom
.width
;
2009 if (g
->wp
.y
+ g
->wp
.height
> g
->geom
.y
+ g
->geom
.height
)
2010 g
->geom
.y
= g
->wp
.height
+ g
->wp
.y
- g
->geom
.height
;
2014 do_zoom_rectangle(struct graph
*g
, struct irect lcl_zoomrect
)
2016 int cur_width
= g
->wp
.width
, cur_height
= g
->wp
.height
;
2017 /* Make copy of geom1 before working out zoom */
2018 struct irect geom1
= g
->geom
;
2019 struct zoomfactor factor
;
2021 /* Left hand too much to the right */
2022 if (lcl_zoomrect
.x
> g
->wp
.x
+ g
->wp
.width
)
2024 /* Right hand not far enough */
2025 if (lcl_zoomrect
.x
+ lcl_zoomrect
.width
< g
->wp
.x
)
2027 /* Left hand too much to the left */
2028 if (lcl_zoomrect
.x
< g
->wp
.x
) {
2029 int dx
= g
->wp
.x
- lcl_zoomrect
.x
;
2030 lcl_zoomrect
.x
+= dx
;
2031 lcl_zoomrect
.width
-= dx
;
2033 /* Right hand too much to the right */
2034 if (lcl_zoomrect
.x
+ lcl_zoomrect
.width
> g
->wp
.x
+ g
->wp
.width
) {
2035 int dx
= lcl_zoomrect
.width
+ lcl_zoomrect
.x
- g
->wp
.x
- g
->wp
.width
;
2036 lcl_zoomrect
.width
-= dx
;
2040 if (lcl_zoomrect
.y
> g
->wp
.y
+ g
->wp
.height
)
2042 /* Bottom too high */
2043 if (lcl_zoomrect
.y
+ lcl_zoomrect
.height
< g
->wp
.y
)
2046 if (lcl_zoomrect
.y
< g
->wp
.y
) {
2047 int dy
= g
->wp
.y
- lcl_zoomrect
.y
;
2048 lcl_zoomrect
.y
+= dy
;
2049 lcl_zoomrect
.height
-= dy
;
2051 /* Bottom too low */
2052 if (lcl_zoomrect
.y
+ lcl_zoomrect
.height
> g
->wp
.y
+ g
->wp
.height
) {
2053 int dy
= lcl_zoomrect
.height
+ lcl_zoomrect
.y
- g
->wp
.y
- g
->wp
.height
;
2054 lcl_zoomrect
.height
-= dy
;
2059 "\tgeom: (%d, %d)+(%d x %d)\n"
2062 factor
.x
= (double)cur_width
/ lcl_zoomrect
.width
;
2063 factor
.y
= (double)cur_height
/ lcl_zoomrect
.height
;
2066 printf("Zoomfactor: %f x %f\n", factor.x, factor.y);
2068 /* Work out new geom settings and zoom factor */
2069 perform_zoom(g
, &factor
,
2070 lcl_zoomrect
.x
, lcl_zoomrect
.y
);
2074 "\tgeom: (%d, %d)+(%d x %d)\n"
2075 "\twp: (%d, %d)+(%d x %d)\n"
2076 "\tzoomrect: (%d, %d)+(%d x %d)\n",
2077 g->geom.x, g->geom.y,
2078 g->geom.width, g->geom.height,
2079 g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2080 lcl_zoomrect.x, lcl_zoomrect.y, lcl_zoomrect.width, lcl_zoomrect.height);
2083 /* Final geom settings are in terms of old geom, zoomreect and zoom factor */
2084 g
->geom
.x
= (int)(geom1
.x
* (1 + factor
.x
) -
2085 lcl_zoomrect
.x
* factor
.x
- (geom1
.x
- g
->wp
.x
));
2086 g
->geom
.y
= (int)(geom1
.y
* (1 + factor
.y
) -
2087 lcl_zoomrect
.y
* factor
.y
- (geom1
.y
- g
->wp
.y
));
2091 "\tgeom: (%d, %d)+(%d x %d)\n"
2092 "\twp: (%d, %d)+(%d x %d)\n"
2093 "\tzoomrect: (%d, %d)+(%d x %d)\n",
2094 g->geom.x, g->geom.y,
2095 g->geom.width, g->geom.height,
2096 g->wp.x, g->wp.y, g->wp.width, g->wp.height,
2097 lcl_zoomrect.x, lcl_zoomrect.y, lcl_zoomrect.width, lcl_zoomrect.height);
2101 graph_element_lists_make(g
);
2102 g
->cross
.erase_needed
= FALSE
;
2104 axis_display(g
->y_axis
);
2105 axis_display(g
->x_axis
);
2109 /* Zoom because of keyboard or mouse press */
2110 static void do_zoom_common(struct graph
*g
, GdkEventButton
*event
,
2111 gboolean lock_vertical
, gboolean lock_horizontal
)
2113 int cur_width
= g
->geom
.width
, cur_height
= g
->geom
.height
;
2114 struct { double x
, y
; } factor
;
2115 int pointer_x
, pointer_y
;
2117 /* Get mouse position */
2118 if (event
== NULL
) {
2119 /* Keyboard - query it */
2120 get_mouse_position(g
->drawing_area
, &pointer_x
, &pointer_y
, NULL
);
2123 /* Mouse - just read it from event */
2124 pointer_x
= (int)event
->x
;
2125 pointer_y
= (int)event
->y
;
2129 if (g
->zoom
.flags
& ZOOM_OUT
) {
2131 /* If can't zoom out anymore so don't waste time redrawing the whole graph! */
2132 if ((g
->geom
.height
<= g
->wp
.height
) &&
2133 (g
->geom
.width
<= g
->wp
.width
)) {
2138 if (lock_horizontal
) {
2142 factor
.x
= 1 / g
->zoom
.step_x
;
2145 if (lock_vertical
) {
2149 factor
.y
= 1 / g
->zoom
.step_y
;
2153 if ((lock_horizontal
) || (g
->geom
.width
>= (g
->bounds
.width
* MAX_PIXELS_PER_SECOND
))) {
2157 factor
.x
= g
->zoom
.step_x
;
2160 /* Don't zoom in too far vertically */
2161 if (lock_vertical
|| (g
->geom
.height
>= (g
->bounds
.height
* MAX_PIXELS_PER_SN
))) {
2165 factor
.y
= g
->zoom
.step_y
;
2169 /* Multiply by x and y factors */
2170 g
->geom
.width
= (int )rint(g
->geom
.width
* factor
.x
);
2171 g
->geom
.height
= (int )rint(g
->geom
.height
* factor
.y
);
2173 /* Clip to space if necessary */
2174 if (g
->geom
.width
< g
->wp
.width
)
2175 g
->geom
.width
= g
->wp
.width
;
2176 if (g
->geom
.height
< g
->wp
.height
)
2177 g
->geom
.height
= g
->wp
.height
;
2179 /* Work out new zoom */
2180 g
->zoom
.x
= (g
->geom
.width
- 1) / g
->bounds
.width
;
2181 g
->zoom
.y
= (g
->geom
.height
- 1) / g
->bounds
.height
;
2183 /* Move origin to keep mouse position at centre of view */
2184 g
->geom
.x
-= (int )rint((g
->geom
.width
- cur_width
) *
2185 ((pointer_x
- g
->geom
.x
)/(double)cur_width
));
2186 g
->geom
.y
-= (int )rint((g
->geom
.height
- cur_height
) *
2187 ((pointer_y
- g
->geom
.y
)/(double)cur_height
));
2189 /* Make sure we haven't moved outside the whole graph */
2190 if (g
->geom
.x
> g
->wp
.x
)
2191 g
->geom
.x
= g
->wp
.x
;
2192 if (g
->geom
.y
> g
->wp
.y
)
2193 g
->geom
.y
= g
->wp
.y
;
2194 if (g
->wp
.x
+ g
->wp
.width
> g
->geom
.x
+ g
->geom
.width
)
2195 g
->geom
.x
= g
->wp
.width
+ g
->wp
.x
- g
->geom
.width
;
2196 if (g
->wp
.y
+ g
->wp
.height
> g
->geom
.y
+ g
->geom
.height
)
2197 g
->geom
.y
= g
->wp
.height
+ g
->wp
.y
- g
->geom
.height
;
2199 printf("%s press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
2200 "(%d,%d); zooms: (%f,%f)\n",
2201 (event
!= NULL
) ? "mouse" : "key", g
->geom
.x
, g
->geom
.y
,
2202 g
->geom
.width
, g
->geom
.height
, g
->wp
.x
, g
->wp
.y
, g
->wp
.width
,
2203 g
->wp
.height
, g
->zoom
.x
, g
->zoom
.y
);
2206 graph_element_lists_make(g
);
2208 axis_display(g
->y_axis
);
2209 axis_display(g
->x_axis
);
2211 if (g
->cross
.draw
) {
2212 g
->cross
.erase_needed
= FALSE
;
2213 cross_draw(g
, pointer_x
, pointer_y
);
2218 static void do_zoom_keyboard(struct graph
*g
,
2219 gboolean lock_vertical
,
2220 gboolean lock_horizontal
)
2222 do_zoom_common(g
, NULL
, lock_vertical
, lock_horizontal
);
2225 static void do_zoom_mouse(struct graph
*g
, GdkEventButton
*event
)
2227 do_zoom_common(g
, event
,
2228 event
->state
& GDK_SHIFT_MASK
,
2229 event
->state
& GDK_CONTROL_MASK
);
2232 static void do_zoom_in_keyboard(struct graph
*g
,
2233 gboolean lock_vertical
,
2234 gboolean lock_horizontal
)
2236 g
->zoom
.flags
&= ~ZOOM_OUT
;
2237 do_zoom_keyboard(g
, lock_vertical
, lock_horizontal
);
2240 static void do_zoom_out_keyboard(struct graph
*g
,
2241 gboolean lock_vertical
,
2242 gboolean lock_horizontal
)
2244 g
->zoom
.flags
|= ZOOM_OUT
;
2245 do_zoom_keyboard(g
, lock_vertical
, lock_horizontal
);
2248 static void do_key_motion(struct graph
*g
)
2250 if (g
->geom
.x
> g
->wp
.x
) {
2251 g
->geom
.x
= g
->wp
.x
;
2253 if (g
->geom
.y
> g
->wp
.y
) {
2254 g
->geom
.y
= g
->wp
.y
;
2256 if ((g
->wp
.x
+ g
->wp
.width
) > (g
->geom
.x
+ g
->geom
.width
)) {
2257 g
->geom
.x
= g
->wp
.width
+ g
->wp
.x
- g
->geom
.width
;
2259 if ((g
->wp
.y
+ g
->wp
.height
) > (g
->geom
.y
+ g
->geom
.height
)) {
2260 g
->geom
.y
= g
->wp
.height
+ g
->wp
.y
- g
->geom
.height
;
2264 axis_display(g
->y_axis
);
2265 axis_display(g
->x_axis
);
2267 if (g
->cross
.draw
) {
2268 int pointer_x
, pointer_y
;
2269 get_mouse_position(g
->drawing_area
, &pointer_x
, &pointer_y
, NULL
);
2270 g
->cross
.erase_needed
= FALSE
;
2271 cross_draw (g
, pointer_x
, pointer_y
);
2275 static void do_key_motion_up(struct graph
*g
, int step
)
2281 static void do_key_motion_down(struct graph
*g
, int step
)
2287 static void do_key_motion_left(struct graph
*g
, int step
)
2293 static void do_key_motion_right(struct graph
*g
, int step
)
2299 static gboolean
button_press_event(GtkWidget
*widget _U_
, GdkEventButton
*event
, gpointer user_data
)
2301 struct graph
*g
= (struct graph
*)user_data
;
2303 debug(DBS_FENTRY
) puts("button_press_event()");
2305 if (event
->button
== MOUSE_BUTTON_RIGHT
) {
2306 /* Turn on grab. N.B. using (maybe) approx mouse position from event... */
2307 g
->grab
.x
= (int )rint (event
->x
) - g
->geom
.x
;
2308 g
->grab
.y
= (int )rint (event
->y
) - g
->geom
.y
;
2309 g
->grab
.grabbed
= TRUE
;
2310 } else if (event
->button
== MOUSE_BUTTON_MIDDLE
) {
2311 do_zoom_mouse(g
, event
);
2312 } else if (event
->button
== MOUSE_BUTTON_LEFT
) {
2313 /* See if we're on an element that links to a frame */
2314 graph_select_segment(g
, (int)event
->x
, (int)event
->y
);
2316 /* Set origin of rect, even if outside graph area */
2317 zoomrect
.x
= (int)event
->x
;
2318 zoomrect
.y
= (int)event
->y
;
2321 unset_busy_cursor(gtk_widget_get_window(g
->drawing_area
));
2325 static gboolean
button_release_event(GtkWidget
*widget _U_
, GdkEventButton
*event _U_
, gpointer user_data
)
2327 struct graph
*g
= (struct graph
*)user_data
;
2329 /* Turn off grab if right button released */
2330 if (event
->button
== MOUSE_BUTTON_RIGHT
) {
2331 g
->grab
.grabbed
= FALSE
;
2333 else if (event
->button
== MOUSE_BUTTON_LEFT
) {
2334 int xx1
= zoomrect
.x
;
2335 int xx2
= (int)event
->x
;
2336 int yy1
= zoomrect
.y
;
2337 int yy2
= (int)event
->y
;
2338 zoomrect
.x
= MIN(xx1
, xx2
);
2339 zoomrect
.width
= abs(xx1
- xx2
);
2340 zoomrect
.y
= MIN(yy1
, yy2
);
2341 zoomrect
.height
= abs(yy1
- yy2
);
2343 /* Finish selecting a region to zoom in on.
2344 Take care not to choose a too-small area (by accident?) */
2345 if ((zoomrect
.width
> 3) && (zoomrect
.height
> 3)) {
2346 int oldflags
= g
->zoom
.flags
;
2348 debug(DBS_GRAPH_DRAWING
) printf("Zoom in from (%d, %d) - (%d, %d)\n",
2349 zoomrect
.x
, zoomrect
.y
,
2350 zoomrect
.width
, zoomrect
.height
);
2352 g
->zoom
.flags
&= ~ZOOM_OUT
;
2353 do_zoom_rectangle(g
, zoomrect
);
2354 g
->zoom
.flags
= oldflags
;
2361 static gboolean
motion_notify_event(GtkWidget
*widget _U_
, GdkEventMotion
*event
, gpointer user_data
)
2363 struct graph
*g
= (struct graph
*)user_data
;
2365 GdkModifierType state
;
2367 /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
2369 /* Make sure we have accurate mouse position */
2371 get_mouse_position(g
->drawing_area
, &x
, &y
, &state
);
2375 state
= (GdkModifierType
)event
->state
;
2378 if (state
& GDK_BUTTON3_MASK
) {
2379 if (g
->grab
.grabbed
) {
2380 /* Move view by difference between where we grabbed and where we are now */
2381 g
->geom
.x
= x
-g
->grab
.x
;
2382 g
->geom
.y
= y
-g
->grab
.y
;
2384 /* Limit to outer bounds of graph */
2385 if (g
->geom
.x
> g
->wp
.x
)
2386 g
->geom
.x
= g
->wp
.x
;
2387 if (g
->geom
.y
> g
->wp
.y
)
2388 g
->geom
.y
= g
->wp
.y
;
2389 if (g
->wp
.x
+ g
->wp
.width
> g
->geom
.x
+ g
->geom
.width
)
2390 g
->geom
.x
= g
->wp
.width
+ g
->wp
.x
- g
->geom
.width
;
2391 if (g
->wp
.y
+ g
->wp
.height
> g
->geom
.y
+ g
->geom
.height
)
2392 g
->geom
.y
= g
->wp
.height
+ g
->wp
.y
- g
->geom
.height
;
2394 /* Redraw everything */
2395 g
->cross
.erase_needed
= 0;
2397 axis_display(g
->y_axis
);
2398 axis_display(g
->x_axis
);
2399 if (g
->cross
.draw
) {
2400 cross_draw(g
, x
, y
);
2404 else if (state
& GDK_BUTTON1_MASK
) {
2406 /* Draw bounded box for zoomrect being chosen! */
2407 if (g
->zoomrect_erase_needed
) {
2410 zoomrect_draw(g
, x
, y
);
2413 /* No button currently pressed */
2415 /* Update the cross if it's being shown */
2416 if (g
->cross
.erase_needed
)
2418 if (g
->cross
.draw
) {
2419 cross_draw(g
, x
, y
);
2426 static gboolean
key_press_event(GtkWidget
*widget _U_
, GdkEventKey
*event
, gpointer user_data
)
2428 struct graph
*g
= (struct graph
*)user_data
;
2431 debug(DBS_FENTRY
) puts("key_press_event()");
2433 /* Holding down these keys can affect the step used for moving */
2434 if ((event
->state
& GDK_CONTROL_MASK
) && (event
->state
& GDK_SHIFT_MASK
)) {
2437 else if (event
->state
& GDK_CONTROL_MASK
) {
2440 else if (event
->state
& GDK_SHIFT_MASK
) {
2447 switch (event
->keyval
) {
2449 toggle_crosshairs(g
);
2452 /* Toggle betwee showing the time starting at 0, or time in capture */
2453 toggle_time_origin(g
);
2457 /* Go back to original view, all zoomed out */
2458 restore_initial_graph_view(g
);
2464 do_zoom_in_keyboard(g
,
2465 event
->state
& GDK_SHIFT_MASK
,
2466 event
->state
& GDK_CONTROL_MASK
);
2469 do_zoom_in_keyboard(g
,
2471 event
->state
& GDK_CONTROL_MASK
);
2477 do_zoom_out_keyboard(g
,
2478 event
->state
& GDK_SHIFT_MASK
,
2479 event
->state
& GDK_CONTROL_MASK
);
2482 do_zoom_out_keyboard(g
,
2484 event
->state
& GDK_CONTROL_MASK
);
2486 /* Direction keys */
2488 do_key_motion_left(g
, step
);
2491 do_key_motion_up(g
, step
);
2494 do_key_motion_right(g
, step
);
2497 do_key_motion_down(g
, step
);
2501 do_key_motion_up(g
, 2000);
2504 do_key_motion_down(g
, 2000);
2509 callback_create_help(NULL
, NULL
);
2518 static void toggle_crosshairs(struct graph
*g
)
2523 /* Draw or erase as needed */
2524 if (g
->cross
.draw
) {
2526 get_mouse_position(g
->drawing_area
, &x
, &y
, NULL
);
2527 cross_draw(g
, x
, y
);
2528 } else if (g
->cross
.erase_needed
) {
2533 static void cross_draw(struct graph
*g
, int x
, int y
)
2535 /* Shouldn't draw twice onto the same position if haven't erased in the
2537 if (g
->cross
.erase_needed
&& (g
->cross
.x
== x
) && (g
->cross
.y
== y
)) {
2541 /* Draw the cross */
2542 if ((x
> g
->wp
.x
) && (x
< g
->wp
.x
+g
->wp
.width
) &&
2543 (y
> g
->wp
.y
) && (y
< g
->wp
.y
+g
->wp
.height
)) {
2545 cairo_t
*cr
= gdk_cairo_create(gtk_widget_get_window(g
->drawing_area
));
2546 gdk_cairo_set_source_rgba(cr
, &g
->style
.seq_color
);
2547 cairo_set_line_width(cr
, 1.0);
2549 /* Horizonal line */
2550 cairo_move_to(cr
, g
->wp
.x
, y
);
2551 cairo_line_to(cr
, g
->wp
.x
+ g
->wp
.width
, y
);
2554 cairo_move_to(cr
, x
, g
->wp
.y
);
2555 cairo_line_to(cr
, x
, g
->wp
.y
+ g
->wp
.height
);
2563 g
->cross
.erase_needed
= TRUE
;
2566 static void cross_erase(struct graph
*g
)
2571 if ((x
> g
->wp
.x
) && (x
< g
->wp
.x
+g
->wp
.width
) &&
2572 (y
>= g
->wp
.y
) && (y
< g
->wp
.y
+g
->wp
.height
)) {
2574 /* Just redraw what is in the pixmap buffer */
2575 graph_pixmap_display(g
);
2578 g
->cross
.erase_needed
= FALSE
;
2581 static void zoomrect_draw(struct graph
*g
, int x
, int y
)
2583 if ((zoomrect
.x
> g
->wp
.x
) && (zoomrect
.x
< g
->wp
.x
+ g
->wp
.width
) &&
2584 (zoomrect
.y
> g
->wp
.y
) && (zoomrect
.y
< g
->wp
.y
+ g
->wp
.height
) &&
2585 (x
> g
->wp
.x
+ 0.5) && (x
< g
->wp
.x
+g
->wp
.width
) &&
2586 (y
> g
->wp
.y
) && (y
< g
->wp
.y
+g
->wp
.height
)) {
2588 cairo_t
*cr
= gdk_cairo_create(gtk_widget_get_window(g
->drawing_area
));
2589 gdk_cairo_set_source_rgba(cr
, &g
->style
.seq_color
);
2590 cairo_set_line_width(cr
, 1.0);
2592 /* Do outline of rect */
2593 cairo_rectangle(cr
, zoomrect
.x
, zoomrect
.y
, x
-zoomrect
.x
, y
-zoomrect
.y
);
2598 g
->zoomrect_erase_needed
= TRUE
;
2601 static void zoomrect_erase(struct graph
*g
)
2603 /* Just redraw what is in the pixmap buffer */
2604 graph_pixmap_display(g
);
2605 g
->zoomrect_erase_needed
= FALSE
;
2609 /* Toggle between showing the time starting at 0, or time in capture */
2610 static void toggle_time_origin(struct graph
*g
)
2612 g
->style
.flags
^= TIME_ORIGIN
;
2614 if ((g
->style
.flags
& TIME_ORIGIN
) == TIME_ORIGIN_CAP
) {
2615 g
->x_axis
->min
= g
->bounds
.x0
;
2621 /* Redraw the axis */
2622 axis_display(g
->x_axis
);
2625 static void restore_initial_graph_view(struct graph
*g
)
2627 g
->geom
.width
= g
->wp
.width
;
2628 g
->geom
.height
= g
->wp
.height
;
2629 g
->geom
.x
= g
->wp
.x
;
2630 g
->geom
.y
= g
->wp
.y
;
2631 graph_init_sequence(g
);
2633 /* Set flags so that mouse zoom will zoom in (zooming out is not possible!) */
2634 g
->zoom
.flags
&= ~ZOOM_OUT
;
2636 if (g
->cross
.draw
) {
2637 g
->cross
.erase_needed
= FALSE
;
2641 /* Walk the segment list, totalling up data PDUs, status ACKs and NACKs */
2642 static void get_data_control_counts(struct graph
*g
, int *data
, int *acks
, int *nacks
)
2644 struct segment
*tmp
;
2649 for (tmp
=g
->segments
; tmp
; tmp
=tmp
->next
) {
2650 if (tmp
->isControlPDU
) {
2652 (*nacks
) += tmp
->noOfNACKs
;
2660 /* Determine "bounds"
2661 * Essentially: look for lowest/highest time and seq in the list of segments
2662 * Not currently trying to work out the upper bound of the window, as we
2663 * don't reliably know the RLC channel state variables...
2665 static void graph_get_bounds(struct graph
*g
)
2667 struct segment
*tmp
;
2669 gboolean data_frame_seen
= FALSE
;
2670 double data_tim_low
= 0;
2671 double data_tim_high
= 0;
2672 guint32 data_seq_cur
;
2673 guint32 data_seq_low
= 0;
2674 guint32 data_seq_high
= 0;
2675 gboolean ack_frame_seen
= FALSE
;
2677 double ack_tim_low
= 0;
2678 double ack_tim_high
= 0;
2679 guint32 ack_seq_cur
;
2680 guint32 ack_seq_low
= 0;
2681 guint32 ack_seq_high
= 0;
2683 /* Go through all segments to determine "bounds" */
2684 for (tmp
=g
->segments
; tmp
; tmp
=tmp
->next
) {
2685 if (!tmp
->isControlPDU
) {
2688 tim
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
2689 data_seq_cur
= tmp
->SN
;
2691 /* Want to include a little beyond end, so that cross on last SN
2692 will (more likely) fit within bounds */
2693 #define A_FEW_SUBFRAMES 0.005
2695 /* Initialise if first time seen */
2696 if (!data_frame_seen
) {
2698 data_tim_high
= tim
+ A_FEW_SUBFRAMES
;
2699 data_seq_low
= data_seq_cur
;
2700 data_seq_high
= data_seq_cur
+1;
2701 data_frame_seen
= TRUE
;
2704 /* Update bounds after this frame */
2705 if (tim
< data_tim_low
) data_tim_low
= tim
;
2706 if (tim
+0.02 > data_tim_high
) data_tim_high
= tim
+ A_FEW_SUBFRAMES
;
2707 if (data_seq_cur
< data_seq_low
) data_seq_low
= data_seq_cur
;
2708 if (data_seq_cur
+1 > data_seq_high
) data_seq_high
= data_seq_cur
+1;
2714 guint32 nack_seq_cur
;
2716 tim
= tmp
->rel_secs
+ tmp
->rel_usecs
/ 1000000.0;
2717 ack_seq_cur
= tmp
->ACKNo
;
2719 /* Initialise if first status PDU seen */
2720 if (!ack_frame_seen
) {
2721 ack_tim_low
= ack_tim_high
= tim
;
2722 ack_seq_low
= ack_seq_cur
;
2723 ack_seq_high
= ack_seq_cur
;
2724 ack_frame_seen
= TRUE
;
2727 /* Update bounds after this frame */
2728 if (tim
< ack_tim_low
) ack_tim_low
= tim
;
2729 if (tim
> ack_tim_high
) ack_tim_high
= tim
;
2730 if (ack_seq_cur
< ack_seq_low
) ack_seq_low
= ack_seq_cur
;
2731 if (ack_seq_cur
> ack_seq_high
) ack_seq_high
= ack_seq_cur
;
2733 /* Also run through any/all NACKs to see if ack_seq_low/ack_seq_high
2734 should be extended */
2735 for (n
=0; n
< tmp
->noOfNACKs
; n
++) {
2736 nack_seq_cur
= tmp
->NACKs
[n
];
2738 if (nack_seq_cur
< ack_seq_low
) ack_seq_low
= nack_seq_cur
;
2739 if (nack_seq_cur
> ack_seq_high
) ack_seq_high
= nack_seq_cur
;
2744 g
->bounds
.x0
= (((data_tim_low
<= ack_tim_low
) && data_frame_seen
) || (!ack_frame_seen
)) ? data_tim_low
: ack_tim_low
;
2745 g
->bounds
.width
= ((((data_tim_high
>= ack_tim_high
) && data_frame_seen
) || (!ack_frame_seen
)) ? data_tim_high
: ack_tim_high
) - g
->bounds
.x0
;
2746 g
->bounds
.y0
= 0; /* We always want the overal bounds to go back down to SN=0 */
2747 g
->bounds
.height
= (((data_seq_high
>= ack_seq_high
) && data_frame_seen
) || (!ack_frame_seen
)) ? data_seq_high
: ack_seq_high
;
2749 g
->zoom
.x
= (g
->geom
.width
- 1) / g
->bounds
.width
;
2750 g
->zoom
.y
= (g
->geom
.height
-1) / g
->bounds
.height
;
2753 static void graph_read_config(struct graph
*g
)
2755 /* Black for PDUs */
2756 g
->style
.seq_color
.red
= (double)0 / 65535.0;
2757 g
->style
.seq_color
.green
= (double)0 / 65535.0;
2758 g
->style
.seq_color
.blue
= (double)0 / 65535.0;
2759 g
->style
.seq_color
.alpha
= 1.0;
2761 /* Grey for resegmentations */
2762 g
->style
.seq_resegmented_color
.red
= (double)0x7777 / 65535.0;
2763 g
->style
.seq_resegmented_color
.green
= (double)0x7777 / 65535.0;
2764 g
->style
.seq_resegmented_color
.blue
= (double)0x7777 / 65535.0;
2765 g
->style
.seq_resegmented_color
.alpha
= 1.0;
2768 g
->style
.ack_color
[0].red
= (double)0x2222 / 65535.0;
2769 g
->style
.ack_color
[0].green
= (double)0x2222 / 65535.0;
2770 g
->style
.ack_color
[0].blue
= (double)0xaaaa / 65535.0;
2771 g
->style
.ack_color
[0].alpha
= 1.0;
2774 g
->style
.ack_color
[1].red
= (double)0xaaaa / 65535.0;
2775 g
->style
.ack_color
[1].green
= (double)0x2222 / 65535.0;
2776 g
->style
.ack_color
[1].blue
= (double)0x2222 / 65535.0;
2777 g
->style
.ack_color
[1].alpha
= 1.0;
2779 /* Time origin should be shown as time in capture by default */
2780 g
->style
.flags
= TIME_ORIGIN_CAP
;
2782 g
->y_axis
->label
= (const char ** )g_malloc(3 * sizeof(char * ));
2783 g
->y_axis
->label
[0] = "Number";
2784 g
->y_axis
->label
[1] = "Sequence";
2785 g
->y_axis
->label
[2] = NULL
;
2786 g
->x_axis
->label
= (const char ** )g_malloc(2 * sizeof(char * ));
2787 g
->x_axis
->label
[0] = "Time[s]";
2788 g
->x_axis
->label
[1] = NULL
;
2791 static void rlc_lte_make_elmtlist(struct graph
*g
)
2793 struct segment
*tmp
;
2794 struct element
*elements0
, *e0
; /* list of elmts showing control */
2795 struct element
*elements1
, *e1
; /* list of elmts showing data */
2796 struct segment
*last_status_segment
= NULL
;
2798 gboolean ack_seen
= FALSE
;
2801 int n
, data
, acks
, nacks
;
2803 double previous_status_x
= 0.0, previous_status_y
= 0.0;
2805 debug(DBS_FENTRY
) puts("rlc_lte_make_elmtlist()");
2807 /* Allocate all needed elements up-front */
2808 if (g
->elists
->elements
== NULL
) {
2809 get_data_control_counts(g
, &data
, &acks
, &nacks
);
2811 /* Allocate elements for data */
2813 e0
= elements0
= (struct element
*)g_malloc(n
*sizeof(struct element
));
2815 /* Allocate elements for status */
2816 n
= (2*acks
) + (4*nacks
) + 2;
2817 e1
= elements1
= (struct element
*)g_malloc(n
*sizeof(struct element
));
2819 /* Allocate container for 2nd list of elements */
2820 g
->elists
->next
= (struct element_list
*)g_malloc0(sizeof(struct element_list
));
2823 e0
= elements0
= g
->elists
->elements
;
2824 e1
= elements1
= g
->elists
->next
->elements
;
2829 seq_base
= (guint32
) yy0
;
2831 for (tmp
=g
->segments
; tmp
; tmp
=tmp
->next
) {
2835 /****************************************/
2836 /* X axis is time, Y is sequence number */
2837 /****************************************/
2839 secs
= tmp
->rel_secs
+ (tmp
->rel_usecs
/ 1000000.0);
2843 if (!tmp
->isControlPDU
) {
2848 seq_cur
= tmp
->SN
- seq_base
;
2850 /* Work out positions around this SN */
2851 #define DATA_BLOB_SIZE 4
2852 y
= (g
->zoom
.y
* seq_cur
);
2854 /* Circle for data point */
2855 e0
->type
= ELMT_ELLIPSE
;
2857 if (!tmp
->isResegmented
) {
2858 e0
->elment_color_p
= &g
->style
.seq_color
;
2861 e0
->elment_color_p
= &g
->style
.seq_resegmented_color
;
2864 e0
->p
.ellipse
.dim
.width
= DATA_BLOB_SIZE
;
2865 e0
->p
.ellipse
.dim
.height
= DATA_BLOB_SIZE
;
2866 e0
->p
.ellipse
.dim
.x
= x
;
2867 e0
->p
.ellipse
.dim
.y
= y
;
2871 /* Remember the last status segment */
2872 last_status_segment
= tmp
;
2874 /* -1 so ACK lines up with last data, rather than showing above it... */
2875 seq_cur
= tmp
->ACKNo
- seq_base
- 1;
2877 /* Work out positions around this SN */
2878 y
= (g
->zoom
.y
* seq_cur
);
2882 if (y
> previous_status_y
) {
2883 /* Draw from previous ACK point horizontally to this time */
2884 e1
->type
= ELMT_LINE
;
2886 e1
->elment_color_p
= &g
->style
.ack_color
[0];
2887 e1
->p
.line
.dim
.x1
= previous_status_x
;
2888 e1
->p
.line
.dim
.y1
= previous_status_y
;
2889 e1
->p
.line
.dim
.x2
= x
;
2890 e1
->p
.line
.dim
.y2
= previous_status_y
;
2893 /* Now draw up to current ACK */
2894 e1
->type
= ELMT_LINE
;
2896 e1
->elment_color_p
= &g
->style
.ack_color
[0];
2897 e1
->p
.line
.dim
.x1
= x
;
2898 e1
->p
.line
.dim
.y1
= previous_status_y
;
2899 e1
->p
.line
.dim
.x2
= x
;
2900 e1
->p
.line
.dim
.y2
= y
;
2904 /* Want to go down, then along in this case... */
2905 e1
->type
= ELMT_LINE
;
2907 e1
->elment_color_p
= &g
->style
.ack_color
[0];
2908 e1
->p
.line
.dim
.x1
= previous_status_x
;
2909 e1
->p
.line
.dim
.y1
= previous_status_y
;
2910 e1
->p
.line
.dim
.x2
= previous_status_x
;
2911 e1
->p
.line
.dim
.y2
= y
;
2914 /* Now draw up to current ACK */
2915 e1
->type
= ELMT_LINE
;
2917 e1
->elment_color_p
= &g
->style
.ack_color
[0];
2918 e1
->p
.line
.dim
.x1
= previous_status_x
;
2919 e1
->p
.line
.dim
.y1
= y
;
2920 e1
->p
.line
.dim
.x2
= x
;
2921 e1
->p
.line
.dim
.y2
= y
;
2926 if (tmp
->noOfNACKs
> 0) {
2927 for (n
=0; n
< tmp
->noOfNACKs
; n
++) {
2928 double nack_y
= (g
->zoom
.y
* tmp
->NACKs
[n
]);
2930 /* A red cross to show where the NACK is reported */
2931 #define NACK_CROSS_SIZE 3
2932 e1
->type
= ELMT_LINE
;
2934 e1
->elment_color_p
= &g
->style
.ack_color
[1];
2935 e1
->p
.line
.dim
.x1
= x
-NACK_CROSS_SIZE
;
2936 e1
->p
.line
.dim
.y1
= nack_y
- NACK_CROSS_SIZE
;
2937 e1
->p
.line
.dim
.x2
= x
+ NACK_CROSS_SIZE
;
2938 e1
->p
.line
.dim
.y2
= nack_y
+ NACK_CROSS_SIZE
;
2941 e1
->type
= ELMT_LINE
;
2943 e1
->elment_color_p
= &g
->style
.ack_color
[1];
2944 e1
->p
.line
.dim
.x1
= x
- NACK_CROSS_SIZE
;
2945 e1
->p
.line
.dim
.y1
= nack_y
+ NACK_CROSS_SIZE
;
2946 e1
->p
.line
.dim
.x2
= x
+ NACK_CROSS_SIZE
;
2947 e1
->p
.line
.dim
.y2
= nack_y
- NACK_CROSS_SIZE
;
2950 e1
->type
= ELMT_LINE
;
2952 e1
->elment_color_p
= &g
->style
.ack_color
[1];
2953 e1
->p
.line
.dim
.x1
= x
;
2954 e1
->p
.line
.dim
.y1
= nack_y
+ NACK_CROSS_SIZE
;
2955 e1
->p
.line
.dim
.x2
= x
;
2956 e1
->p
.line
.dim
.y2
= nack_y
- NACK_CROSS_SIZE
;
2959 e1
->type
= ELMT_LINE
;
2961 e1
->elment_color_p
= &g
->style
.ack_color
[1];
2962 e1
->p
.line
.dim
.x1
= x
- NACK_CROSS_SIZE
;
2963 e1
->p
.line
.dim
.y1
= nack_y
;
2964 e1
->p
.line
.dim
.x2
= x
+ NACK_CROSS_SIZE
;
2965 e1
->p
.line
.dim
.y2
= nack_y
;
2972 previous_status_x
= x
;
2973 previous_status_y
= y
;
2978 /* Add one more line for status, from the last PDU -> rhs of graph */
2979 e1
->type
= ELMT_LINE
;
2980 e1
->parent
= last_status_segment
;
2981 e1
->elment_color_p
= &g
->style
.ack_color
[0];
2982 e1
->p
.line
.dim
.x1
= previous_status_x
;
2983 e1
->p
.line
.dim
.y1
= previous_status_y
;
2984 e1
->p
.line
.dim
.x2
= g
->bounds
.width
* g
->zoom
.x
; /* right edge of graph area */
2985 e1
->p
.line
.dim
.y2
= previous_status_y
;
2989 /* Complete both element lists */
2990 e0
->type
= ELMT_NONE
;
2991 e1
->type
= ELMT_NONE
;
2992 g
->elists
->elements
= elements0
;
2993 g
->elists
->next
->elements
= elements1
;
2996 #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
2997 /* Starting VS2013, rint already defined in math.h. N o need to redefine */
2998 /* replacement of Unix rint() for Windows */
2999 static int rint(double x
)
3004 buf
= _fcvt(x
, 0, &dec
, &sig
);