Have the time field in the Graph Analyzis windos use the same time format as used...
[metze/wireshark/wip.git] / ui / gtk / io_stat.c
1 /* io_stat.c
2  * io_stat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
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.
14  *
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.
19  *
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.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <math.h>
37 #include <ctype.h>
38
39 #include <gtk/gtk.h>
40
41 #include <epan/epan_dissect.h>
42 #include <epan/packet_info.h>
43 #include <epan/stat_cmd_args.h>
44 #include <epan/tap.h>
45 #include <epan/strutil.h>
46
47 #include "../stat_menu.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
50
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"
61
62 #include "ui/gtk/old-gtk-compat.h"
63
64 #define MAX_GRAPHS 5
65
66 #define MAX_YSCALE 28
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};
71
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};
75 #define NO_FILTER 0
76 #define MOVING_AVERAGE_FILTER 1
77
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};
81
82
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] = {
90         "Line",
91         "Impulse",
92         "FBar",
93         "Dot",
94 };
95
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..."};
103
104 /* unit is in ms */
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 };
108
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(*)"};
118
119
120 typedef struct _io_stat_calc_type_t {
121         struct _io_stat_graph_t *gio;
122         int calc_type;
123 } io_stat_calc_type_t;
124
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*/
129         guint32 fields;
130         gint32 int_max;
131         gint32 int_min;
132         gint32 int_tot;
133         gfloat float_max;
134         gfloat float_min;
135         gfloat float_tot;
136         gdouble double_max;
137         gdouble double_min;
138         gdouble double_tot;
139         nstime_t time_max;
140         nstime_t time_min;
141         nstime_t time_tot;
142 } io_item_t;
143
144 typedef struct _io_stat_graph_t {
145         struct _io_stat_t *io;
146         io_item_t items[NUM_IO_ITEMS];
147         int plot_style;
148         gboolean display;
149         GtkWidget *display_button;
150         GtkWidget *filter_field;
151         GtkWidget *advanced_buttons;
152         int calc_type;
153         int hf_index;
154         GtkWidget *calc_field;
155         GdkColor color;
156 #if GTK_CHECK_VERSION(3,0,0)
157         GdkRGBA rgba_color;
158 #endif
159         construct_args_t *args;
160         GtkWidget *filter_bt;
161 } io_stat_graph_t;
162
163
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 */
169         guint32 num_items;
170         guint32 left_x_border;
171         guint32 right_x_border;
172         gboolean view_as_time;
173         nstime_t start_time;
174
175         struct _io_stat_graph_t graphs[MAX_GRAPHS];
176         GtkWidget *window;
177         GtkWidget *draw_area;
178 #if GTK_CHECK_VERSION(2,22,0)
179         cairo_surface_t *surface;
180 #else
181         GdkPixmap *pixmap;
182 #endif
183         GtkAdjustment *scrollbar_adjustment;
184         GtkWidget *scrollbar;
185         guint first_frame_num[NUM_IO_ITEMS];
186         guint last_frame_num;
187         int pixmap_width;
188         int pixmap_height;
189         int pixels_per_tick;
190         int max_y_units;
191         int count_type;
192
193         guint32 filter_order;
194         int filter_type;
195 } io_stat_t;
196
197
198 static void init_io_stat_window(io_stat_t *io);
199 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
200
201 static void
202 io_stat_set_title(io_stat_t *io)
203 {
204         char            *title;
205
206         if(!io->window){
207                 return;
208         }
209         title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
210         gtk_window_set_title(GTK_WINDOW(io->window), title);
211         g_free(title);
212 }
213
214 static void
215 io_stat_reset(io_stat_t *io)
216 {
217         int i, j;
218
219         io->needs_redraw=TRUE;
220         for(i=0;i<MAX_GRAPHS;i++){
221                 for(j=0;j<NUM_IO_ITEMS;j++){
222                         io_item_t *ioi;
223                         ioi=&io->graphs[i].items[j];
224
225                         ioi->frames=0;
226                         ioi->bytes=0;
227                         ioi->fields=0;
228                         ioi->int_max=0;
229                         ioi->int_min=0;
230                         ioi->int_tot=0;
231                         ioi->float_max=0;
232                         ioi->float_min=0;
233                         ioi->float_tot=0;
234                         ioi->double_max=0;
235                         ioi->double_min=0;
236                         ioi->double_tot=0;
237                         nstime_set_zero(&ioi->time_max);
238                         nstime_set_zero(&ioi->time_min);
239                         nstime_set_zero(&ioi->time_tot);
240                 }
241         }
242         io->last_interval=0xffffffff;
243         io->max_interval=0;
244         io->num_items=0;
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;
249         }
250         io->last_frame_num=0;
251
252         io_stat_set_title(io);
253 }
254
255 static void
256 tap_iostat_reset(void *g)
257 {
258         io_stat_graph_t *gio=g;
259
260         io_stat_reset(gio->io);
261 }
262
263 static gboolean
264 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
265 {
266         io_stat_graph_t *git=g;
267         io_item_t *it;
268         nstime_t time_delta;
269         int idx;
270
271         /* we sometimes get called when git is disabled.
272            this is a bug since the tap listener should be removed first */
273         if(!git->display){
274                 return FALSE;
275         }
276
277         git->io->needs_redraw=TRUE;
278
279         /*
280          * Find which interval this is supposed to go in and store the
281          * interval index as idx
282          */
283         time_delta=pinfo->fd->rel_ts;
284         if(time_delta.nsecs<0){
285                 time_delta.secs--;
286                 time_delta.nsecs+=1000000000;
287         }
288         if(time_delta.secs<0){
289                 return FALSE;
290         }
291         idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
292
293         /* some sanity checks */
294         if((idx<0)||(idx>=NUM_IO_ITEMS)){
295                 git->io->num_items = NUM_IO_ITEMS-1;
296                 return FALSE;
297         }
298
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;
303         }
304
305         /* set start time */
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);
308         }
309
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;
313         }
314         git->io->last_frame_num=pinfo->fd->num;
315
316         /*
317          * Find the appropriate io_item_t structure
318          */
319         it=&git->items[idx];
320
321
322         /*
323          * For ADVANCED mode we need to keep track of some more stuff
324          * than just frame and byte counts
325          */
326         if(git->io->count_type==COUNT_TYPE_ADVANCED){
327                 GPtrArray *gp;
328                 guint i;
329
330                 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
331                 if(!gp){
332                         return FALSE;
333                 }
334
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
338                  */
339                 for(i=0;i<gp->len;i++){
340                         int new_int;
341                         float new_float;
342                         double new_double;
343                         nstime_t *new_time;
344
345                         switch(proto_registrar_get_ftype(git->hf_index)){
346                         case FT_UINT8:
347                         case FT_UINT16:
348                         case FT_UINT24:
349                         case FT_UINT32:
350                                 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
351
352                                 if((new_int>it->int_max)||(it->fields==0)){
353                                         it->int_max=new_int;
354                                 }
355                                 if((new_int<it->int_min)||(it->fields==0)){
356                                         it->int_min=new_int;
357                                 }
358                                 it->int_tot+=new_int;
359                                 it->fields++;
360                                 break;
361                         case FT_INT8:
362                         case FT_INT16:
363                         case FT_INT24:
364                         case FT_INT32:
365                                 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
366                                 if((new_int>it->int_max)||(it->fields==0)){
367                                         it->int_max=new_int;
368                                 }
369                                 if((new_int<it->int_min)||(it->fields==0)){
370                                         it->int_min=new_int;
371                                 }
372                                 it->int_tot+=new_int;
373                                 it->fields++;
374                                 break;
375                         case FT_FLOAT:
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;
379                                 }
380                                 if((new_float<it->float_min)||(it->fields==0)){
381                                         it->float_min=new_float;
382                                 }
383                                 it->float_tot+=new_float;
384                                 it->fields++;
385                                 break;
386                         case FT_DOUBLE:
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;
390                                 }
391                                 if((new_double<it->double_min)||(it->fields==0)){
392                                         it->double_min=new_double;
393                                 }
394                                 it->double_tot+=new_double;
395                                 it->fields++;
396                                 break;
397                         case FT_RELATIVE_TIME:
398                                 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
399
400                                 switch(git->calc_type){
401                                         guint64 t, pt; /* time in us */
402                                         int j;
403                                 case CALC_TYPE_LOAD:
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
407                                          * to that interval.
408                                          */
409                                         t=new_time->secs;
410                                         t=t*1000000+new_time->nsecs/1000;
411                                         j=idx;
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);
415                                         if(pt>t){
416                                                 pt=t;
417                                         }
418                                         while(t){
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;
423                                                 }
424
425                                                 if(j==0){
426                                                         break;
427                                                 }
428                                                 j--;
429                                                 t-=pt;
430                                                 if(t > (guint32) (git->io->interval*1000)){
431                                                         pt=git->io->interval*1000;
432                                                 } else {
433                                                         pt=t;
434                                                 }
435                                         }
436                                         break;
437                                 default:
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))
441                                         ||(it->fields==0)){
442                                                 it->time_max=*new_time;
443                                         }
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))
447                                         ||(it->fields==0)){
448                                                 it->time_min=*new_time;
449                                         }
450                                         nstime_add(&it->time_tot, new_time);
451                                         it->fields++;
452                                 }
453                         }
454                 }
455         }
456
457         it->frames++;
458         it->bytes+=pinfo->fd->pkt_len;
459
460         return TRUE;
461 }
462
463 static guint
464 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
465 {
466         guint i, frame_num=0;
467
468         if (idx>io->num_items) {
469                 return 0;
470         }
471
472         if (first) {
473                 frame_num=io->first_frame_num[idx];
474         }
475
476         if (frame_num==0) {
477                 /*
478                  * If first frame not found we select the last
479                  * frame in the previous interval
480                  *
481                  * If selecting the last frame we select the frame
482                  * before the first frame in the next interval
483                  */
484                 for(i=idx+1;i<=io->num_items;i++) {
485                         frame_num=io->first_frame_num[i];
486                         if (frame_num != 0) {
487                                 return frame_num-1;
488                         }
489                 }
490
491                 /*
492                  * If not found we select the last frame
493                  */
494                 frame_num=io->last_frame_num;
495         }
496
497         return frame_num;
498 }
499
500 static guint32
501 get_it_value(io_stat_t *io, int graph_id, int idx)
502 {
503         double value=0;
504         int adv_type;
505         io_item_t *it;
506
507         it=&io->graphs[graph_id].items[idx];
508
509         switch(io->count_type){
510         case COUNT_TYPE_FRAMES:
511                 return it->frames;
512         case COUNT_TYPE_BYTES:
513                 return it->bytes;
514         case COUNT_TYPE_BITS:
515                 return (it->bytes * 8);
516         }
517
518
519         adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
520         switch(adv_type){
521         case FT_NONE:
522                 switch(io->graphs[graph_id].calc_type){
523                 case CALC_TYPE_COUNT:
524                         value=it->frames;
525                         break;
526                 default:
527                         break;
528                 }
529                 break;
530         case FT_UINT8:
531         case FT_UINT16:
532         case FT_UINT24:
533         case FT_UINT32:
534         case FT_INT8:
535         case FT_INT16:
536         case FT_INT24:
537         case FT_INT32:
538                 switch(io->graphs[graph_id].calc_type){
539                 case CALC_TYPE_SUM:
540                         value=it->int_tot;
541                         break;
542                 case CALC_TYPE_COUNT:
543                         value=it->frames;
544                         break;
545                 case CALC_TYPE_MAX:
546                         value=it->int_max;
547                         break;
548                 case CALC_TYPE_MIN:
549                         value=it->int_min;
550                         break;
551                 case CALC_TYPE_AVG:
552                         if(it->fields){
553                                 value=it->int_tot/it->fields;
554                         } else {
555                                 value=0;
556                         }
557                         break;
558                 default:
559                         break;
560                 }
561                 break;
562         case FT_FLOAT:
563                 switch(io->graphs[graph_id].calc_type){
564                 case CALC_TYPE_SUM:
565                         value=it->float_tot;
566                         break;
567                 case CALC_TYPE_COUNT:
568                         value=it->frames;
569                         break;
570                 case CALC_TYPE_MAX:
571                         value=it->float_max;
572                         break;
573                 case CALC_TYPE_MIN:
574                         value=it->float_min;
575                         break;
576                 case CALC_TYPE_AVG:
577                         if(it->fields){
578                                 value=it->float_tot/it->fields;
579                         } else {
580                                 value=0;
581                         }
582                         break;
583                 default:
584                         break;
585                 }
586                 break;
587         case FT_DOUBLE:
588                 switch(io->graphs[graph_id].calc_type){
589                 case CALC_TYPE_SUM:
590                         value=it->double_tot;
591                         break;
592                 case CALC_TYPE_COUNT:
593                         value=it->frames;
594                         break;
595                 case CALC_TYPE_MAX:
596                         value=it->double_max;
597                         break;
598                 case CALC_TYPE_MIN:
599                         value=it->double_min;
600                         break;
601                 case CALC_TYPE_AVG:
602                         if(it->fields){
603                                 value=it->double_tot/it->fields;
604                         } else {
605                                 value=0;
606                         }
607                         break;
608                 default:
609                         break;
610                 }
611                 break;
612         case FT_RELATIVE_TIME:
613                 switch(io->graphs[graph_id].calc_type){
614                 case CALC_TYPE_COUNT:
615                         value=it->frames;
616                         break;
617                 case CALC_TYPE_MAX:
618                         value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
619                         break;
620                 case CALC_TYPE_MIN:
621                         value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
622                         break;
623                 case CALC_TYPE_SUM:
624                         value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
625                         break;
626                 case CALC_TYPE_AVG:
627                         if(it->fields){
628                                 guint64 t; /* time in us */
629
630                                 t=it->time_tot.secs;
631                                 t=t*1000000+it->time_tot.nsecs/1000;
632                                 value=(guint32) (t/it->fields);
633                         } else {
634                                 value=0;
635                         }
636                         break;
637                 case CALC_TYPE_LOAD:
638                         value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
639                         break;
640                 default:
641                         break;
642                 }
643                 break;
644         default:
645                 break;
646         }
647         return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
648 }
649
650 static void
651 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
652 {
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);
661         } else {
662                 g_snprintf(buf, buf_len, "%dus",t);
663         }
664 }
665
666 static void
667 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
668                       gboolean ext)
669 {
670         if (io->view_as_time) {
671                 struct tm *tmp;
672                 time_t sec_val = interval/1000 + io->start_time.secs;
673                 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
674
675                 if(nsec_val >= 1000) {
676                         sec_val++;
677                         nsec_val -= 1000;
678                 }
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);
686                 } else {
687                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
688                 }
689         } else {
690                 if (!ext) {
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);
700                 } else {
701                         g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
702                 }
703         }
704 }
705
706 static void
707 io_stat_draw(io_stat_t *io)
708 {
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;
714         PangoLayout  *layout;
715         int label_width, label_height;
716         guint32 draw_width, draw_height;
717         char label_string[45];
718         GtkAllocation widget_alloc;
719
720         /* new variables */
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;
725         cairo_t *cr;
726         if(!io->needs_redraw){
727                 return;
728         }
729         io->needs_redraw=FALSE;
730
731         /*
732          * Find the length of the intervals we have data for
733          * so we know how large arrays we need to malloc()
734          */
735         num_time_intervals=io->num_items+1;
736
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");
740                 return;
741         }
742
743
744         /*
745          * find the max value so we can autoscale the y axis
746          */
747         max_value=0;
748         for(i=0;i<MAX_GRAPHS;i++){
749                 int idx;
750
751                 if(!io->graphs[i].display){
752                         continue;
753                 }
754                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
755                         guint32 val;
756
757                         val=get_it_value(io, i, idx);
758
759                         /* keep track of the max value we have encountered */
760                         if(val>max_value){
761                                 max_value=val;
762                         }
763                 }
764         }
765
766
767
768         /*
769          * Clear out old plot
770          */
771 #if GTK_CHECK_VERSION(2,22,0)
772         cr = cairo_create (io->surface);
773 #else
774         cr = gdk_cairo_create (io->pixmap);
775 #endif
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);
779         cairo_fill (cr);
780         cairo_destroy (cr);
781         /*
782          * Calculate the y scale we should use
783          */
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]){
788                                 max_y=yscale_max[i];
789                         }
790                 }
791         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
792                 max_y=1000000000;
793                 for(i=1000000000;i>1;i/=10){
794                         if(max_value<(guint32)i){
795                                 max_y=i;
796                         }
797                 }
798         } else {
799                 /* the user had specified an explicit y scale to use */
800                 max_y=io->max_y_units;
801         }
802
803
804         /*
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
811          */
812         draw_y_as_time=FALSE;
813         if(io->count_type==COUNT_TYPE_ADVANCED){
814                 draw_y_as_time=TRUE;
815                 for(i=0;i<MAX_GRAPHS;i++){
816                         int adv_type;
817
818                         if(!io->graphs[i].display){
819                                 continue;
820                         }
821                         adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
822                         switch(adv_type){
823                         case FT_RELATIVE_TIME:
824                                 switch(io->graphs[i].calc_type){
825                                 case CALC_TYPE_SUM:
826                                 case CALC_TYPE_MAX:
827                                 case CALC_TYPE_MIN:
828                                 case CALC_TYPE_AVG:
829                                         break;
830                                 default:
831                                         draw_y_as_time=FALSE;
832                                 }
833                                 break;
834                         default:
835                                 draw_y_as_time=FALSE;
836                         }
837                 }
838         }
839
840
841
842         /*
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
847          */
848         if(draw_y_as_time){
849                 if(io->max_y_units==LOGARITHMIC_YSCALE){
850                         print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
851                 } else {
852                         print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
853                 }
854         } else {
855                 g_snprintf(label_string, 15, "%d", max_y);
856         }
857         layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
858         pango_layout_get_pixel_size(layout, &label_width, &label_height);
859
860         io->left_x_border=10;
861         io->right_x_border=label_width+20;
862         top_y_border=10;
863         bottom_y_border=label_height+20;
864
865
866         /*
867          * Calculate the size of the drawing area for the actual plot
868          */
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;
871
872
873         /*
874          * Add a warning if too many entries
875          */
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);
879
880 #if GTK_CHECK_VERSION(2,22,0)
881                 cr = cairo_create (io->surface);
882 #else
883                 cr = gdk_cairo_create (io->pixmap);
884 #endif
885                 cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
886                 pango_cairo_show_layout (cr, layout);
887                 cairo_destroy (cr);
888                 cr = NULL;
889         }
890
891         /*
892          * Draw the y axis and labels
893          * (we always draw the y scale with 11 ticks along the axis)
894          */
895 #if GTK_CHECK_VERSION(2,22,0)
896         cr = cairo_create (io->surface);
897 #else
898         cr = gdk_cairo_create (io->pixmap);
899 #endif
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);
903         cairo_stroke(cr);
904         cairo_destroy(cr);
905         if(io->max_y_units==LOGARITHMIC_YSCALE){
906                 tics=(int)log10((double)max_y);
907                 ystart=draw_height/10;
908                 ys=-1;
909         } else {
910                 tics=10;
911                 ystart=ys=0;
912         }
913
914         for(i=ys;i<=tics;i++){
915                 int xwidth, lwidth, ypos;
916
917                 xwidth=5;
918                 if(io->max_y_units==LOGARITHMIC_YSCALE){
919                         if(i==ys) {
920                                 /* position for the 0 value */
921                                 ypos=io->pixmap_height-bottom_y_border;
922                         } else if(i==tics) {
923                                 /* position for the top value, do not draw logarithmic tics above graph */
924                                 ypos=io->pixmap_height-bottom_y_border-draw_height;
925                         } else {
926                                 int j;
927                                 /* draw the logarithmic tics */
928                                 for(j=2;j<10;j++) {
929                                         ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
930                                         /* draw the tick */
931 #if GTK_CHECK_VERSION(2,22,0)
932                                         cr = cairo_create (io->surface);
933 #else
934                                         cr = gdk_cairo_create (io->pixmap);
935 #endif
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);
939                                         cairo_stroke(cr);
940                                         cairo_destroy(cr);
941                                 }
942                                 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
943                         }
944                         /* all "main" logarithmic lines are slightly longer */
945                         xwidth=10;
946                 } else {
947                         if(!(i%5)){
948                                 /* first, middle and last tick are slightly longer */
949                                 xwidth=10;
950                         }
951                         ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
952                 }
953                 /* draw the tick */
954 #if GTK_CHECK_VERSION(2,22,0)
955                 cr = cairo_create (io->surface);
956 #else
957                 cr = gdk_cairo_create (io->pixmap);
958 #endif
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);
962                 cairo_stroke(cr);
963                 cairo_destroy(cr);
964                 /* draw the labels */
965                 if(xwidth==10) {
966                         guint32 value;
967                         if(io->max_y_units==LOGARITHMIC_YSCALE){
968                                 value=(guint32)(max_y/pow(10,tics-i));
969                                 if(draw_y_as_time){
970                                         print_time_scale_string(label_string, 15, value, value, TRUE);
971                                 } else {
972                                         g_snprintf(label_string, 15, "%d", value);
973                                 }
974                         } else {
975                                 value=(max_y/10)*i;
976                                 if(draw_y_as_time){
977                                         print_time_scale_string(label_string, 15, value, max_y, FALSE);
978                                 } else {
979                                         g_snprintf(label_string, 15, "%d", value);
980                                 }
981                         }
982
983                 pango_layout_set_text(layout, label_string, -1);
984                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
985
986 #if GTK_CHECK_VERSION(2,22,0)
987                         cr = cairo_create (io->surface);
988 #else
989                         cr = gdk_cairo_create (io->pixmap);
990 #endif
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);
993                         cairo_destroy (cr);
994                         cr = NULL;
995
996                 }
997         }
998
999         /*
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
1003          */
1004         if(io->last_interval==0xffffffff){
1005                 last_interval=io->max_interval;
1006         } else {
1007                 last_interval=io->last_interval;
1008         }
1009
1010
1011
1012
1013 /*XXX*/
1014         /* plot the x-scale */
1015 #if GTK_CHECK_VERSION(2,22,0)
1016                 cr = cairo_create (io->surface);
1017 #else
1018                 cr = gdk_cairo_create (io->pixmap);
1019 #endif
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);
1023                 cairo_stroke(cr);
1024                 cairo_destroy(cr);
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;
1028         } else {
1029                 first_interval=0;
1030         }
1031
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){
1034                 int x, xlen;
1035
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)))){
1040                         continue;
1041                 }
1042
1043                 if(!(current_interval%interval_delta)){
1044                         xlen=10;
1045                 } else if(!(current_interval%(interval_delta/2))){
1046                         xlen=8;
1047                 } else {
1048                         xlen=5;
1049                 }
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);
1053 #else
1054                 cr = gdk_cairo_create (io->pixmap);
1055 #endif
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);
1059                 cairo_stroke(cr);
1060                 cairo_destroy(cr);
1061                 if(xlen==10){
1062                         int lwidth, x_pos;
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);
1066
1067                         if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1068                                 x_pos=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;
1071                         } else {
1072                                 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1073                         }
1074 #if GTK_CHECK_VERSION(2,22,0)
1075                         cr = cairo_create (io->surface);
1076 #else
1077                         cr = gdk_cairo_create (io->pixmap);
1078 #endif
1079                         cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
1080                         pango_cairo_show_layout (cr, layout);
1081                         cairo_destroy (cr);
1082                         cr = NULL;
1083                 }
1084
1085         }
1086         g_object_unref(G_OBJECT(layout));
1087
1088
1089         /*
1090          * Loop over all graphs and draw them
1091          */
1092         for(i=MAX_GRAPHS-1;i>=0;i--){
1093                 guint64 val;
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;
1099
1100                 if(!io->graphs[i].display){
1101                         continue;
1102                 }
1103
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 
1108                         */
1109                         guint32 warmup_interval;
1110
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;
1114                         } else {
1115                                 warmup_interval = 0;
1116                         }
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++;
1121                                 mavg_left++;
1122                         }
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++;
1128                                 mavg_right++;
1129                         }
1130                         mavg_to_add = warmup_interval;
1131                 }
1132
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;
1139                         }
1140                 }
1141                 if(val>max_y){
1142                         prev_y_pos=0;
1143                 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1144                         if (val==0) {
1145                                 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1146                         } else {
1147                                 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1148                         }
1149                 } else {
1150                         prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1151                 }
1152
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;
1155
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){
1160                                         mavg_left++;
1161                                         if(mavg_left > io->filter_order/2){
1162                                                 mavg_left--;
1163                                                 mavg_in_average_count--;
1164                                                 mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval);
1165                                                 mavg_to_remove += io->interval;
1166                                         }
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;
1171                                         }else{
1172                                                 mavg_right--;
1173                                         }
1174                                 }
1175                                 val = mavg_cumulated / mavg_in_average_count;
1176                         }
1177
1178                         if(val>max_y){
1179                                 y_pos=0;
1180                         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1181                                 if (val==0) {
1182                                         y_pos=(guint32)(draw_height-1+top_y_border);
1183                                 } else {
1184                                         y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1185                                 }
1186                         } else {
1187                                 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1188                         }
1189
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
1194                                  */
1195                                 if( (prev_y_pos!=0) || (y_pos!=0) ){
1196 #if GTK_CHECK_VERSION(2,22,0)
1197                                         cr = cairo_create (io->surface);
1198 #else
1199                                         cr = gdk_cairo_create (io->pixmap);
1200 #endif
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);
1205                                         cairo_stroke(cr);
1206                                         cairo_destroy(cr);
1207                                 }
1208                                 break;
1209                         case PLOT_STYLE_IMPULSE:
1210                                 if(val){
1211 #if GTK_CHECK_VERSION(2,22,0)
1212                                         cr = cairo_create (io->surface);
1213 #else
1214                                         cr = gdk_cairo_create (io->pixmap);
1215 #endif
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);
1220                                         cairo_stroke(cr);
1221                                         cairo_destroy(cr);
1222                                 }
1223                                 break;
1224                         case PLOT_STYLE_FILLED_BAR:
1225                                 if(val){
1226 #if GTK_CHECK_VERSION(2,22,0)
1227                                         cr = cairo_create (io->surface);
1228 #else
1229                                         cr = gdk_cairo_create (io->pixmap);
1230 #endif
1231                                         cairo_rectangle (cr,
1232                                                 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1233                                                 y_pos+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);
1237                                         cairo_fill (cr);
1238                                         cairo_destroy (cr);
1239                                 }
1240                                 break;
1241                         case PLOT_STYLE_DOT:
1242                                 if(val){
1243 #if GTK_CHECK_VERSION(2,22,0)
1244                                         cr = cairo_create (io->surface);
1245 #else
1246                                         cr = gdk_cairo_create (io->pixmap);
1247 #endif
1248                                         cairo_arc (cr,
1249                                                 x_pos+0.5,
1250                                                 y_pos+0.5,
1251                                                 (gdouble)io->pixels_per_tick/2,
1252                                                 0,
1253                                                 2 * G_PI);
1254                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1255                                         cairo_fill (cr);
1256                                         cairo_destroy (cr);
1257                                 }
1258                                 break;
1259                         }
1260
1261                         prev_y_pos=y_pos;
1262                         prev_x_pos=x_pos;
1263                 }
1264         }
1265
1266         cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1267
1268 #if GTK_CHECK_VERSION(2,22,0)
1269         cairo_set_source_surface (cr, io->surface, 0, 0); 
1270 #else
1271         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1272 #endif
1273         cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1274         cairo_fill (cr);
1275
1276         cairo_destroy (cr);
1277
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);
1283         } else {
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));
1287         }
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);
1292
1293 }
1294
1295 static void
1296 io_stat_redraw(io_stat_t *io)
1297 {
1298         io->needs_redraw=TRUE;
1299         io_stat_draw(io);
1300 }
1301
1302 static void
1303 tap_iostat_draw(void *g)
1304 {
1305         io_stat_graph_t *git=g;
1306
1307         io_stat_draw(git->io);
1308 }
1309
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
1315    pruned
1316 */
1317 static GString *
1318 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1319 {
1320         char real_filter[262];
1321
1322         gio->display=TRUE;
1323
1324         real_filter[0]=0;
1325         if(filter){
1326                 /* skip all whitespaces */
1327                 while(*filter){
1328                         if(*filter==' '){
1329                                 filter++;
1330                                 continue;
1331                         }
1332                         if(*filter=='\t'){
1333                                 filter++;
1334                                 continue;
1335                         }
1336                         break;
1337                 }
1338                 if(*filter){
1339                         g_snprintf(real_filter, 257, "(%s)", filter);
1340                         real_filter[257]=0;
1341                 }
1342         }
1343         if(field){
1344                 /* skip all whitespaces */
1345                 while(*field){
1346                         if(*field==' '){
1347                                 field++;
1348                                 continue;
1349                         }
1350                         if(*field=='\t'){
1351                                 field++;
1352                                 continue;
1353                         }
1354                         break;
1355                 }
1356                 if(*field){
1357                         if(real_filter[0]!=0){
1358                                 g_strlcat(real_filter, " && ", 262);
1359                         }
1360                         g_strlcat(real_filter, field, 262);
1361                 }
1362         }
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);
1366 }
1367
1368 static void
1369 disable_graph(io_stat_graph_t *gio)
1370 {
1371         if (gio->display) {
1372                 gio->display=FALSE;
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),
1377                     FALSE);
1378         }
1379 }
1380
1381 static void
1382 iostat_init(const char *optarg _U_, void* userdata _U_)
1383 {
1384         io_stat_t *io;
1385         int i=0;
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 */
1392         };
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 */
1400         };
1401 #endif
1402         GString *error_string;
1403
1404         io=g_malloc(sizeof(io_stat_t));
1405         io->needs_redraw=TRUE;
1406         io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1407         io->window=NULL;
1408         io->draw_area=NULL;
1409 #if GTK_CHECK_VERSION(2,22,0)
1410         io->surface=NULL;
1411 #else
1412         io->pixmap=NULL;
1413 #endif
1414         io->scrollbar=NULL;
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;
1420         io->count_type=0;
1421         io->last_interval=0xffffffff;
1422         io->max_interval=0;
1423         io->num_items=0;
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;
1429
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;
1440 #endif
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;
1446
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;
1452
1453                 io->graphs[i].filter_bt=NULL;
1454         }
1455         io_stat_reset(io);
1456
1457         error_string=enable_graph(&io->graphs[0], NULL, NULL);
1458         g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1459 #if 0
1460         if(error_string){
1461
1462                 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1463                     error_string->str);
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;
1469                 exit(10);
1470         }
1471 #endif
1472         /* build the GUI */
1473         init_io_stat_window(io);
1474
1475         cf_retap_packets(&cfile);
1476         gdk_window_raise(gtk_widget_get_window(io->window));
1477         io_stat_redraw(io);
1478 }
1479
1480 static void
1481 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1482 {
1483         io_stat_t *io = user_data;
1484         int i;
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");
1487
1488         g_free(surface_info);
1489
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();
1495
1496                         g_free( (gpointer) (io->graphs[i].args->title) );
1497                         io->graphs[i].args->title=NULL;
1498
1499                         g_free(io->graphs[i].args);
1500                         io->graphs[i].args=NULL;
1501                 }
1502         }
1503         g_free(io);
1504
1505         return;
1506 }
1507
1508 static gboolean
1509 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1510 {
1511         io_stat_t *io = user_data;
1512         guint32 draw_width, interval, last_interval;
1513         guint frame_num;
1514
1515         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1516
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 */
1520               return FALSE;
1521         }
1522
1523 #if GTK_CHECK_VERSION(2,22,0)
1524         if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1525 #else
1526         if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1527 #endif
1528                 /*
1529                  * Button 1 selects the first package in the interval.
1530                  * Button 3 selects the last package in the interval.
1531                  */
1532                 if (io->last_interval==0xffffffff) {
1533                         last_interval=io->max_interval;
1534                 } else {
1535                         last_interval=io->last_interval;
1536                 }
1537
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);
1542                 }
1543         }
1544
1545         return TRUE;
1546 }
1547
1548 /* create a new backing pixmap of the appropriate size */
1549 static gboolean
1550 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1551 {
1552         io_stat_t *io = user_data;
1553         GtkWidget *save_bt;
1554         GtkAllocation widget_alloc;
1555         cairo_t *cr;
1556 #if GTK_CHECK_VERSION(2,22,0)
1557         surface_info_t *surface_info = g_new(surface_info_t, 1);
1558 #endif
1559
1560 #if GTK_CHECK_VERSION(2,22,0)
1561         if(io->surface){
1562                  cairo_surface_destroy (io->surface);
1563                 io->surface=NULL;
1564         }
1565 #else
1566         if(io->pixmap){
1567                 g_object_unref(io->pixmap);
1568                 io->pixmap=NULL;
1569         }
1570 #endif
1571
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,
1576                         widget_alloc.width,
1577                         widget_alloc.height);
1578
1579 #else
1580         io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1581                         widget_alloc.width,
1582                         widget_alloc.height,
1583                         -1);
1584 #endif
1585         io->pixmap_width=widget_alloc.width;
1586         io->pixmap_height=widget_alloc.height;
1587
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);
1595
1596         cr = cairo_create (io->surface);
1597 #else
1598         g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1599         gtk_widget_set_sensitive(save_bt, TRUE);
1600
1601         cr = gdk_cairo_create (io->pixmap);
1602 #endif
1603         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1604         cairo_set_source_rgb (cr, 1, 1, 1);
1605         cairo_fill (cr);
1606         cairo_destroy (cr);
1607
1608         io_stat_redraw(io);
1609         return TRUE;
1610 }
1611
1612 static void
1613 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1614 {
1615         io_stat_t *io = user_data;
1616         guint32 mi;
1617
1618         mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1619         if(io->last_interval==mi){
1620                 return;
1621         }
1622         if( (io->last_interval==0xffffffff)
1623         &&  (mi==io->max_interval) ){
1624                 return;
1625         }
1626
1627         io->last_interval=(mi/io->interval)*io->interval;
1628         io_stat_redraw(io);
1629
1630         return;
1631 }
1632 #if GTK_CHECK_VERSION(3,0,0)
1633 static gboolean
1634 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1635 {
1636         io_stat_t *io = user_data;
1637         GtkAllocation allocation;
1638
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);
1642         cairo_fill (cr);
1643
1644         return FALSE;
1645 }
1646 #else
1647 /* redraw the screen from the backing pixmap */
1648 static gboolean
1649 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1650 {
1651         io_stat_t *io = user_data;
1652         cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1653
1654 #if GTK_CHECK_VERSION(2,22,0)
1655         cairo_set_source_surface (cr, io->surface, 0, 0); 
1656 #else
1657         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1658 #endif
1659         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1660         cairo_fill (cr);
1661
1662         cairo_destroy (cr);
1663
1664         return FALSE;
1665 }
1666 #endif
1667 static void
1668 create_draw_area(io_stat_t *io, GtkWidget *box)
1669 {
1670         io->draw_area=gtk_drawing_area_new();
1671         g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1672
1673         gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1674
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);
1678 #else
1679         g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1680 #endif
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);
1684
1685         gtk_widget_show(io->draw_area);
1686         gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1687
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);
1694 }
1695
1696 static void
1697 tick_interval_select(GtkWidget *item, gpointer user_data)
1698 {
1699         io_stat_t *io = user_data;
1700         int i;
1701
1702         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1703
1704         io->interval=tick_interval_values[i];
1705         cf_retap_packets(&cfile);
1706         gdk_window_raise(gtk_widget_get_window(io->window));
1707         io_stat_redraw(io);
1708 }
1709
1710 static void
1711 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1712 {
1713         io_stat_t *io = user_data;
1714         int i;
1715
1716         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1717         io->pixels_per_tick=pixels_per_tick[i];
1718         io_stat_redraw(io);
1719 }
1720
1721 static void
1722 plot_style_select(GtkWidget *item, gpointer user_data)
1723 {
1724         io_stat_graph_t *ppt = user_data;
1725         int val;
1726
1727         val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1728
1729         ppt->plot_style=val;
1730
1731         io_stat_redraw(ppt->io);
1732 }
1733
1734 static GtkWidget *
1735 create_pixels_per_tick_menu_items(io_stat_t *io)
1736 {
1737         char str[5];
1738         GtkWidget *combo_box;
1739         int i;
1740         combo_box = gtk_combo_box_text_new ();
1741
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);
1745         }
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);
1748
1749         return combo_box;
1750 }
1751
1752 static void
1753 yscale_select(GtkWidget *item, gpointer user_data)
1754 {
1755         io_stat_t *io = user_data;
1756         int i;
1757
1758         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1759
1760         io->max_y_units = yscale_max[i];
1761         io_stat_redraw(io);
1762 }
1763
1764 static void
1765 filter_select(GtkWidget *item, gpointer user_data)
1766 {
1767         io_stat_t *io = user_data;
1768         int i;
1769
1770         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1771
1772         if(i==NO_FILTER_ORDER){
1773                 io->filter_type = NO_FILTER;
1774         } else {
1775                 io->filter_type = MOVING_AVERAGE_FILTER;
1776                 io->filter_order = moving_average_orders[i];
1777         }
1778         io_stat_redraw(io);
1779 }
1780
1781 static GtkWidget *
1782 create_tick_interval_menu_items(io_stat_t *io)
1783 {
1784         GtkWidget *combo_box;
1785         char str[15];
1786         int i;
1787
1788         combo_box = gtk_combo_box_text_new ();
1789
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);
1799                 } else {
1800                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1801                 }
1802                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1803         }
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);
1806
1807         return combo_box;
1808 }
1809
1810 static GtkWidget *
1811 create_yscale_max_menu_items(io_stat_t *io)
1812 {
1813         char str[15];
1814         GtkWidget *combo_box;
1815         int i;
1816
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);
1823                 } else {
1824                         g_snprintf(str, 15, "%u", yscale_max[i]);
1825                 }
1826                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1827         }
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);
1830         return combo_box;
1831 }
1832
1833 static GtkWidget *
1834 create_filter_menu_items(io_stat_t *io)
1835 {
1836         char str[15];
1837         GtkWidget *combo_box;
1838         int i;
1839
1840         combo_box = gtk_combo_box_text_new ();
1841
1842         for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){
1843                 if(i==NO_FILTER_ORDER){
1844                         g_strlcpy(str, "No filter", 15);
1845                 } else {
1846                         g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]);
1847                 }
1848                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1849         }
1850         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1851         g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io);
1852         return combo_box;
1853 }
1854
1855 static void
1856 count_type_select(GtkWidget *item, gpointer user_data)
1857 {
1858         io_stat_t *io = user_data;
1859         static gboolean advanced_visible=FALSE;
1860         int i;
1861         GtkAllocation widget_alloc;
1862
1863         io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1864
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,
1872                                                    0,
1873                                                    0,
1874                                                    widget_alloc.width,
1875                                                    widget_alloc.height);
1876                 }
1877                 advanced_visible=TRUE;
1878                 io_stat_redraw(io);
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]);
1883                 }
1884                 advanced_visible=FALSE;
1885         } else {
1886                 io_stat_redraw(io);
1887         }
1888 }
1889
1890 static GtkWidget *
1891 create_frames_or_bytes_menu_items(io_stat_t *io)
1892 {
1893         GtkWidget *combo_box;
1894         int i;
1895
1896         combo_box = gtk_combo_box_text_new ();
1897
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]);
1900         }
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);
1903         return combo_box;
1904 }
1905
1906 static void
1907 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1908 {
1909         GtkWidget *hbox;
1910         GtkWidget *label;
1911         GtkWidget *combo_box;
1912
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);
1917
1918         label=gtk_label_new(name);
1919         gtk_widget_show(label);
1920         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1921
1922         combo_box = (*func)(io);
1923         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1924         gtk_widget_show(combo_box);
1925 }
1926
1927 static void
1928 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1929 {
1930         io_stat_t *io = user_data;
1931
1932         io->view_as_time = io->view_as_time ? FALSE : TRUE;
1933
1934         io_stat_redraw(io);
1935 }
1936
1937 static void
1938 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1939 {
1940         GtkWidget *frame_vbox;
1941         GtkWidget *frame;
1942         GtkWidget *vbox;
1943         GtkWidget *view_cb;
1944
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);
1948
1949         frame = gtk_frame_new("X Axis");
1950         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1951         gtk_widget_show(frame);
1952
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);
1958
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);
1961
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);
1967
1968         frame = gtk_frame_new("Y Axis");
1969         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1970         gtk_widget_show(frame);
1971
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);
1977
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);
1981
1982         return;
1983 }
1984
1985 static void
1986 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1987 {
1988         io_stat_graph_t *gio = user_data;
1989         const char *filter;
1990         const char *field=NULL;
1991         header_field_info *hfi;
1992         dfilter_t *dfilter;
1993
1994         /* this graph is not active, just update display and redraw */
1995         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1996                 disable_graph(gio);
1997                 io_stat_redraw(gio->io);
1998                 return;
1999         }
2000
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));
2004
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.");
2008                         disable_graph(gio);
2009                         io_stat_redraw(gio->io);
2010                         return;
2011                 }
2012                 /* warn and bail out if the field could not be found */
2013                 hfi=proto_registrar_get_byname(field);
2014                 if(hfi==NULL){
2015                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
2016                         disable_graph(gio);
2017                         io_stat_redraw(gio->io);
2018                         return;
2019                 }
2020                 gio->hf_index=hfi->id;
2021                 /* check that the type is compatible */
2022                 switch(hfi->type){
2023                 case FT_UINT8:
2024                 case FT_UINT16:
2025                 case FT_UINT24:
2026                 case FT_UINT32:
2027                 case FT_INT8:
2028                 case FT_INT16:
2029                 case FT_INT24:
2030                 case FT_INT32:
2031                 case FT_FLOAT:
2032                 case FT_DOUBLE:
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.");
2038                                 disable_graph(gio);
2039                                 io_stat_redraw(gio->io);
2040                                 return;
2041                         }
2042                         /* these types support all calculations */
2043                         break;
2044                 case FT_RELATIVE_TIME:
2045                         /* this type only supports COUNT, MAX, MIN, AVG */
2046                         switch(gio->calc_type){
2047                         case CALC_TYPE_SUM:
2048                         case CALC_TYPE_COUNT:
2049                         case CALC_TYPE_MAX:
2050                         case CALC_TYPE_MIN:
2051                         case CALC_TYPE_AVG:
2052                         case CALC_TYPE_LOAD:
2053                                 break;
2054                         default:
2055                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2056                                     "%s is a relative-time field, so %s calculations are not supported on it.",
2057                                     field,
2058                                     calc_type_names[gio->calc_type]);
2059                                 disable_graph(gio);
2060                                 io_stat_redraw(gio->io);
2061                                 return;
2062                         }
2063                         break;
2064                 case FT_UINT64:
2065                 case FT_INT64:
2066                         /*
2067                          * XXX - support this if gint64/guint64 are
2068                          * available?
2069                          */
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.",
2073                                     field,
2074                                     calc_type_names[gio->calc_type]);
2075                                 disable_graph(gio);
2076                                 io_stat_redraw(gio->io);
2077                                 return;
2078                         }
2079                         break;
2080                 default:
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.",
2084                                     field,
2085                                     calc_type_names[gio->calc_type]);
2086                                 disable_graph(gio);
2087                                 io_stat_redraw(gio->io);
2088                                 return;
2089                         }
2090                         break;
2091                 }
2092         }
2093
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);
2098                 disable_graph(gio);
2099                 io_stat_redraw(gio->io);
2100                 return;
2101         }
2102         if (dfilter != NULL)
2103                 dfilter_free(dfilter);
2104
2105         /* ok, we have a valid filter and the graph is active.
2106            first just try to delete any previous settings and then apply
2107            the new ones.
2108         */
2109         protect_thread_critical_region();
2110         remove_tap_listener(gio);
2111         unprotect_thread_critical_region();
2112
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);
2118
2119         return;
2120 }
2121
2122 static void
2123 calc_type_select(GtkWidget *item, gpointer user_data)
2124 {
2125         io_stat_graph_t *gio = user_data;
2126
2127         gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
2128
2129         /* disable the graph */
2130         disable_graph(gio);
2131         io_stat_redraw(gio->io);
2132 }
2133
2134 static GtkWidget *
2135 create_calc_types_menu_items(io_stat_graph_t *gio)
2136 {
2137         GtkWidget *combo_box;
2138         int i;
2139
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]);
2143         }
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);
2146         return combo_box;
2147 }
2148
2149 static void
2150 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name,  GtkWidget *(*func)(io_stat_graph_t *io))
2151 {
2152         GtkWidget *hbox;
2153         GtkWidget *label;
2154         GtkWidget *combo_box;
2155
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);
2160
2161         label=gtk_label_new(name);
2162         gtk_widget_show(label);
2163         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2164
2165         combo_box = (*func)(gio);
2166         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2167         gtk_widget_show(combo_box);
2168 }
2169
2170 static void
2171 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2172 {
2173
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);
2185 }
2186
2187 static void
2188 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2189 {
2190         GtkWidget *hbox;
2191
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);
2197
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);
2201 }
2202
2203 static void
2204 filter_button_clicked(GtkWidget *w, gpointer user_data)
2205 {
2206         io_stat_graph_t *gio = user_data;
2207
2208         display_filter_construct_cb(w, gio->args);
2209         return;
2210 }
2211
2212 static void
2213 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2214 {
2215         GtkWidget *combo_box;
2216         GtkWidget *hbox;
2217         GtkWidget *label;
2218         char str[256];
2219         int i;
2220
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);
2225
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);
2232
2233         label=gtk_label_new("Color");
2234         gtk_widget_show(label);
2235         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2236
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);
2243 #else   
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);
2249 #endif
2250 /*      g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2251
2252
2253         /* filter prefs dialog */
2254         gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2255
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);
2259
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);
2262
2263         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2264         gtk_widget_show(gio->filter_bt);
2265
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 */
2271
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);
2280
2281         create_advanced_box(gio, hbox);
2282
2283         /*
2284          * create PlotStyle menu
2285          */
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);
2290
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]);
2294         }
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]);
2297
2298         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2299         gtk_widget_show(combo_box);
2300
2301         return;
2302 }
2303
2304 static void
2305 create_filter_area(io_stat_t *io, GtkWidget *box)
2306 {
2307         GtkWidget *frame;
2308         GtkWidget *vbox;
2309         int i;
2310
2311         frame=gtk_frame_new("Graphs");
2312         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2313         gtk_widget_show(frame);
2314
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);
2320
2321         for(i=0;i<MAX_GRAPHS;i++){
2322                 create_filter_box(&io->graphs[i], vbox, i+1);
2323         }
2324
2325         return;
2326 }
2327
2328 static void
2329 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2330 {
2331         guint32         i, interval, val;
2332         char            string[15];
2333         GtkClipboard    *cb;
2334         GString         *CSV_str=g_string_new("");
2335         io_stat_t       *io = user_data;
2336
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);
2341                 }
2342         }
2343         g_string_append(CSV_str,"\n");
2344
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);
2352                         }
2353                 }
2354                 g_string_append(CSV_str,"\n");
2355         }
2356
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 */
2361 }
2362
2363 static void
2364 init_io_stat_window(io_stat_t *io)
2365 {
2366         GtkWidget *vbox;
2367         GtkWidget *hbox;
2368         GtkWidget *bbox;
2369         GtkWidget *close_bt, *help_bt;
2370         GtkWidget *copy_bt;
2371         GtkWidget *save_bt;
2372
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);
2376
2377         vbox=gtk_vbox_new(FALSE, 0);
2378         gtk_container_add(GTK_CONTAINER(io->window), vbox);
2379         gtk_widget_show(vbox);
2380
2381         create_draw_area(io, vbox);
2382
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);
2388
2389         create_filter_area(io, hbox);
2390         create_ctrl_area(io, hbox);
2391
2392         io_stat_set_title(io);
2393
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);
2398
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);
2407
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);
2411
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);
2416
2417         gtk_widget_show(io->window);
2418         window_present(io->window);
2419 }
2420
2421 void
2422 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2423 {
2424         iostat_init(NULL,NULL);
2425 }
2426
2427 void
2428 register_tap_listener_gtk_iostat(void)
2429 {
2430         register_stat_cmd_arg("io,stat", iostat_init,NULL);
2431 }