2 * io_stat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
41 #include <epan/epan_dissect.h>
42 #include <epan/packet_info.h>
43 #include <epan/stat_cmd_args.h>
45 #include <epan/strutil.h>
47 #include "../stat_menu.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
51 #include "ui/gtk/gtkglobals.h"
52 #include "ui/gtk/gui_utils.h"
53 #include "ui/gtk/gui_stat_menu.h"
54 #include "ui/gtk/stock_icons.h"
55 #include "ui/gtk/dlg_utils.h"
56 #include "ui/gtk/filter_dlg.h"
57 #include "ui/gtk/help_dlg.h"
58 #include "ui/gtk/pixmap_save.h"
59 #include "ui/gtk/main.h"
60 #include "ui/gtk/filter_autocomplete.h"
62 #include "ui/gtk/old-gtk-compat.h"
67 #define LOGARITHMIC_YSCALE 0
68 #define AUTO_MAX_YSCALE 1
69 #define DEFAULT_YSCALE_INDEX 1
70 static guint32 yscale_max[MAX_YSCALE] = {LOGARITHMIC_YSCALE, AUTO_MAX_YSCALE, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000};
72 #define NO_FILTER_ORDER 0
73 #define MAX_MOVING_AVERAGE_ORDER 10
74 static guint32 moving_average_orders[MAX_MOVING_AVERAGE_ORDER] = {NO_FILTER_ORDER, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
76 #define MOVING_AVERAGE_FILTER 1
78 #define MAX_PIXELS_PER_TICK 4
79 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
80 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
83 #define DEFAULT_PLOT_STYLE 0
84 #define PLOT_STYLE_LINE 0
85 #define PLOT_STYLE_IMPULSE 1
86 #define PLOT_STYLE_FILLED_BAR 2
87 #define PLOT_STYLE_DOT 3
88 #define MAX_PLOT_STYLES 4
89 static const char *plot_style_name[MAX_PLOT_STYLES] = {
96 #define DEFAULT_COUNT_TYPE 0
97 #define COUNT_TYPE_FRAMES 0
98 #define COUNT_TYPE_BYTES 1
99 #define COUNT_TYPE_BITS 2
100 #define COUNT_TYPE_ADVANCED 3
101 #define MAX_COUNT_TYPES 4
102 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
105 #define MAX_TICK_VALUES 7
106 #define DEFAULT_TICK_VALUE_INDEX 3
107 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
109 #define CALC_TYPE_SUM 0
110 #define CALC_TYPE_COUNT 1
111 #define CALC_TYPE_MAX 2
112 #define CALC_TYPE_MIN 3
113 #define CALC_TYPE_AVG 4
114 #define CALC_TYPE_LOAD 5
115 #define MAX_CALC_TYPES 6
116 #define DEFAULT_CALC_TYPE 0
117 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
120 typedef struct _io_stat_calc_type_t {
121 struct _io_stat_graph_t *gio;
123 } io_stat_calc_type_t;
125 #define NUM_IO_ITEMS 100000
126 typedef struct _io_item_t {
127 guint32 frames; /* always calculated, will hold number of frames*/
128 guint32 bytes; /* always calculated, will hold number of bytes*/
144 typedef struct _io_stat_graph_t {
145 struct _io_stat_t *io;
146 io_item_t items[NUM_IO_ITEMS];
149 GtkWidget *display_button;
150 GtkWidget *filter_field;
151 GtkWidget *advanced_buttons;
154 GtkWidget *calc_field;
156 #if GTK_CHECK_VERSION(3,0,0)
159 construct_args_t *args;
160 GtkWidget *filter_bt;
164 typedef struct _io_stat_t {
165 gboolean needs_redraw;
166 gint32 interval; /* measurement interval in ms */
167 guint32 last_interval;
168 guint32 max_interval; /* XXX max_interval and num_items are redundant */
170 guint32 left_x_border;
171 guint32 right_x_border;
172 gboolean view_as_time;
175 struct _io_stat_graph_t graphs[MAX_GRAPHS];
177 GtkWidget *draw_area;
178 #if GTK_CHECK_VERSION(2,22,0)
179 cairo_surface_t *surface;
183 GtkAdjustment *scrollbar_adjustment;
184 GtkWidget *scrollbar;
185 guint first_frame_num[NUM_IO_ITEMS];
186 guint last_frame_num;
193 guint32 filter_order;
198 static void init_io_stat_window(io_stat_t *io);
199 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
202 io_stat_set_title(io_stat_t *io)
209 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
210 gtk_window_set_title(GTK_WINDOW(io->window), title);
215 io_stat_reset(io_stat_t *io)
219 io->needs_redraw=TRUE;
220 for(i=0;i<MAX_GRAPHS;i++){
221 for(j=0;j<NUM_IO_ITEMS;j++){
223 ioi=&io->graphs[i].items[j];
237 nstime_set_zero(&ioi->time_max);
238 nstime_set_zero(&ioi->time_min);
239 nstime_set_zero(&ioi->time_tot);
242 io->last_interval=0xffffffff;
245 io->start_time.secs=0;
246 io->start_time.nsecs=0;
247 for(j=0;j<NUM_IO_ITEMS;j++) {
248 io->first_frame_num[j]=0;
250 io->last_frame_num=0;
252 io_stat_set_title(io);
256 tap_iostat_reset(void *g)
258 io_stat_graph_t *gio=g;
260 io_stat_reset(gio->io);
264 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
266 io_stat_graph_t *git=g;
271 /* we sometimes get called when git is disabled.
272 this is a bug since the tap listener should be removed first */
277 git->io->needs_redraw=TRUE;
280 * Find which interval this is supposed to go in and store the
281 * interval index as idx
283 time_delta=pinfo->fd->rel_ts;
284 if(time_delta.nsecs<0){
286 time_delta.nsecs+=1000000000;
288 if(time_delta.secs<0){
291 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
293 /* some sanity checks */
294 if((idx<0)||(idx>=NUM_IO_ITEMS)){
295 git->io->num_items = NUM_IO_ITEMS-1;
299 /* update num_items */
300 if((guint32)idx > git->io->num_items){
301 git->io->num_items=idx;
302 git->io->max_interval=(idx+1)*git->io->interval;
306 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
307 nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
310 /* set first and last frame num in current interval */
311 if (git->io->first_frame_num[idx] == 0) {
312 git->io->first_frame_num[idx]=pinfo->fd->num;
314 git->io->last_frame_num=pinfo->fd->num;
317 * Find the appropriate io_item_t structure
323 * For ADVANCED mode we need to keep track of some more stuff
324 * than just frame and byte counts
326 if(git->io->count_type==COUNT_TYPE_ADVANCED){
330 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
335 /* update the appropriate counters, make sure that if
336 * fields==0 then this is the first seen value so
337 * set any min/max values accordingly
339 for(i=0;i<gp->len;i++){
345 switch(proto_registrar_get_ftype(git->hf_index)){
350 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
352 if((new_int>it->int_max)||(it->fields==0)){
355 if((new_int<it->int_min)||(it->fields==0)){
358 it->int_tot+=new_int;
365 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
366 if((new_int>it->int_max)||(it->fields==0)){
369 if((new_int<it->int_min)||(it->fields==0)){
372 it->int_tot+=new_int;
376 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
377 if((new_float>it->float_max)||(it->fields==0)){
378 it->float_max=new_float;
380 if((new_float<it->float_min)||(it->fields==0)){
381 it->float_min=new_float;
383 it->float_tot+=new_float;
387 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
388 if((new_double>it->double_max)||(it->fields==0)){
389 it->double_max=new_double;
391 if((new_double<it->double_min)||(it->fields==0)){
392 it->double_min=new_double;
394 it->double_tot+=new_double;
397 case FT_RELATIVE_TIME:
398 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
400 switch(git->calc_type){
401 guint64 t, pt; /* time in us */
404 /* it is a LOAD calculation of a relative time field.
405 * add the time this call spanned to each
406 * interval it spanned according to its contribution
410 t=t*1000000+new_time->nsecs/1000;
412 /* handle current interval */
413 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
414 pt=pt%(git->io->interval*1000);
419 git->items[j].time_tot.nsecs+=(int) (pt*1000);
420 if(git->items[j].time_tot.nsecs>1000000000){
421 git->items[j].time_tot.secs++;
422 git->items[j].time_tot.nsecs-=1000000000;
430 if(t > (guint32) (git->io->interval*1000)){
431 pt=git->io->interval*1000;
438 if( (new_time->secs>it->time_max.secs)
439 ||( (new_time->secs==it->time_max.secs)
440 &&(new_time->nsecs>it->time_max.nsecs))
442 it->time_max=*new_time;
444 if( (new_time->secs<it->time_min.secs)
445 ||( (new_time->secs==it->time_min.secs)
446 &&(new_time->nsecs<it->time_min.nsecs))
448 it->time_min=*new_time;
450 nstime_add(&it->time_tot, new_time);
458 it->bytes+=pinfo->fd->pkt_len;
464 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
466 guint i, frame_num=0;
468 if (idx>io->num_items) {
473 frame_num=io->first_frame_num[idx];
478 * If first frame not found we select the last
479 * frame in the previous interval
481 * If selecting the last frame we select the frame
482 * before the first frame in the next interval
484 for(i=idx+1;i<=io->num_items;i++) {
485 frame_num=io->first_frame_num[i];
486 if (frame_num != 0) {
492 * If not found we select the last frame
494 frame_num=io->last_frame_num;
501 get_it_value(io_stat_t *io, int graph_id, int idx)
507 it=&io->graphs[graph_id].items[idx];
509 switch(io->count_type){
510 case COUNT_TYPE_FRAMES:
512 case COUNT_TYPE_BYTES:
514 case COUNT_TYPE_BITS:
515 return (it->bytes * 8);
519 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
522 switch(io->graphs[graph_id].calc_type){
523 case CALC_TYPE_COUNT:
538 switch(io->graphs[graph_id].calc_type){
542 case CALC_TYPE_COUNT:
553 value=it->int_tot/it->fields;
563 switch(io->graphs[graph_id].calc_type){
567 case CALC_TYPE_COUNT:
578 value=it->float_tot/it->fields;
588 switch(io->graphs[graph_id].calc_type){
590 value=it->double_tot;
592 case CALC_TYPE_COUNT:
596 value=it->double_max;
599 value=it->double_min;
603 value=it->double_tot/it->fields;
612 case FT_RELATIVE_TIME:
613 switch(io->graphs[graph_id].calc_type){
614 case CALC_TYPE_COUNT:
618 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
621 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
624 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
628 guint64 t; /* time in us */
631 t=t*1000000+it->time_tot.nsecs/1000;
632 value=(guint32) (t/it->fields);
638 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
647 return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
651 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
653 if(t_max>=10000000 || (log_flag && t_max>=1000000)){
654 g_snprintf(buf, buf_len, "%ds",t/1000000);
655 } else if(t_max>=1000000){
656 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
657 } else if(t_max>=10000 || (log_flag && t_max>=1000)){
658 g_snprintf(buf, buf_len, "%dms",t/1000);
659 } else if(t_max>=1000){
660 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
662 g_snprintf(buf, buf_len, "%dus",t);
667 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
670 if (io->view_as_time) {
672 time_t sec_val = interval/1000 + io->start_time.secs;
673 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
675 if(nsec_val >= 1000) {
679 tmp = localtime (&sec_val);
680 if(io->interval>=1000){
681 g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
682 } else if(io->interval>=100){
683 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
684 } else if(io->interval>=10){
685 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
687 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
691 g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
692 } else if(io->interval>=60000){
693 g_snprintf(buf, buf_len, "%dm", interval/60000);
694 } else if(io->interval>=1000){
695 g_snprintf(buf, buf_len, "%ds", interval/1000);
696 } else if(io->interval>=100){
697 g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
698 } else if(io->interval>=10){
699 g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
701 g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
707 io_stat_draw(io_stat_t *io)
709 int i, tics, ystart, ys;
710 guint32 last_interval, first_interval, interval_delta;
711 gint32 current_interval;
712 guint32 top_y_border;
713 guint32 bottom_y_border;
715 int label_width, label_height;
716 guint32 draw_width, draw_height;
717 char label_string[45];
718 GtkAllocation widget_alloc;
721 guint32 num_time_intervals;
722 guint32 max_value; /* max value of seen data */
723 guint32 max_y; /* max value of the Y scale */
724 gboolean draw_y_as_time;
726 if(!io->needs_redraw){
729 io->needs_redraw=FALSE;
732 * Find the length of the intervals we have data for
733 * so we know how large arrays we need to malloc()
735 num_time_intervals=io->num_items+1;
737 /* XXX move this check to _packet() */
738 if(num_time_intervals>NUM_IO_ITEMS){
739 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
745 * find the max value so we can autoscale the y axis
748 for(i=0;i<MAX_GRAPHS;i++){
751 if(!io->graphs[i].display){
754 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
757 val=get_it_value(io, i, idx);
759 /* keep track of the max value we have encountered */
771 #if GTK_CHECK_VERSION(2,22,0)
772 cr = cairo_create (io->surface);
774 cr = gdk_cairo_create (io->pixmap);
776 cairo_set_source_rgb (cr, 1, 1, 1);
777 gtk_widget_get_allocation(io->draw_area, &widget_alloc);
778 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
782 * Calculate the y scale we should use
784 if(io->max_y_units==AUTO_MAX_YSCALE){
785 max_y=yscale_max[MAX_YSCALE-1];
786 for(i=MAX_YSCALE-1;i>1;i--){
787 if(max_value<yscale_max[i]){
791 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
793 for(i=1000000000;i>1;i/=10){
794 if(max_value<(guint32)i){
799 /* the user had specified an explicit y scale to use */
800 max_y=io->max_y_units;
805 * If we use ADVANCED and all the graphs are plotting
806 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
807 * then we will do some some special processing for the
808 * labels for the Y axis below:
809 * we will append the time unit " s" " ms" or " us"
810 * and we will present the unit in decimal
812 draw_y_as_time=FALSE;
813 if(io->count_type==COUNT_TYPE_ADVANCED){
815 for(i=0;i<MAX_GRAPHS;i++){
818 if(!io->graphs[i].display){
821 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
823 case FT_RELATIVE_TIME:
824 switch(io->graphs[i].calc_type){
831 draw_y_as_time=FALSE;
835 draw_y_as_time=FALSE;
843 * Calculate size of borders surrounding the plot
844 * The border on the right side needs to be adjusted depending
845 * on the width of the text labels. For simplicity we assume that the
846 * top y scale label will be the widest one
849 if(io->max_y_units==LOGARITHMIC_YSCALE){
850 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
852 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
855 g_snprintf(label_string, 15, "%d", max_y);
857 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
858 pango_layout_get_pixel_size(layout, &label_width, &label_height);
860 io->left_x_border=10;
861 io->right_x_border=label_width+20;
863 bottom_y_border=label_height+20;
867 * Calculate the size of the drawing area for the actual plot
869 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
870 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
874 * Add a warning if too many entries
876 if (num_time_intervals == NUM_IO_ITEMS) {
877 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
878 pango_layout_set_text(layout, label_string, -1);
880 #if GTK_CHECK_VERSION(2,22,0)
881 cr = cairo_create (io->surface);
883 cr = gdk_cairo_create (io->pixmap);
885 cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
886 pango_cairo_show_layout (cr, layout);
892 * Draw the y axis and labels
893 * (we always draw the y scale with 11 ticks along the axis)
895 #if GTK_CHECK_VERSION(2,22,0)
896 cr = cairo_create (io->surface);
898 cr = gdk_cairo_create (io->pixmap);
900 cairo_set_line_width (cr, 1.0);
901 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5);
902 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5);
905 if(io->max_y_units==LOGARITHMIC_YSCALE){
906 tics=(int)log10((double)max_y);
907 ystart=draw_height/10;
914 for(i=ys;i<=tics;i++){
915 int xwidth, lwidth, ypos;
918 if(io->max_y_units==LOGARITHMIC_YSCALE){
920 /* position for the 0 value */
921 ypos=io->pixmap_height-bottom_y_border;
923 /* position for the top value, do not draw logarithmic tics above graph */
924 ypos=io->pixmap_height-bottom_y_border-draw_height;
927 /* draw the logarithmic tics */
929 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
931 #if GTK_CHECK_VERSION(2,22,0)
932 cr = cairo_create (io->surface);
934 cr = gdk_cairo_create (io->pixmap);
936 cairo_set_line_width (cr, 1.0);
937 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
938 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
942 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
944 /* all "main" logarithmic lines are slightly longer */
948 /* first, middle and last tick are slightly longer */
951 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
954 #if GTK_CHECK_VERSION(2,22,0)
955 cr = cairo_create (io->surface);
957 cr = gdk_cairo_create (io->pixmap);
959 cairo_set_line_width (cr, 1.0);
960 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
961 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
964 /* draw the labels */
967 if(io->max_y_units==LOGARITHMIC_YSCALE){
968 value=(guint32)(max_y/pow(10,tics-i));
970 print_time_scale_string(label_string, 15, value, value, TRUE);
972 g_snprintf(label_string, 15, "%d", value);
977 print_time_scale_string(label_string, 15, value, max_y, FALSE);
979 g_snprintf(label_string, 15, "%d", value);
983 pango_layout_set_text(layout, label_string, -1);
984 pango_layout_get_pixel_size(layout, &lwidth, NULL);
986 #if GTK_CHECK_VERSION(2,22,0)
987 cr = cairo_create (io->surface);
989 cr = gdk_cairo_create (io->pixmap);
991 cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
992 pango_cairo_show_layout (cr, layout);
1000 * if we have not specified the last_interval via the gui,
1001 * then just pick the current end of the capture so that is scrolls
1002 * nicely when doing live captures
1004 if(io->last_interval==0xffffffff){
1005 last_interval=io->max_interval;
1007 last_interval=io->last_interval;
1014 /* plot the x-scale */
1015 #if GTK_CHECK_VERSION(2,22,0)
1016 cr = cairo_create (io->surface);
1018 cr = gdk_cairo_create (io->pixmap);
1020 cairo_set_line_width (cr, 1.0);
1021 cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5);
1022 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5);
1025 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
1026 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
1027 first_interval*=io->interval;
1032 interval_delta=(100/io->pixels_per_tick)*io->interval;
1033 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
1036 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
1037 /* if pixels_per_tick is 5, only draw every 5 ticks */
1038 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
1039 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
1043 if(!(current_interval%interval_delta)){
1045 } else if(!(current_interval%(interval_delta/2))){
1050 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
1051 #if GTK_CHECK_VERSION(2,22,0)
1052 cr = cairo_create (io->surface);
1054 cr = gdk_cairo_create (io->pixmap);
1056 cairo_set_line_width (cr, 1.0);
1057 cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5);
1058 cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5);
1063 print_interval_string (label_string, 15, current_interval, io, TRUE);
1064 pango_layout_set_text(layout, label_string, -1);
1065 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1067 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1069 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1070 x_pos=io->pixmap_width-lwidth-5;
1072 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1074 #if GTK_CHECK_VERSION(2,22,0)
1075 cr = cairo_create (io->surface);
1077 cr = gdk_cairo_create (io->pixmap);
1079 cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
1080 pango_cairo_show_layout (cr, layout);
1086 g_object_unref(G_OBJECT(layout));
1090 * Loop over all graphs and draw them
1092 for(i=MAX_GRAPHS-1;i>=0;i--){
1094 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1095 /* Moving average variables */
1096 guint32 mavg_in_average_count = 0, mavg_left = 0, mavg_right = 0;
1097 guint64 mavg_cumulated = 0;
1098 guint32 mavg_to_remove = 0, mavg_to_add = 0;
1100 if(!io->graphs[i].display){
1104 if(io->filter_type == MOVING_AVERAGE_FILTER){
1105 /* "Warm-up phase" - calculate average on some data not displayed;
1106 just to make sure average on leftmost and rightmost displayed
1107 values is as reliable as possible
1109 guint32 warmup_interval;
1111 if(first_interval/io->interval > io->filter_order/2){
1112 warmup_interval = first_interval/io->interval - io->filter_order/2;
1113 warmup_interval*= io->interval;
1115 warmup_interval = 0;
1117 mavg_to_remove = warmup_interval;
1118 for(;warmup_interval<first_interval;warmup_interval+=io->interval){
1119 mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
1120 mavg_in_average_count++;
1123 mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
1124 mavg_in_average_count++;
1125 for(warmup_interval+=io->interval;warmup_interval<(first_interval+(io->filter_order/2)*io->interval)&&(warmup_interval<=(io->num_items*io->interval));warmup_interval+=io->interval){
1126 mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
1127 mavg_in_average_count++;
1130 mavg_to_add = warmup_interval;
1133 /* initialize prev x/y to the value of the first interval */
1134 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1135 val=get_it_value(io, i, first_interval/io->interval);
1136 if(io->filter_type == MOVING_AVERAGE_FILTER){
1137 if(mavg_in_average_count>0){
1138 val = mavg_cumulated/mavg_in_average_count;
1143 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1145 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1147 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1150 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1153 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1154 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1156 val=get_it_value(io, i, interval/io->interval);
1157 /* Moving average calculation */
1158 if(io->filter_type == MOVING_AVERAGE_FILTER){
1159 if(interval!=first_interval){
1161 if(mavg_left > io->filter_order/2){
1163 mavg_in_average_count--;
1164 mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval);
1165 mavg_to_remove += io->interval;
1167 if(mavg_to_add<=io->num_items*io->interval){
1168 mavg_in_average_count++;
1169 mavg_cumulated += get_it_value(io, i, mavg_to_add/io->interval);
1170 mavg_to_add += io->interval;
1175 val = mavg_cumulated / mavg_in_average_count;
1180 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1182 y_pos=(guint32)(draw_height-1+top_y_border);
1184 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1187 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1190 switch(io->graphs[i].plot_style){
1191 case PLOT_STYLE_LINE:
1192 /* dont need to draw anything if the segment
1193 * is entirely above the top of the graph
1195 if( (prev_y_pos!=0) || (y_pos!=0) ){
1196 #if GTK_CHECK_VERSION(2,22,0)
1197 cr = cairo_create (io->surface);
1199 cr = gdk_cairo_create (io->pixmap);
1201 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1202 cairo_set_line_width (cr, 1.0);
1203 cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
1204 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1209 case PLOT_STYLE_IMPULSE:
1211 #if GTK_CHECK_VERSION(2,22,0)
1212 cr = cairo_create (io->surface);
1214 cr = gdk_cairo_create (io->pixmap);
1216 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1217 cairo_set_line_width (cr, 1.0);
1218 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1219 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1224 case PLOT_STYLE_FILLED_BAR:
1226 #if GTK_CHECK_VERSION(2,22,0)
1227 cr = cairo_create (io->surface);
1229 cr = gdk_cairo_create (io->pixmap);
1231 cairo_rectangle (cr,
1232 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1234 io->pixels_per_tick,
1235 draw_height-1+top_y_border-y_pos);
1236 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1241 case PLOT_STYLE_DOT:
1243 #if GTK_CHECK_VERSION(2,22,0)
1244 cr = cairo_create (io->surface);
1246 cr = gdk_cairo_create (io->pixmap);
1251 (gdouble)io->pixels_per_tick/2,
1254 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1266 cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1268 #if GTK_CHECK_VERSION(2,22,0)
1269 cairo_set_source_surface (cr, io->surface, 0, 0);
1271 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1273 cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1278 /* update the scrollbar */
1279 if (io->max_interval == 0) {
1280 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
1281 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
1282 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
1284 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
1285 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1286 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1288 gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
1289 gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
1290 gtk_adjustment_changed(io->scrollbar_adjustment);
1291 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1296 io_stat_redraw(io_stat_t *io)
1298 io->needs_redraw=TRUE;
1303 tap_iostat_draw(void *g)
1305 io_stat_graph_t *git=g;
1307 io_stat_draw(git->io);
1310 /* ok we get called with both the filter and the field.
1311 make sure the field is part of the filter.
1312 (make sure and make sure just append it)
1313 the field MUST be part of the filter or else we wont
1314 be able to pick up the field values after the edt tree has been
1318 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1320 char real_filter[262];
1326 /* skip all whitespaces */
1339 g_snprintf(real_filter, 257, "(%s)", filter);
1344 /* skip all whitespaces */
1357 if(real_filter[0]!=0){
1358 g_strlcat(real_filter, " && ", 262);
1360 g_strlcat(real_filter, field, 262);
1363 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1364 TL_REQUIRES_PROTO_TREE,
1365 tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1369 disable_graph(io_stat_graph_t *gio)
1373 protect_thread_critical_region();
1374 remove_tap_listener(gio);
1375 unprotect_thread_critical_region();
1376 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1382 iostat_init(const char *optarg _U_, void* userdata _U_)
1386 static GdkColor col[MAX_GRAPHS] = {
1387 {0, 0x0000, 0x0000, 0x0000}, /* Black */
1388 {0, 0xffff, 0x0000, 0x0000}, /* Red */
1389 {0, 0x0000, 0xffff, 0x0000}, /* Green */
1390 {0, 0x0000, 0x0000, 0xffff}, /* Blue */
1391 {0, 0xffff, 0x5000, 0xffff} /* Light brilliant magenta */
1393 #if GTK_CHECK_VERSION(3,0,0)
1394 static GdkRGBA rgba_col[MAX_GRAPHS] = {
1395 {0.0, 0.0, 0.0, 1.0}, /* Black */
1396 {1.0, 0.0, 0.1, 1.0}, /* Red */
1397 {0.0, 1.0, 0.0, 1.0}, /* Green */
1398 {0.0, 0.0, 1.0, 1.0}, /* Blue */
1399 {1.0, 0.314, 1.0, 1.0} /* Light brilliant magenta */
1402 GString *error_string;
1404 io=g_malloc(sizeof(io_stat_t));
1405 io->needs_redraw=TRUE;
1406 io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1409 #if GTK_CHECK_VERSION(2,22,0)
1415 io->scrollbar_adjustment=NULL;
1416 io->pixmap_width=500;
1417 io->pixmap_height=200;
1418 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1419 io->max_y_units=AUTO_MAX_YSCALE;
1421 io->last_interval=0xffffffff;
1424 io->left_x_border=0;
1425 io->right_x_border=500;
1426 io->view_as_time=FALSE;
1427 io->start_time.secs=0;
1428 io->start_time.nsecs=0;
1430 for(i=0;i<MAX_GRAPHS;i++){
1431 io->graphs[i].color.pixel=col[i].pixel;
1432 io->graphs[i].color.red=col[i].red;
1433 io->graphs[i].color.green=col[i].green;
1434 io->graphs[i].color.blue=col[i].blue;
1435 #if GTK_CHECK_VERSION(3,0,0)
1436 io->graphs[i].rgba_color.red=rgba_col[i].red;
1437 io->graphs[i].rgba_color.green=rgba_col[i].green;
1438 io->graphs[i].rgba_color.blue=rgba_col[i].blue;
1439 io->graphs[i].rgba_color.alpha=rgba_col[i].alpha;
1441 io->graphs[i].display=0;
1442 io->graphs[i].display_button=NULL;
1443 io->graphs[i].filter_field=NULL;
1444 io->graphs[i].advanced_buttons=NULL;
1445 io->graphs[i].io=io;
1447 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1448 io->graphs[i].args->title = NULL;
1449 io->graphs[i].args->wants_apply_button=TRUE;
1450 io->graphs[i].args->activate_on_ok=TRUE;
1451 io->graphs[i].args->modal_and_transient=FALSE;
1453 io->graphs[i].filter_bt=NULL;
1457 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1458 g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1462 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1464 g_string_free(error_string, TRUE);
1465 io->graphs[0].display=0;
1466 io->graphs[0].display_button=NULL;
1467 io->graphs[0].filter_field=NULL;
1468 io->graphs[0].advanced_buttons=NULL;
1473 init_io_stat_window(io);
1475 cf_retap_packets(&cfile);
1476 gdk_window_raise(gtk_widget_get_window(io->window));
1481 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1483 io_stat_t *io = user_data;
1485 GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1486 surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
1488 g_free(surface_info);
1490 for(i=0;i<MAX_GRAPHS;i++){
1491 if(io->graphs[i].display){
1492 protect_thread_critical_region();
1493 remove_tap_listener(&io->graphs[i]);
1494 unprotect_thread_critical_region();
1496 g_free( (gpointer) (io->graphs[i].args->title) );
1497 io->graphs[i].args->title=NULL;
1499 g_free(io->graphs[i].args);
1500 io->graphs[i].args=NULL;
1509 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1511 io_stat_t *io = user_data;
1512 guint32 draw_width, interval, last_interval;
1515 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1517 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1518 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1519 /* Outside draw area */
1523 #if GTK_CHECK_VERSION(2,22,0)
1524 if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1526 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1529 * Button 1 selects the first package in the interval.
1530 * Button 3 selects the last package in the interval.
1532 if (io->last_interval==0xffffffff) {
1533 last_interval=io->max_interval;
1535 last_interval=io->last_interval;
1538 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1539 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1540 if (frame_num != 0) {
1541 cf_goto_frame(&cfile, frame_num);
1548 /* create a new backing pixmap of the appropriate size */
1550 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1552 io_stat_t *io = user_data;
1554 GtkAllocation widget_alloc;
1556 #if GTK_CHECK_VERSION(2,22,0)
1557 surface_info_t *surface_info = g_new(surface_info_t, 1);
1560 #if GTK_CHECK_VERSION(2,22,0)
1562 cairo_surface_destroy (io->surface);
1567 g_object_unref(io->pixmap);
1572 gtk_widget_get_allocation(widget, &widget_alloc);
1573 #if GTK_CHECK_VERSION(2,22,0)
1574 io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1575 CAIRO_CONTENT_COLOR,
1577 widget_alloc.height);
1580 io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1582 widget_alloc.height,
1585 io->pixmap_width=widget_alloc.width;
1586 io->pixmap_height=widget_alloc.height;
1588 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1589 #if GTK_CHECK_VERSION(2,22,0)
1590 surface_info->surface = io->surface;
1591 surface_info->width = widget_alloc.width;
1592 surface_info->height = widget_alloc.height;
1593 g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
1594 gtk_widget_set_sensitive(save_bt, TRUE);
1596 cr = cairo_create (io->surface);
1598 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1599 gtk_widget_set_sensitive(save_bt, TRUE);
1601 cr = gdk_cairo_create (io->pixmap);
1603 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1604 cairo_set_source_rgb (cr, 1, 1, 1);
1613 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1615 io_stat_t *io = user_data;
1618 mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1619 if(io->last_interval==mi){
1622 if( (io->last_interval==0xffffffff)
1623 && (mi==io->max_interval) ){
1627 io->last_interval=(mi/io->interval)*io->interval;
1632 #if GTK_CHECK_VERSION(3,0,0)
1634 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1636 io_stat_t *io = user_data;
1637 GtkAllocation allocation;
1639 gtk_widget_get_allocation (widget, &allocation);
1640 cairo_set_source_surface (cr, io->surface, 0, 0);
1641 cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
1647 /* redraw the screen from the backing pixmap */
1649 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1651 io_stat_t *io = user_data;
1652 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1654 #if GTK_CHECK_VERSION(2,22,0)
1655 cairo_set_source_surface (cr, io->surface, 0, 0);
1657 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1659 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1668 create_draw_area(io_stat_t *io, GtkWidget *box)
1670 io->draw_area=gtk_drawing_area_new();
1671 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1673 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1675 /* signals needed to handle backing pixmap */
1676 #if GTK_CHECK_VERSION(3,0,0)
1677 g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io);
1679 g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1681 g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1682 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1683 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1685 gtk_widget_show(io->draw_area);
1686 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1688 /* create the associated scrollbar */
1689 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1690 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1691 gtk_widget_show(io->scrollbar);
1692 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1693 g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1697 tick_interval_select(GtkWidget *item, gpointer user_data)
1699 io_stat_t *io = user_data;
1702 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1704 io->interval=tick_interval_values[i];
1705 cf_retap_packets(&cfile);
1706 gdk_window_raise(gtk_widget_get_window(io->window));
1711 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1713 io_stat_t *io = user_data;
1716 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1717 io->pixels_per_tick=pixels_per_tick[i];
1722 plot_style_select(GtkWidget *item, gpointer user_data)
1724 io_stat_graph_t *ppt = user_data;
1727 val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1729 ppt->plot_style=val;
1731 io_stat_redraw(ppt->io);
1735 create_pixels_per_tick_menu_items(io_stat_t *io)
1738 GtkWidget *combo_box;
1740 combo_box = gtk_combo_box_text_new ();
1742 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1743 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1744 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1746 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1747 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1753 yscale_select(GtkWidget *item, gpointer user_data)
1755 io_stat_t *io = user_data;
1758 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1760 io->max_y_units = yscale_max[i];
1765 filter_select(GtkWidget *item, gpointer user_data)
1767 io_stat_t *io = user_data;
1770 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1772 if(i==NO_FILTER_ORDER){
1773 io->filter_type = NO_FILTER;
1775 io->filter_type = MOVING_AVERAGE_FILTER;
1776 io->filter_order = moving_average_orders[i];
1782 create_tick_interval_menu_items(io_stat_t *io)
1784 GtkWidget *combo_box;
1788 combo_box = gtk_combo_box_text_new ();
1790 for(i=0;i<MAX_TICK_VALUES;i++){
1791 if(tick_interval_values[i]>=60000){
1792 g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1793 } else if(tick_interval_values[i]>=1000){
1794 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1795 } else if(tick_interval_values[i]>=100){
1796 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1797 } else if(tick_interval_values[i]>=10){
1798 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1800 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1802 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1804 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1805 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1811 create_yscale_max_menu_items(io_stat_t *io)
1814 GtkWidget *combo_box;
1817 combo_box = gtk_combo_box_text_new ();
1818 for(i=0;i<MAX_YSCALE;i++){
1819 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1820 g_strlcpy(str, "Logarithmic", 15);
1821 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1822 g_strlcpy(str, "Auto", 15);
1824 g_snprintf(str, 15, "%u", yscale_max[i]);
1826 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1828 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1829 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1834 create_filter_menu_items(io_stat_t *io)
1837 GtkWidget *combo_box;
1840 combo_box = gtk_combo_box_text_new ();
1842 for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){
1843 if(i==NO_FILTER_ORDER){
1844 g_strlcpy(str, "No filter", 15);
1846 g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]);
1848 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1850 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1851 g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io);
1856 count_type_select(GtkWidget *item, gpointer user_data)
1858 io_stat_t *io = user_data;
1859 static gboolean advanced_visible=FALSE;
1861 GtkAllocation widget_alloc;
1863 io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1865 if(io->count_type==COUNT_TYPE_ADVANCED){
1866 for(i=0;i<MAX_GRAPHS;i++){
1867 disable_graph(&io->graphs[i]);
1868 gtk_widget_show(io->graphs[i].advanced_buttons);
1869 /* redraw the entire window so the unhidden widgets show up, hopefully */
1870 gtk_widget_get_allocation(io->window, &widget_alloc);
1871 gtk_widget_queue_draw_area(io->window,
1875 widget_alloc.height);
1877 advanced_visible=TRUE;
1879 } else if (advanced_visible) {
1880 for(i=0;i<MAX_GRAPHS;i++){
1881 gtk_widget_hide(io->graphs[i].advanced_buttons);
1882 filter_callback(item, &io->graphs[i]);
1884 advanced_visible=FALSE;
1891 create_frames_or_bytes_menu_items(io_stat_t *io)
1893 GtkWidget *combo_box;
1896 combo_box = gtk_combo_box_text_new ();
1898 for(i=0;i<MAX_COUNT_TYPES;i++){
1899 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
1901 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1902 g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1907 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1911 GtkWidget *combo_box;
1913 hbox=gtk_hbox_new(FALSE, 0);
1914 gtk_container_add(GTK_CONTAINER(box), hbox);
1915 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1916 gtk_widget_show(hbox);
1918 label=gtk_label_new(name);
1919 gtk_widget_show(label);
1920 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1922 combo_box = (*func)(io);
1923 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1924 gtk_widget_show(combo_box);
1928 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1930 io_stat_t *io = user_data;
1932 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1938 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1940 GtkWidget *frame_vbox;
1945 frame_vbox=gtk_vbox_new(FALSE, 0);
1946 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1947 gtk_widget_show(frame_vbox);
1949 frame = gtk_frame_new("X Axis");
1950 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1951 gtk_widget_show(frame);
1953 vbox=gtk_vbox_new(FALSE, 0);
1954 gtk_container_add(GTK_CONTAINER(frame), vbox);
1955 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1956 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1957 gtk_widget_show(vbox);
1959 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1960 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1962 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1963 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1965 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1966 gtk_widget_show(view_cb);
1968 frame = gtk_frame_new("Y Axis");
1969 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1970 gtk_widget_show(frame);
1972 vbox=gtk_vbox_new(FALSE, 0);
1973 gtk_container_add(GTK_CONTAINER(frame), vbox);
1974 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1975 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1976 gtk_widget_show(vbox);
1978 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1979 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1980 create_ctrl_menu(io, vbox, "Smooth:", create_filter_menu_items);
1986 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1988 io_stat_graph_t *gio = user_data;
1990 const char *field=NULL;
1991 header_field_info *hfi;
1994 /* this graph is not active, just update display and redraw */
1995 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1997 io_stat_redraw(gio->io);
2001 /* first check if the field string is valid */
2002 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
2003 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
2005 /* warn and bail out if there was no field specified */
2006 if(field==NULL || field[0]==0){
2007 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
2009 io_stat_redraw(gio->io);
2012 /* warn and bail out if the field could not be found */
2013 hfi=proto_registrar_get_byname(field);
2015 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
2017 io_stat_redraw(gio->io);
2020 gio->hf_index=hfi->id;
2021 /* check that the type is compatible */
2033 /* these values support all calculations except LOAD */
2034 switch(gio->calc_type){
2035 case CALC_TYPE_LOAD:
2036 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2037 "LOAD(*) is only supported for relative-time fields.");
2039 io_stat_redraw(gio->io);
2042 /* these types support all calculations */
2044 case FT_RELATIVE_TIME:
2045 /* this type only supports COUNT, MAX, MIN, AVG */
2046 switch(gio->calc_type){
2048 case CALC_TYPE_COUNT:
2052 case CALC_TYPE_LOAD:
2055 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2056 "%s is a relative-time field, so %s calculations are not supported on it.",
2058 calc_type_names[gio->calc_type]);
2060 io_stat_redraw(gio->io);
2067 * XXX - support this if gint64/guint64 are
2070 if(gio->calc_type!=CALC_TYPE_COUNT){
2071 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2072 "%s is a 64-bit integer, so %s calculations are not supported on it.",
2074 calc_type_names[gio->calc_type]);
2076 io_stat_redraw(gio->io);
2081 if(gio->calc_type!=CALC_TYPE_COUNT){
2082 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2083 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
2085 calc_type_names[gio->calc_type]);
2087 io_stat_redraw(gio->io);
2094 /* first check if the filter string is valid. */
2095 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
2096 if(!dfilter_compile(filter, &dfilter)) {
2097 bad_dfilter_alert_box(filter);
2099 io_stat_redraw(gio->io);
2102 if (dfilter != NULL)
2103 dfilter_free(dfilter);
2105 /* ok, we have a valid filter and the graph is active.
2106 first just try to delete any previous settings and then apply
2109 protect_thread_critical_region();
2110 remove_tap_listener(gio);
2111 unprotect_thread_critical_region();
2113 io_stat_reset(gio->io);
2114 enable_graph(gio, filter, field);
2115 cf_retap_packets(&cfile);
2116 gdk_window_raise(gtk_widget_get_window(gio->io->window));
2117 io_stat_redraw(gio->io);
2123 calc_type_select(GtkWidget *item, gpointer user_data)
2125 io_stat_graph_t *gio = user_data;
2127 gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
2129 /* disable the graph */
2131 io_stat_redraw(gio->io);
2135 create_calc_types_menu_items(io_stat_graph_t *gio)
2137 GtkWidget *combo_box;
2140 combo_box = gtk_combo_box_text_new ();
2141 for(i=0;i<MAX_CALC_TYPES;i++){
2142 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
2144 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
2145 g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
2150 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io))
2154 GtkWidget *combo_box;
2156 hbox=gtk_hbox_new(FALSE, 0);
2157 gtk_container_add(GTK_CONTAINER(box), hbox);
2158 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2159 gtk_widget_show(hbox);
2161 label=gtk_label_new(name);
2162 gtk_widget_show(label);
2163 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2165 combo_box = (*func)(gio);
2166 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2167 gtk_widget_show(combo_box);
2171 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2174 gio->calc_field=gtk_entry_new();
2175 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
2176 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
2177 gtk_widget_show(gio->calc_field);
2178 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
2179 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
2180 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2181 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2182 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2183 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2184 colorize_filter_te_as_empty(gio->calc_field);
2188 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2192 hbox=gtk_hbox_new(FALSE, 0);
2193 gio->advanced_buttons=hbox;
2194 gtk_container_add(GTK_CONTAINER(box), hbox);
2195 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
2196 gtk_widget_hide(hbox);
2198 gio->calc_type=CALC_TYPE_SUM;
2199 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
2200 create_advanced_field(gio, hbox);
2204 filter_button_clicked(GtkWidget *w, gpointer user_data)
2206 io_stat_graph_t *gio = user_data;
2208 display_filter_construct_cb(w, gio->args);
2213 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2215 GtkWidget *combo_box;
2221 hbox=gtk_hbox_new(FALSE, 3);
2222 gtk_container_add(GTK_CONTAINER(box), hbox);
2223 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2224 gtk_widget_show(hbox);
2226 g_snprintf(str, 256, "Graph %d", num);
2227 gio->display_button=gtk_toggle_button_new_with_label(str);
2228 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2229 gtk_widget_show(gio->display_button);
2230 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2231 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2233 label=gtk_label_new("Color");
2234 gtk_widget_show(label);
2235 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2237 #if GTK_CHECK_VERSION(3,0,0)
2238 gtk_widget_override_color(label, GTK_STATE_NORMAL, &gio->rgba_color);
2239 gtk_widget_override_color(label, GTK_STATE_ACTIVE, &gio->rgba_color);
2240 gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &gio->rgba_color);
2241 gtk_widget_override_color(label, GTK_STATE_SELECTED, &gio->rgba_color);
2242 gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &gio->rgba_color);
2244 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2245 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2246 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2247 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2248 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2250 /* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2253 /* filter prefs dialog */
2254 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2256 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
2257 g_free( (gpointer) (gio->args->title) );
2258 gio->args->title=g_strdup(str);
2260 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2261 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2263 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2264 gtk_widget_show(gio->filter_bt);
2266 gio->filter_field=gtk_entry_new();
2267 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2268 /* filter prefs dialog */
2269 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2270 /* filter prefs dialog */
2272 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2273 gtk_widget_show(gio->filter_field);
2274 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2275 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2276 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2277 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2278 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2279 colorize_filter_te_as_empty(gio->filter_field);
2281 create_advanced_box(gio, hbox);
2284 * create PlotStyle menu
2286 g_snprintf(str, 256, " Style:");
2287 label=gtk_label_new(str);
2288 gtk_widget_show(label);
2289 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2291 combo_box = gtk_combo_box_text_new ();
2292 for(i=0;i<MAX_PLOT_STYLES;i++){
2293 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
2295 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2296 g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2298 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2299 gtk_widget_show(combo_box);
2305 create_filter_area(io_stat_t *io, GtkWidget *box)
2311 frame=gtk_frame_new("Graphs");
2312 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2313 gtk_widget_show(frame);
2315 vbox=gtk_vbox_new(FALSE, 1);
2316 gtk_container_add(GTK_CONTAINER(frame), vbox);
2317 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2318 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2319 gtk_widget_show(vbox);
2321 for(i=0;i<MAX_GRAPHS;i++){
2322 create_filter_box(&io->graphs[i], vbox, i+1);
2329 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2331 guint32 i, interval, val;
2334 GString *CSV_str=g_string_new("");
2335 io_stat_t *io = user_data;
2337 g_string_append(CSV_str, "\"Interval start\"");
2338 for(i=0;i<MAX_GRAPHS;i++) {
2339 if (io->graphs[i].display) {
2340 g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2343 g_string_append(CSV_str,"\n");
2345 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2346 print_interval_string (string, 15, interval, io, FALSE);
2347 g_string_append_printf(CSV_str, "\"%s\"", string);
2348 for(i=0;i<MAX_GRAPHS;i++) {
2349 if (io->graphs[i].display) {
2350 val=get_it_value(io, i, interval/io->interval);
2351 g_string_append_printf(CSV_str, ",\"%d\"", val);
2354 g_string_append(CSV_str,"\n");
2357 /* Now that we have the CSV data, copy it into the default clipboard */
2358 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2359 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2360 g_string_free(CSV_str, TRUE); /* Free the memory */
2364 init_io_stat_window(io_stat_t *io)
2369 GtkWidget *close_bt, *help_bt;
2373 /* create the main window, transient_for top_level */
2374 io->window = dlg_window_new("I/O Graphs");
2375 gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2377 vbox=gtk_vbox_new(FALSE, 0);
2378 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2379 gtk_widget_show(vbox);
2381 create_draw_area(io, vbox);
2383 hbox=gtk_hbox_new(FALSE, 3);
2384 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2385 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2386 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2387 gtk_widget_show(hbox);
2389 create_filter_area(io, hbox);
2390 create_ctrl_area(io, hbox);
2392 io_stat_set_title(io);
2394 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2395 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2396 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2397 gtk_widget_show(bbox);
2399 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2400 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2401 gtk_widget_set_tooltip_text(close_bt, "Close this dialog");
2402 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2403 gtk_widget_set_sensitive(save_bt, FALSE);
2404 gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
2405 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2406 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2408 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2409 gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
2410 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2412 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2413 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2414 gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
2415 g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2417 gtk_widget_show(io->window);
2418 window_present(io->window);
2422 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2424 iostat_init(NULL,NULL);
2428 register_tap_listener_gtk_iostat(void)
2430 register_stat_cmd_arg("io,stat", iostat_init,NULL);