Make colors work on GTK3
[obnox/wireshark/wip.git] / 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 "gtk/gtkglobals.h"
52 #include "gtk/gui_utils.h"
53 #include "gtk/gui_stat_menu.h"
54 #include "gtk/stock_icons.h"
55 #include "gtk/dlg_utils.h"
56 #include "gtk/filter_dlg.h"
57 #include "gtk/help_dlg.h"
58 #include "gtk/pixmap_save.h"
59 #include "gtk/main.h"
60 #include "gtk/filter_autocomplete.h"
61
62 #include "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, 2, 5, 10, 20, 50, 100, 250, 500, 1000};
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;
1097                 guint64 mavg_cumulated;
1098                 guint32 mavg_to_remove;
1099
1100                 if(!io->graphs[i].display){
1101                         continue;
1102                 }
1103
1104                 /* initialize prev x/y to the value of the first interval */
1105                 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1106                 val=get_it_value(io, i, first_interval/io->interval);
1107                 if(val>max_y){
1108                         prev_y_pos=0;
1109                 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1110                         if (val==0) {
1111                                 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1112                         } else {
1113                                 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1114                         }
1115                 } else {
1116                         prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1117                 }
1118                 /* Moving average initialization */
1119                 mavg_to_remove = first_interval;
1120                 mavg_in_average_count = 0;
1121                 mavg_cumulated = 0;
1122
1123                 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1124                         x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1125
1126                         val=get_it_value(io, i, interval/io->interval);
1127                         /* Moving average calculation */
1128                         if(io->filter_type == MOVING_AVERAGE_FILTER){
1129                                 mavg_cumulated += val;
1130                                 mavg_in_average_count++;
1131                                 if(mavg_in_average_count > io->filter_order){
1132                                         mavg_in_average_count--;
1133                                         mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval);
1134                                         mavg_to_remove += io->interval;
1135                                 }
1136                                 val = mavg_cumulated / mavg_in_average_count;
1137                         }
1138
1139                         if(val>max_y){
1140                                 y_pos=0;
1141                         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1142                                 if (val==0) {
1143                                         y_pos=(guint32)(draw_height-1+top_y_border);
1144                                 } else {
1145                                         y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1146                                 }
1147                         } else {
1148                                 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1149                         }
1150
1151                         switch(io->graphs[i].plot_style){
1152                         case PLOT_STYLE_LINE:
1153                                 /* dont need to draw anything if the segment
1154                                  * is entirely above the top of the graph
1155                                  */
1156                                 if( (prev_y_pos!=0) || (y_pos!=0) ){
1157 #if GTK_CHECK_VERSION(2,22,0)
1158                                         cr = cairo_create (io->surface);
1159 #else
1160                                         cr = gdk_cairo_create (io->pixmap);
1161 #endif
1162                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1163                                         cairo_set_line_width (cr, 1.0);
1164                                         cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
1165                                         cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1166                                         cairo_stroke(cr);
1167                                         cairo_destroy(cr);
1168                                 }
1169                                 break;
1170                         case PLOT_STYLE_IMPULSE:
1171                                 if(val){
1172 #if GTK_CHECK_VERSION(2,22,0)
1173                                         cr = cairo_create (io->surface);
1174 #else
1175                                         cr = gdk_cairo_create (io->pixmap);
1176 #endif
1177                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1178                                         cairo_set_line_width (cr, 1.0);
1179                                         cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1180                                         cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1181                                         cairo_stroke(cr);
1182                                         cairo_destroy(cr);
1183                                 }
1184                                 break;
1185                         case PLOT_STYLE_FILLED_BAR:
1186                                 if(val){
1187 #if GTK_CHECK_VERSION(2,22,0)
1188                                         cr = cairo_create (io->surface);
1189 #else
1190                                         cr = gdk_cairo_create (io->pixmap);
1191 #endif
1192                                         cairo_rectangle (cr,
1193                                                 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1194                                                 y_pos+0.5,
1195                                                 io->pixels_per_tick,
1196                                                 draw_height-1+top_y_border-y_pos);
1197                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1198                                         cairo_fill (cr);
1199                                         cairo_destroy (cr);
1200                                 }
1201                                 break;
1202                         case PLOT_STYLE_DOT:
1203                                 if(val){
1204 #if GTK_CHECK_VERSION(2,22,0)
1205                                         cr = cairo_create (io->surface);
1206 #else
1207                                         cr = gdk_cairo_create (io->pixmap);
1208 #endif
1209                                         cairo_arc (cr,
1210                                                 x_pos+0.5,
1211                                                 y_pos+0.5,
1212                                                 (gdouble)io->pixels_per_tick/2,
1213                                                 0,
1214                                                 2 * G_PI);
1215                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1216                                         cairo_fill (cr);
1217                                         cairo_destroy (cr);
1218                                 }
1219                                 break;
1220                         }
1221
1222                         prev_y_pos=y_pos;
1223                         prev_x_pos=x_pos;
1224                 }
1225         }
1226
1227         cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1228
1229 #if GTK_CHECK_VERSION(2,22,0)
1230         cairo_set_source_surface (cr, io->surface, 0, 0); 
1231 #else
1232         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1233 #endif
1234         cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1235         cairo_fill (cr);
1236
1237         cairo_destroy (cr);
1238
1239         /* update the scrollbar */
1240         if (io->max_interval == 0) {
1241                 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
1242                 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
1243                 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
1244         } else {
1245                 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
1246                 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1247                 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1248         }
1249         gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
1250         gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
1251         gtk_adjustment_changed(io->scrollbar_adjustment);
1252         gtk_adjustment_value_changed(io->scrollbar_adjustment);
1253
1254 }
1255
1256 static void
1257 io_stat_redraw(io_stat_t *io)
1258 {
1259         io->needs_redraw=TRUE;
1260         io_stat_draw(io);
1261 }
1262
1263 static void
1264 tap_iostat_draw(void *g)
1265 {
1266         io_stat_graph_t *git=g;
1267
1268         io_stat_draw(git->io);
1269 }
1270
1271 /* ok we get called with both the filter and the field.
1272    make sure the field is part of the filter.
1273    (make sure and make sure  just append it)
1274    the field MUST be part of the filter or else we wont
1275    be able to pick up the field values after the edt tree has been
1276    pruned
1277 */
1278 static GString *
1279 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1280 {
1281         char real_filter[262];
1282
1283         gio->display=TRUE;
1284
1285         real_filter[0]=0;
1286         if(filter){
1287                 /* skip all whitespaces */
1288                 while(*filter){
1289                         if(*filter==' '){
1290                                 filter++;
1291                                 continue;
1292                         }
1293                         if(*filter=='\t'){
1294                                 filter++;
1295                                 continue;
1296                         }
1297                         break;
1298                 }
1299                 if(*filter){
1300                         g_snprintf(real_filter, 257, "(%s)", filter);
1301                         real_filter[257]=0;
1302                 }
1303         }
1304         if(field){
1305                 /* skip all whitespaces */
1306                 while(*field){
1307                         if(*field==' '){
1308                                 field++;
1309                                 continue;
1310                         }
1311                         if(*field=='\t'){
1312                                 field++;
1313                                 continue;
1314                         }
1315                         break;
1316                 }
1317                 if(*field){
1318                         if(real_filter[0]!=0){
1319                                 g_strlcat(real_filter, " && ", 262);
1320                         }
1321                         g_strlcat(real_filter, field, 262);
1322                 }
1323         }
1324         return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1325                                      TL_REQUIRES_PROTO_TREE,
1326                                      tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1327 }
1328
1329 static void
1330 disable_graph(io_stat_graph_t *gio)
1331 {
1332         if (gio->display) {
1333                 gio->display=FALSE;
1334                 protect_thread_critical_region();
1335                 remove_tap_listener(gio);
1336                 unprotect_thread_critical_region();
1337                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1338                     FALSE);
1339         }
1340 }
1341
1342 static void
1343 iostat_init(const char *optarg _U_, void* userdata _U_)
1344 {
1345         io_stat_t *io;
1346         int i=0;
1347         static GdkColor col[MAX_GRAPHS] = {
1348                 {0,     0x0000, 0x0000, 0x0000}, /* Black */
1349                 {0,     0xffff, 0x0000, 0x0000}, /* Red */
1350                 {0,     0x0000, 0xffff, 0x0000}, /* Green */
1351                 {0,     0x0000, 0x0000, 0xffff}, /* Blue */
1352                 {0,     0xffff, 0x5000, 0xffff}  /* Light brilliant magenta */
1353         };
1354 #if GTK_CHECK_VERSION(3,0,0)
1355         static GdkRGBA rgba_col[MAX_GRAPHS] = {
1356                 {0.0, 0.0,   0.0,   1.0}, /* Black */
1357                 {1.0, 0.0,   0.1,   1.0}, /* Red */
1358                 {0.0, 1.0,   0.0,   1.0}, /* Green */
1359                 {0.0, 0.0,   1.0,   1.0}, /* Blue */
1360                 {1.0, 0.314, 1.0,   1.0}  /* Light brilliant magenta */
1361         };
1362 #endif
1363         GString *error_string;
1364
1365         io=g_malloc(sizeof(io_stat_t));
1366         io->needs_redraw=TRUE;
1367         io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1368         io->window=NULL;
1369         io->draw_area=NULL;
1370 #if GTK_CHECK_VERSION(2,22,0)
1371         io->surface=NULL;
1372 #else
1373         io->pixmap=NULL;
1374 #endif
1375         io->scrollbar=NULL;
1376         io->scrollbar_adjustment=NULL;
1377         io->pixmap_width=500;
1378         io->pixmap_height=200;
1379         io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1380         io->max_y_units=AUTO_MAX_YSCALE;
1381         io->count_type=0;
1382         io->last_interval=0xffffffff;
1383         io->max_interval=0;
1384         io->num_items=0;
1385         io->left_x_border=0;
1386         io->right_x_border=500;
1387         io->view_as_time=FALSE;
1388         io->start_time.secs=0;
1389         io->start_time.nsecs=0;
1390
1391         for(i=0;i<MAX_GRAPHS;i++){
1392                 io->graphs[i].color.pixel=col[i].pixel;
1393                 io->graphs[i].color.red=col[i].red;
1394                 io->graphs[i].color.green=col[i].green;
1395                 io->graphs[i].color.blue=col[i].blue;
1396 #if GTK_CHECK_VERSION(3,0,0)
1397                 io->graphs[i].rgba_color.red=rgba_col[i].red;
1398                 io->graphs[i].rgba_color.green=rgba_col[i].green;
1399                 io->graphs[i].rgba_color.blue=rgba_col[i].blue;
1400                 io->graphs[i].rgba_color.alpha=rgba_col[i].alpha;
1401 #endif
1402                 io->graphs[i].display=0;
1403                 io->graphs[i].display_button=NULL;
1404                 io->graphs[i].filter_field=NULL;
1405                 io->graphs[i].advanced_buttons=NULL;
1406                 io->graphs[i].io=io;
1407
1408                 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1409                 io->graphs[i].args->title = NULL;
1410                 io->graphs[i].args->wants_apply_button=TRUE;
1411                 io->graphs[i].args->activate_on_ok=TRUE;
1412                 io->graphs[i].args->modal_and_transient=FALSE;
1413
1414                 io->graphs[i].filter_bt=NULL;
1415         }
1416         io_stat_reset(io);
1417
1418         error_string=enable_graph(&io->graphs[0], NULL, NULL);
1419         g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1420 #if 0
1421         if(error_string){
1422
1423                 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1424                     error_string->str);
1425                 g_string_free(error_string, TRUE);
1426                 io->graphs[0].display=0;
1427                 io->graphs[0].display_button=NULL;
1428                 io->graphs[0].filter_field=NULL;
1429                 io->graphs[0].advanced_buttons=NULL;
1430                 exit(10);
1431         }
1432 #endif
1433         /* build the GUI */
1434         init_io_stat_window(io);
1435
1436         cf_retap_packets(&cfile);
1437         gdk_window_raise(gtk_widget_get_window(io->window));
1438         io_stat_redraw(io);
1439 }
1440
1441 static void
1442 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1443 {
1444         io_stat_t *io = user_data;
1445         int i;
1446         GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1447         surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
1448
1449         g_free(surface_info);
1450
1451         for(i=0;i<MAX_GRAPHS;i++){
1452                 if(io->graphs[i].display){
1453                         protect_thread_critical_region();
1454                         remove_tap_listener(&io->graphs[i]);
1455                         unprotect_thread_critical_region();
1456
1457                         g_free( (gpointer) (io->graphs[i].args->title) );
1458                         io->graphs[i].args->title=NULL;
1459
1460                         g_free(io->graphs[i].args);
1461                         io->graphs[i].args=NULL;
1462                 }
1463         }
1464         g_free(io);
1465
1466         return;
1467 }
1468
1469 static gboolean
1470 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1471 {
1472         io_stat_t *io = user_data;
1473         guint32 draw_width, interval, last_interval;
1474         guint frame_num;
1475
1476         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1477
1478         if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1479             (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1480               /* Outside draw area */
1481               return FALSE;
1482         }
1483
1484 #if GTK_CHECK_VERSION(2,22,0)
1485         if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1486 #else
1487         if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1488 #endif
1489                 /*
1490                  * Button 1 selects the first package in the interval.
1491                  * Button 3 selects the last package in the interval.
1492                  */
1493                 if (io->last_interval==0xffffffff) {
1494                         last_interval=io->max_interval;
1495                 } else {
1496                         last_interval=io->last_interval;
1497                 }
1498
1499                 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1500                 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1501                 if (frame_num != 0) {
1502                         cf_goto_frame(&cfile, frame_num);
1503                 }
1504         }
1505
1506         return TRUE;
1507 }
1508
1509 /* create a new backing pixmap of the appropriate size */
1510 static gboolean
1511 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1512 {
1513         io_stat_t *io = user_data;
1514         GtkWidget *save_bt;
1515         GtkAllocation widget_alloc;
1516         cairo_t *cr;
1517 #if GTK_CHECK_VERSION(2,22,0)
1518         surface_info_t *surface_info = g_new(surface_info_t, 1);
1519 #endif
1520
1521 #if GTK_CHECK_VERSION(2,22,0)
1522         if(io->surface){
1523                  cairo_surface_destroy (io->surface);
1524                 io->surface=NULL;
1525         }
1526 #else
1527         if(io->pixmap){
1528                 g_object_unref(io->pixmap);
1529                 io->pixmap=NULL;
1530         }
1531 #endif
1532
1533         gtk_widget_get_allocation(widget, &widget_alloc);
1534 #if GTK_CHECK_VERSION(2,22,0)
1535         io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1536                         CAIRO_CONTENT_COLOR,
1537                         widget_alloc.width,
1538                         widget_alloc.height);
1539
1540 #else
1541         io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1542                         widget_alloc.width,
1543                         widget_alloc.height,
1544                         -1);
1545 #endif
1546         io->pixmap_width=widget_alloc.width;
1547         io->pixmap_height=widget_alloc.height;
1548
1549         save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1550 #if GTK_CHECK_VERSION(2,22,0)
1551         surface_info->surface = io->surface;
1552         surface_info->width = widget_alloc.width;
1553         surface_info->height = widget_alloc.height;
1554         g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
1555         gtk_widget_set_sensitive(save_bt, TRUE);
1556
1557         cr = cairo_create (io->surface);
1558 #else
1559         g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1560         gtk_widget_set_sensitive(save_bt, TRUE);
1561
1562         cr = gdk_cairo_create (io->pixmap);
1563 #endif
1564         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1565         cairo_set_source_rgb (cr, 1, 1, 1);
1566         cairo_fill (cr);
1567         cairo_destroy (cr);
1568
1569         io_stat_redraw(io);
1570         return TRUE;
1571 }
1572
1573 static void
1574 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1575 {
1576         io_stat_t *io = user_data;
1577         guint32 mi;
1578
1579         mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1580         if(io->last_interval==mi){
1581                 return;
1582         }
1583         if( (io->last_interval==0xffffffff)
1584         &&  (mi==io->max_interval) ){
1585                 return;
1586         }
1587
1588         io->last_interval=(mi/io->interval)*io->interval;
1589         io_stat_redraw(io);
1590
1591         return;
1592 }
1593 #if GTK_CHECK_VERSION(3,0,0)
1594 static gboolean
1595 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1596 {
1597         io_stat_t *io = user_data;
1598         GtkAllocation allocation;
1599
1600         gtk_widget_get_allocation (widget, &allocation);
1601         cairo_set_source_surface (cr, io->surface, 0, 0); 
1602         cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
1603         cairo_fill (cr);
1604
1605         return FALSE;
1606 }
1607 #else
1608 /* redraw the screen from the backing pixmap */
1609 static gboolean
1610 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1611 {
1612         io_stat_t *io = user_data;
1613         cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1614
1615 #if GTK_CHECK_VERSION(2,22,0)
1616         cairo_set_source_surface (cr, io->surface, 0, 0); 
1617 #else
1618         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1619 #endif
1620         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1621         cairo_fill (cr);
1622
1623         cairo_destroy (cr);
1624
1625         return FALSE;
1626 }
1627 #endif
1628 static void
1629 create_draw_area(io_stat_t *io, GtkWidget *box)
1630 {
1631         io->draw_area=gtk_drawing_area_new();
1632         g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1633
1634         gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1635
1636         /* signals needed to handle backing pixmap */
1637 #if GTK_CHECK_VERSION(3,0,0)
1638         g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io);
1639 #else
1640         g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1641 #endif
1642         g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1643         gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1644         g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1645
1646         gtk_widget_show(io->draw_area);
1647         gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1648
1649         /* create the associated scrollbar */
1650         io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1651         io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1652         gtk_widget_show(io->scrollbar);
1653         gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1654         g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1655 }
1656
1657 static void
1658 tick_interval_select(GtkWidget *item, gpointer user_data)
1659 {
1660         io_stat_t *io = user_data;
1661         int i;
1662
1663         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1664
1665         io->interval=tick_interval_values[i];
1666         cf_retap_packets(&cfile);
1667         gdk_window_raise(gtk_widget_get_window(io->window));
1668         io_stat_redraw(io);
1669 }
1670
1671 static void
1672 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1673 {
1674         io_stat_t *io = user_data;
1675         int i;
1676
1677         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1678         io->pixels_per_tick=pixels_per_tick[i];
1679         io_stat_redraw(io);
1680 }
1681
1682 static void
1683 plot_style_select(GtkWidget *item, gpointer user_data)
1684 {
1685         io_stat_graph_t *ppt = user_data;
1686         int val;
1687
1688         val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1689
1690         ppt->plot_style=val;
1691
1692         io_stat_redraw(ppt->io);
1693 }
1694
1695 static GtkWidget *
1696 create_pixels_per_tick_menu_items(io_stat_t *io)
1697 {
1698         char str[5];
1699         GtkWidget *combo_box;
1700         int i;
1701         combo_box = gtk_combo_box_text_new ();
1702
1703         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1704                 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1705                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1706         }
1707         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1708         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1709
1710         return combo_box;
1711 }
1712
1713 static void
1714 yscale_select(GtkWidget *item, gpointer user_data)
1715 {
1716         io_stat_t *io = user_data;
1717         int i;
1718
1719         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1720
1721         io->max_y_units = yscale_max[i];
1722         io_stat_redraw(io);
1723 }
1724
1725 static void
1726 filter_select(GtkWidget *item, gpointer user_data)
1727 {
1728         io_stat_t *io = user_data;
1729         int i;
1730
1731         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1732
1733         if(i==NO_FILTER_ORDER){
1734                 io->filter_type = NO_FILTER;
1735         } else {
1736                 io->filter_type = MOVING_AVERAGE_FILTER;
1737                 io->filter_order = moving_average_orders[i];
1738         }
1739         io_stat_redraw(io);
1740 }
1741
1742 static GtkWidget *
1743 create_tick_interval_menu_items(io_stat_t *io)
1744 {
1745         GtkWidget *combo_box;
1746         char str[15];
1747         int i;
1748
1749         combo_box = gtk_combo_box_text_new ();
1750
1751         for(i=0;i<MAX_TICK_VALUES;i++){
1752                 if(tick_interval_values[i]>=60000){
1753                         g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1754                 } else if(tick_interval_values[i]>=1000){
1755                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1756                 } else if(tick_interval_values[i]>=100){
1757                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1758                 } else if(tick_interval_values[i]>=10){
1759                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1760                 } else {
1761                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1762                 }
1763                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1764         }
1765         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1766         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1767
1768         return combo_box;
1769 }
1770
1771 static GtkWidget *
1772 create_yscale_max_menu_items(io_stat_t *io)
1773 {
1774         char str[15];
1775         GtkWidget *combo_box;
1776         int i;
1777
1778         combo_box = gtk_combo_box_text_new ();
1779         for(i=0;i<MAX_YSCALE;i++){
1780                 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1781                         g_strlcpy(str, "Logarithmic", 15);
1782                 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1783                         g_strlcpy(str, "Auto", 15);
1784                 } else {
1785                         g_snprintf(str, 15, "%u", yscale_max[i]);
1786                 }
1787                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1788         }
1789         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1790         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1791         return combo_box;
1792 }
1793
1794 static GtkWidget *
1795 create_filter_menu_items(io_stat_t *io)
1796 {
1797         char str[15];
1798         GtkWidget *combo_box;
1799         int i;
1800
1801         combo_box = gtk_combo_box_text_new ();
1802
1803         for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){
1804                 if(i==NO_FILTER_ORDER){
1805                         g_strlcpy(str, "No filter", 15);
1806                 } else {
1807                         g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]);
1808                 }
1809                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1810         }
1811         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1812         g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io);
1813         return combo_box;
1814 }
1815
1816 static void
1817 count_type_select(GtkWidget *item, gpointer user_data)
1818 {
1819         io_stat_t *io = user_data;
1820         static gboolean advanced_visible=FALSE;
1821         int i;
1822         GtkAllocation widget_alloc;
1823
1824         io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1825
1826         if(io->count_type==COUNT_TYPE_ADVANCED){
1827                 for(i=0;i<MAX_GRAPHS;i++){
1828                         disable_graph(&io->graphs[i]);
1829                         gtk_widget_show(io->graphs[i].advanced_buttons);
1830                         /* redraw the entire window so the unhidden widgets show up, hopefully */
1831                         gtk_widget_get_allocation(io->window, &widget_alloc);
1832                         gtk_widget_queue_draw_area(io->window,
1833                                                    0,
1834                                                    0,
1835                                                    widget_alloc.width,
1836                                                    widget_alloc.height);
1837                 }
1838                 advanced_visible=TRUE;
1839                 io_stat_redraw(io);
1840         } else if (advanced_visible) {
1841                 for(i=0;i<MAX_GRAPHS;i++){
1842                         gtk_widget_hide(io->graphs[i].advanced_buttons);
1843                         filter_callback(item, &io->graphs[i]);
1844                 }
1845                 advanced_visible=FALSE;
1846         } else {
1847                 io_stat_redraw(io);
1848         }
1849 }
1850
1851 static GtkWidget *
1852 create_frames_or_bytes_menu_items(io_stat_t *io)
1853 {
1854         GtkWidget *combo_box;
1855         int i;
1856
1857         combo_box = gtk_combo_box_text_new ();
1858
1859         for(i=0;i<MAX_COUNT_TYPES;i++){
1860                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
1861         }
1862         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1863         g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1864         return combo_box;
1865 }
1866
1867 static void
1868 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1869 {
1870         GtkWidget *hbox;
1871         GtkWidget *label;
1872         GtkWidget *combo_box;
1873
1874         hbox=gtk_hbox_new(FALSE, 0);
1875         gtk_container_add(GTK_CONTAINER(box), hbox);
1876         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1877         gtk_widget_show(hbox);
1878
1879         label=gtk_label_new(name);
1880         gtk_widget_show(label);
1881         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1882
1883         combo_box = (*func)(io);
1884         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1885         gtk_widget_show(combo_box);
1886 }
1887
1888 static void
1889 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1890 {
1891         io_stat_t *io = user_data;
1892
1893         io->view_as_time = io->view_as_time ? FALSE : TRUE;
1894
1895         io_stat_redraw(io);
1896 }
1897
1898 static void
1899 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1900 {
1901         GtkWidget *frame_vbox;
1902         GtkWidget *frame;
1903         GtkWidget *vbox;
1904         GtkWidget *view_cb;
1905
1906         frame_vbox=gtk_vbox_new(FALSE, 0);
1907         gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1908         gtk_widget_show(frame_vbox);
1909
1910         frame = gtk_frame_new("X Axis");
1911         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1912         gtk_widget_show(frame);
1913
1914         vbox=gtk_vbox_new(FALSE, 0);
1915         gtk_container_add(GTK_CONTAINER(frame), vbox);
1916         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1917         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1918         gtk_widget_show(vbox);
1919
1920         create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1921         create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1922
1923         view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1924         gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1925         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1926         g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1927         gtk_widget_show(view_cb);
1928
1929         frame = gtk_frame_new("Y Axis");
1930         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1931         gtk_widget_show(frame);
1932
1933         vbox=gtk_vbox_new(FALSE, 0);
1934         gtk_container_add(GTK_CONTAINER(frame), vbox);
1935         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1936         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1937         gtk_widget_show(vbox);
1938
1939         create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1940         create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1941         create_ctrl_menu(io, vbox, "Filter:", create_filter_menu_items);
1942
1943         return;
1944 }
1945
1946 static void
1947 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1948 {
1949         io_stat_graph_t *gio = user_data;
1950         const char *filter;
1951         const char *field=NULL;
1952         header_field_info *hfi;
1953         dfilter_t *dfilter;
1954
1955         /* this graph is not active, just update display and redraw */
1956         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1957                 disable_graph(gio);
1958                 io_stat_redraw(gio->io);
1959                 return;
1960         }
1961
1962         /* first check if the field string is valid */
1963         if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1964                 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1965
1966                 /* warn and bail out if there was no field specified */
1967                 if(field==NULL || field[0]==0){
1968                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1969                         disable_graph(gio);
1970                         io_stat_redraw(gio->io);
1971                         return;
1972                 }
1973                 /* warn and bail out if the field could not be found */
1974                 hfi=proto_registrar_get_byname(field);
1975                 if(hfi==NULL){
1976                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1977                         disable_graph(gio);
1978                         io_stat_redraw(gio->io);
1979                         return;
1980                 }
1981                 gio->hf_index=hfi->id;
1982                 /* check that the type is compatible */
1983                 switch(hfi->type){
1984                 case FT_UINT8:
1985                 case FT_UINT16:
1986                 case FT_UINT24:
1987                 case FT_UINT32:
1988                 case FT_INT8:
1989                 case FT_INT16:
1990                 case FT_INT24:
1991                 case FT_INT32:
1992                 case FT_FLOAT:
1993                 case FT_DOUBLE:
1994                         /* these values support all calculations except LOAD */
1995                         switch(gio->calc_type){
1996                         case CALC_TYPE_LOAD:
1997                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1998                                     "LOAD(*) is only supported for relative-time fields.");
1999                                 disable_graph(gio);
2000                                 io_stat_redraw(gio->io);
2001                                 return;
2002                         }
2003                         /* these types support all calculations */
2004                         break;
2005                 case FT_RELATIVE_TIME:
2006                         /* this type only supports COUNT, MAX, MIN, AVG */
2007                         switch(gio->calc_type){
2008                         case CALC_TYPE_SUM:
2009                         case CALC_TYPE_COUNT:
2010                         case CALC_TYPE_MAX:
2011                         case CALC_TYPE_MIN:
2012                         case CALC_TYPE_AVG:
2013                         case CALC_TYPE_LOAD:
2014                                 break;
2015                         default:
2016                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2017                                     "%s is a relative-time field, so %s calculations are not supported on it.",
2018                                     field,
2019                                     calc_type_names[gio->calc_type]);
2020                                 disable_graph(gio);
2021                                 io_stat_redraw(gio->io);
2022                                 return;
2023                         }
2024                         break;
2025                 case FT_UINT64:
2026                 case FT_INT64:
2027                         /*
2028                          * XXX - support this if gint64/guint64 are
2029                          * available?
2030                          */
2031                         if(gio->calc_type!=CALC_TYPE_COUNT){
2032                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2033                                     "%s is a 64-bit integer, so %s calculations are not supported on it.",
2034                                     field,
2035                                     calc_type_names[gio->calc_type]);
2036                                 disable_graph(gio);
2037                                 io_stat_redraw(gio->io);
2038                                 return;
2039                         }
2040                         break;
2041                 default:
2042                         if(gio->calc_type!=CALC_TYPE_COUNT){
2043                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2044                                     "%s doesn't have integral or float values, so %s calculations are not supported on it.",
2045                                     field,
2046                                     calc_type_names[gio->calc_type]);
2047                                 disable_graph(gio);
2048                                 io_stat_redraw(gio->io);
2049                                 return;
2050                         }
2051                         break;
2052                 }
2053         }
2054
2055         /* first check if the filter string is valid. */
2056         filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
2057         if(!dfilter_compile(filter, &dfilter)) {
2058                 bad_dfilter_alert_box(filter);
2059                 disable_graph(gio);
2060                 io_stat_redraw(gio->io);
2061                 return;
2062         }
2063         if (dfilter != NULL)
2064                 dfilter_free(dfilter);
2065
2066         /* ok, we have a valid filter and the graph is active.
2067            first just try to delete any previous settings and then apply
2068            the new ones.
2069         */
2070         protect_thread_critical_region();
2071         remove_tap_listener(gio);
2072         unprotect_thread_critical_region();
2073
2074         io_stat_reset(gio->io);
2075         enable_graph(gio, filter, field);
2076         cf_retap_packets(&cfile);
2077         gdk_window_raise(gtk_widget_get_window(gio->io->window));
2078         io_stat_redraw(gio->io);
2079
2080         return;
2081 }
2082
2083 static void
2084 calc_type_select(GtkWidget *item, gpointer user_data)
2085 {
2086         io_stat_graph_t *gio = user_data;
2087
2088         gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
2089
2090         /* disable the graph */
2091         disable_graph(gio);
2092         io_stat_redraw(gio->io);
2093 }
2094
2095 static GtkWidget *
2096 create_calc_types_menu_items(io_stat_graph_t *gio)
2097 {
2098         GtkWidget *combo_box;
2099         int i;
2100
2101         combo_box = gtk_combo_box_text_new ();
2102         for(i=0;i<MAX_CALC_TYPES;i++){
2103                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
2104         }
2105         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
2106         g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
2107         return combo_box;
2108 }
2109
2110 static void
2111 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name,  GtkWidget *(*func)(io_stat_graph_t *io))
2112 {
2113         GtkWidget *hbox;
2114         GtkWidget *label;
2115         GtkWidget *combo_box;
2116
2117         hbox=gtk_hbox_new(FALSE, 0);
2118         gtk_container_add(GTK_CONTAINER(box), hbox);
2119         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2120         gtk_widget_show(hbox);
2121
2122         label=gtk_label_new(name);
2123         gtk_widget_show(label);
2124         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2125
2126         combo_box = (*func)(gio);
2127         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2128         gtk_widget_show(combo_box);
2129 }
2130
2131 static void
2132 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2133 {
2134
2135         gio->calc_field=gtk_entry_new();
2136         gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
2137         gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
2138         gtk_widget_show(gio->calc_field);
2139         g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
2140         g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
2141         g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2142         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2143         g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2144         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2145         colorize_filter_te_as_empty(gio->calc_field);
2146 }
2147
2148 static void
2149 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2150 {
2151         GtkWidget *hbox;
2152
2153         hbox=gtk_hbox_new(FALSE, 0);
2154         gio->advanced_buttons=hbox;
2155         gtk_container_add(GTK_CONTAINER(box), hbox);
2156         gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
2157         gtk_widget_hide(hbox);
2158
2159         gio->calc_type=CALC_TYPE_SUM;
2160         create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
2161         create_advanced_field(gio, hbox);
2162 }
2163
2164 static void
2165 filter_button_clicked(GtkWidget *w, gpointer user_data)
2166 {
2167         io_stat_graph_t *gio = user_data;
2168
2169         display_filter_construct_cb(w, gio->args);
2170         return;
2171 }
2172
2173 static void
2174 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2175 {
2176         GtkWidget *combo_box;
2177         GtkWidget *hbox;
2178         GtkWidget *label;
2179         char str[256];
2180         int i;
2181
2182         hbox=gtk_hbox_new(FALSE, 3);
2183         gtk_container_add(GTK_CONTAINER(box), hbox);
2184         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2185         gtk_widget_show(hbox);
2186
2187         g_snprintf(str, 256, "Graph %d", num);
2188         gio->display_button=gtk_toggle_button_new_with_label(str);
2189         gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2190         gtk_widget_show(gio->display_button);
2191         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2192         g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2193
2194         label=gtk_label_new("Color");
2195         gtk_widget_show(label);
2196         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2197
2198 #if GTK_CHECK_VERSION(3,0,0)
2199         gtk_widget_override_color(label, GTK_STATE_NORMAL, &gio->rgba_color);
2200         gtk_widget_override_color(label, GTK_STATE_ACTIVE, &gio->rgba_color);
2201         gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &gio->rgba_color);
2202         gtk_widget_override_color(label, GTK_STATE_SELECTED, &gio->rgba_color);
2203         gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &gio->rgba_color);
2204 #else   
2205         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2206         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2207         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2208         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2209         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2210 #endif
2211 /*      g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2212
2213
2214         /* filter prefs dialog */
2215         gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2216
2217         g_snprintf(str, 256, "Wireshark: Display Filter  IO-Stat (Filter:%d)", num);
2218         g_free( (gpointer) (gio->args->title) );
2219         gio->args->title=g_strdup(str);
2220
2221         g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2222         g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2223
2224         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2225         gtk_widget_show(gio->filter_bt);
2226
2227         gio->filter_field=gtk_entry_new();
2228         gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2229         /* filter prefs dialog */
2230         g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2231         /* filter prefs dialog */
2232
2233         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2234         gtk_widget_show(gio->filter_field);
2235         g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2236         g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2237         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2238         g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2239         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2240         colorize_filter_te_as_empty(gio->filter_field);
2241
2242         create_advanced_box(gio, hbox);
2243
2244         /*
2245          * create PlotStyle menu
2246          */
2247         g_snprintf(str, 256, " Style:");
2248         label=gtk_label_new(str);
2249         gtk_widget_show(label);
2250         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2251
2252         combo_box = gtk_combo_box_text_new ();
2253         for(i=0;i<MAX_PLOT_STYLES;i++){
2254                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
2255         }
2256         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2257         g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2258
2259         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2260         gtk_widget_show(combo_box);
2261
2262         return;
2263 }
2264
2265 static void
2266 create_filter_area(io_stat_t *io, GtkWidget *box)
2267 {
2268         GtkWidget *frame;
2269         GtkWidget *vbox;
2270         int i;
2271
2272         frame=gtk_frame_new("Graphs");
2273         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2274         gtk_widget_show(frame);
2275
2276         vbox=gtk_vbox_new(FALSE, 1);
2277         gtk_container_add(GTK_CONTAINER(frame), vbox);
2278         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2279         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2280         gtk_widget_show(vbox);
2281
2282         for(i=0;i<MAX_GRAPHS;i++){
2283                 create_filter_box(&io->graphs[i], vbox, i+1);
2284         }
2285
2286         return;
2287 }
2288
2289 static void
2290 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2291 {
2292         guint32         i, interval, val;
2293         char            string[15];
2294         GtkClipboard    *cb;
2295         GString         *CSV_str=g_string_new("");
2296         io_stat_t       *io = user_data;
2297
2298         g_string_append(CSV_str, "\"Interval start\"");
2299         for(i=0;i<MAX_GRAPHS;i++) {
2300                 if (io->graphs[i].display) {
2301                         g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2302                 }
2303         }
2304         g_string_append(CSV_str,"\n");
2305
2306         for(interval=0; interval<io->max_interval; interval+=io->interval) {
2307                 print_interval_string (string, 15, interval, io, FALSE);
2308                 g_string_append_printf(CSV_str, "\"%s\"", string);
2309                 for(i=0;i<MAX_GRAPHS;i++) {
2310                         if (io->graphs[i].display) {
2311                                 val=get_it_value(io, i, interval/io->interval);
2312                                 g_string_append_printf(CSV_str, ",\"%d\"", val);
2313                         }
2314                 }
2315                 g_string_append(CSV_str,"\n");
2316         }
2317
2318         /* Now that we have the CSV data, copy it into the default clipboard */
2319         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);    /* Get the default clipboard */
2320         gtk_clipboard_set_text(cb, CSV_str->str, -1);       /* Copy the CSV data into the clipboard */
2321         g_string_free(CSV_str, TRUE);                       /* Free the memory */
2322 }
2323
2324 static void
2325 init_io_stat_window(io_stat_t *io)
2326 {
2327         GtkWidget *vbox;
2328         GtkWidget *hbox;
2329         GtkWidget *bbox;
2330         GtkWidget *close_bt, *help_bt;
2331         GtkWidget *copy_bt;
2332         GtkWidget *save_bt;
2333
2334         /* create the main window, transient_for top_level */
2335         io->window = dlg_window_new("I/O Graphs");
2336         gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2337
2338         vbox=gtk_vbox_new(FALSE, 0);
2339         gtk_container_add(GTK_CONTAINER(io->window), vbox);
2340         gtk_widget_show(vbox);
2341
2342         create_draw_area(io, vbox);
2343
2344         hbox=gtk_hbox_new(FALSE, 3);
2345         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2346         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2347         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2348         gtk_widget_show(hbox);
2349
2350         create_filter_area(io, hbox);
2351         create_ctrl_area(io, hbox);
2352
2353         io_stat_set_title(io);
2354
2355         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2356                                   GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2357         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2358         gtk_widget_show(bbox);
2359
2360         close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2361         window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2362         gtk_widget_set_tooltip_text(close_bt,  "Close this dialog");
2363         save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2364         gtk_widget_set_sensitive(save_bt, FALSE);
2365         gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
2366         g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2367         g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2368
2369         copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2370         gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
2371         g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2372
2373         help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2374         g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2375         gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
2376         g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2377
2378         gtk_widget_show(io->window);
2379         window_present(io->window);
2380 }
2381
2382 #ifdef MAIN_MENU_USE_UIMANAGER
2383 void
2384 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2385 {
2386         iostat_init(NULL,NULL);
2387 }
2388 #else
2389 static void
2390 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2391 {
2392         iostat_init(NULL,NULL);
2393 }
2394 #endif
2395
2396 void
2397 register_tap_listener_gtk_iostat(void)
2398 {
2399         register_stat_cmd_arg("io,stat", iostat_init,NULL);
2400
2401 #ifdef MAIN_MENU_USE_UIMANAGER
2402 #else
2403         register_stat_menu_item_stock("_IO Graphs",
2404                                       REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2405                                       gui_iostat_cb, NULL, NULL, NULL);
2406 #endif
2407 }