merge_all_tap_menus() has been moved to menus.c.
[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 MAX_PIXELS_PER_TICK 4
73 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
74 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
75
76
77 #define DEFAULT_PLOT_STYLE      0
78 #define PLOT_STYLE_LINE         0
79 #define PLOT_STYLE_IMPULSE      1
80 #define PLOT_STYLE_FILLED_BAR   2
81 #define PLOT_STYLE_DOT          3
82 #define MAX_PLOT_STYLES         4
83 static const char *plot_style_name[MAX_PLOT_STYLES] = {
84         "Line",
85         "Impulse",
86         "FBar",
87         "Dot",
88 };
89
90 #define DEFAULT_COUNT_TYPE  0
91 #define COUNT_TYPE_FRAMES   0
92 #define COUNT_TYPE_BYTES    1
93 #define COUNT_TYPE_BITS     2
94 #define COUNT_TYPE_ADVANCED 3
95 #define MAX_COUNT_TYPES     4
96 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
97
98 /* unit is in ms */
99 #define MAX_TICK_VALUES 7
100 #define DEFAULT_TICK_VALUE_INDEX 3
101 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
102
103 #define CALC_TYPE_SUM   0
104 #define CALC_TYPE_COUNT 1
105 #define CALC_TYPE_MAX   2
106 #define CALC_TYPE_MIN   3
107 #define CALC_TYPE_AVG   4
108 #define CALC_TYPE_LOAD  5
109 #define MAX_CALC_TYPES  6
110 #define DEFAULT_CALC_TYPE 0
111 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
112
113
114 typedef struct _io_stat_calc_type_t {
115         struct _io_stat_graph_t *gio;
116         int calc_type;
117 } io_stat_calc_type_t;
118
119 #define NUM_IO_ITEMS 100000
120 typedef struct _io_item_t {
121         guint32 frames; /* always calculated, will hold number of frames*/
122         guint32 bytes;  /* always calculated, will hold number of bytes*/
123         guint32 fields;
124         gint32 int_max;
125         gint32 int_min;
126         gint32 int_tot;
127         gfloat float_max;
128         gfloat float_min;
129         gfloat float_tot;
130         gdouble double_max;
131         gdouble double_min;
132         gdouble double_tot;
133         nstime_t time_max;
134         nstime_t time_min;
135         nstime_t time_tot;
136 } io_item_t;
137
138 typedef struct _io_stat_graph_t {
139         struct _io_stat_t *io;
140         io_item_t items[NUM_IO_ITEMS];
141         int plot_style;
142         gboolean display;
143         GtkWidget *display_button;
144         GtkWidget *filter_field;
145         GtkWidget *advanced_buttons;
146         int calc_type;
147         int hf_index;
148         GtkWidget *calc_field;
149         GdkColor color;
150         construct_args_t *args;
151         GtkWidget *filter_bt;
152 } io_stat_graph_t;
153
154
155 typedef struct _io_stat_t {
156         gboolean needs_redraw;
157         gint32 interval;      /* measurement interval in ms */
158         guint32 last_interval;
159         guint32 max_interval; /* XXX max_interval and num_items are redundant */
160         guint32 num_items;
161         guint32 left_x_border;
162         guint32 right_x_border;
163         gboolean view_as_time;
164         nstime_t start_time;
165
166         struct _io_stat_graph_t graphs[MAX_GRAPHS];
167         GtkWidget *window;
168         GtkWidget *draw_area;
169 #if GTK_CHECK_VERSION(2,22,0)
170         cairo_surface_t *surface;
171 #else
172         GdkPixmap *pixmap;
173 #endif
174         GtkAdjustment *scrollbar_adjustment;
175         GtkWidget *scrollbar;
176         guint first_frame_num[NUM_IO_ITEMS];
177         guint last_frame_num;
178         int pixmap_width;
179         int pixmap_height;
180         int pixels_per_tick;
181         int max_y_units;
182         int count_type;
183 } io_stat_t;
184
185
186 static void init_io_stat_window(io_stat_t *io);
187 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
188
189 static void
190 io_stat_set_title(io_stat_t *io)
191 {
192         char            *title;
193
194         if(!io->window){
195                 return;
196         }
197         title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
198         gtk_window_set_title(GTK_WINDOW(io->window), title);
199         g_free(title);
200 }
201
202 static void
203 io_stat_reset(io_stat_t *io)
204 {
205         int i, j;
206
207         io->needs_redraw=TRUE;
208         for(i=0;i<MAX_GRAPHS;i++){
209                 for(j=0;j<NUM_IO_ITEMS;j++){
210                         io_item_t *ioi;
211                         ioi=&io->graphs[i].items[j];
212
213                         ioi->frames=0;
214                         ioi->bytes=0;
215                         ioi->fields=0;
216                         ioi->int_max=0;
217                         ioi->int_min=0;
218                         ioi->int_tot=0;
219                         ioi->float_max=0;
220                         ioi->float_min=0;
221                         ioi->float_tot=0;
222                         ioi->double_max=0;
223                         ioi->double_min=0;
224                         ioi->double_tot=0;
225                         nstime_set_zero(&ioi->time_max);
226                         nstime_set_zero(&ioi->time_min);
227                         nstime_set_zero(&ioi->time_tot);
228                 }
229         }
230         io->last_interval=0xffffffff;
231         io->max_interval=0;
232         io->num_items=0;
233         io->start_time.secs=0;
234         io->start_time.nsecs=0;
235         for(j=0;j<NUM_IO_ITEMS;j++) {
236                 io->first_frame_num[j]=0;
237         }
238         io->last_frame_num=0;
239
240         io_stat_set_title(io);
241 }
242
243 static void
244 tap_iostat_reset(void *g)
245 {
246         io_stat_graph_t *gio=g;
247
248         io_stat_reset(gio->io);
249 }
250
251 static gboolean
252 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
253 {
254         io_stat_graph_t *git=g;
255         io_item_t *it;
256         nstime_t time_delta;
257         int idx;
258
259         /* we sometimes get called when git is disabled.
260            this is a bug since the tap listener should be removed first */
261         if(!git->display){
262                 return FALSE;
263         }
264
265         git->io->needs_redraw=TRUE;
266
267         /*
268          * Find which interval this is supposed to go in and store the
269          * interval index as idx
270          */
271         time_delta=pinfo->fd->rel_ts;
272         if(time_delta.nsecs<0){
273                 time_delta.secs--;
274                 time_delta.nsecs+=1000000000;
275         }
276         if(time_delta.secs<0){
277                 return FALSE;
278         }
279         idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
280
281         /* some sanity checks */
282         if((idx<0)||(idx>=NUM_IO_ITEMS)){
283                 git->io->num_items = NUM_IO_ITEMS-1;
284                 return FALSE;
285         }
286
287         /* update num_items */
288         if((guint32)idx > git->io->num_items){
289                 git->io->num_items=idx;
290                 git->io->max_interval=(idx+1)*git->io->interval;
291         }
292
293         /* set start time */
294         if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
295                 nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
296         }
297
298         /* set first and last frame num in current interval */
299         if (git->io->first_frame_num[idx] == 0) {
300                 git->io->first_frame_num[idx]=pinfo->fd->num;
301         }
302         git->io->last_frame_num=pinfo->fd->num;
303
304         /*
305          * Find the appropriate io_item_t structure
306          */
307         it=&git->items[idx];
308
309
310         /*
311          * For ADVANCED mode we need to keep track of some more stuff
312          * than just frame and byte counts
313          */
314         if(git->io->count_type==COUNT_TYPE_ADVANCED){
315                 GPtrArray *gp;
316                 guint i;
317
318                 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
319                 if(!gp){
320                         return FALSE;
321                 }
322
323                 /* update the appropriate counters, make sure that if
324                  * fields==0 then this is the first seen value so
325                  * set any min/max values accordingly
326                  */
327                 for(i=0;i<gp->len;i++){
328                         int new_int;
329                         float new_float;
330                         double new_double;
331                         nstime_t *new_time;
332
333                         switch(proto_registrar_get_ftype(git->hf_index)){
334                         case FT_UINT8:
335                         case FT_UINT16:
336                         case FT_UINT24:
337                         case FT_UINT32:
338                                 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
339
340                                 if((new_int>it->int_max)||(it->fields==0)){
341                                         it->int_max=new_int;
342                                 }
343                                 if((new_int<it->int_min)||(it->fields==0)){
344                                         it->int_min=new_int;
345                                 }
346                                 it->int_tot+=new_int;
347                                 it->fields++;
348                                 break;
349                         case FT_INT8:
350                         case FT_INT16:
351                         case FT_INT24:
352                         case FT_INT32:
353                                 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
354                                 if((new_int>it->int_max)||(it->fields==0)){
355                                         it->int_max=new_int;
356                                 }
357                                 if((new_int<it->int_min)||(it->fields==0)){
358                                         it->int_min=new_int;
359                                 }
360                                 it->int_tot+=new_int;
361                                 it->fields++;
362                                 break;
363                         case FT_FLOAT:
364                                 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
365                                 if((new_float>it->float_max)||(it->fields==0)){
366                                         it->float_max=new_float;
367                                 }
368                                 if((new_float<it->float_min)||(it->fields==0)){
369                                         it->float_min=new_float;
370                                 }
371                                 it->float_tot+=new_float;
372                                 it->fields++;
373                                 break;
374                         case FT_DOUBLE:
375                                 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
376                                 if((new_double>it->double_max)||(it->fields==0)){
377                                         it->double_max=new_double;
378                                 }
379                                 if((new_double<it->double_min)||(it->fields==0)){
380                                         it->double_min=new_double;
381                                 }
382                                 it->double_tot+=new_double;
383                                 it->fields++;
384                                 break;
385                         case FT_RELATIVE_TIME:
386                                 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
387
388                                 switch(git->calc_type){
389                                         guint64 t, pt; /* time in us */
390                                         int j;
391                                 case CALC_TYPE_LOAD:
392                                         /* it is a LOAD calculation of a relative time field.
393                                          * add the time this call spanned to each
394                                          * interval it spanned according to its contribution
395                                          * to that interval.
396                                          */
397                                         t=new_time->secs;
398                                         t=t*1000000+new_time->nsecs/1000;
399                                         j=idx;
400                                         /* handle current interval */
401                                         pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
402                                         pt=pt%(git->io->interval*1000);
403                                         if(pt>t){
404                                                 pt=t;
405                                         }
406                                         while(t){
407                                                 git->items[j].time_tot.nsecs+=(int) (pt*1000);
408                                                 if(git->items[j].time_tot.nsecs>1000000000){
409                                                         git->items[j].time_tot.secs++;
410                                                         git->items[j].time_tot.nsecs-=1000000000;
411                                                 }
412
413                                                 if(j==0){
414                                                         break;
415                                                 }
416                                                 j--;
417                                                 t-=pt;
418                                                 if(t > (guint32) (git->io->interval*1000)){
419                                                         pt=git->io->interval*1000;
420                                                 } else {
421                                                         pt=t;
422                                                 }
423                                         }
424                                         break;
425                                 default:
426                                         if( (new_time->secs>it->time_max.secs)
427                                         ||( (new_time->secs==it->time_max.secs)
428                                           &&(new_time->nsecs>it->time_max.nsecs))
429                                         ||(it->fields==0)){
430                                                 it->time_max=*new_time;
431                                         }
432                                         if( (new_time->secs<it->time_min.secs)
433                                         ||( (new_time->secs==it->time_min.secs)
434                                           &&(new_time->nsecs<it->time_min.nsecs))
435                                         ||(it->fields==0)){
436                                                 it->time_min=*new_time;
437                                         }
438                                         nstime_add(&it->time_tot, new_time);
439                                         it->fields++;
440                                 }
441                         }
442                 }
443         }
444
445         it->frames++;
446         it->bytes+=pinfo->fd->pkt_len;
447
448         return TRUE;
449 }
450
451 static guint
452 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
453 {
454         guint i, frame_num=0;
455
456         if (idx>io->num_items) {
457                 return 0;
458         }
459
460         if (first) {
461                 frame_num=io->first_frame_num[idx];
462         }
463
464         if (frame_num==0) {
465                 /*
466                  * If first frame not found we select the last
467                  * frame in the previous interval
468                  *
469                  * If selecting the last frame we select the frame
470                  * before the first frame in the next interval
471                  */
472                 for(i=idx+1;i<=io->num_items;i++) {
473                         frame_num=io->first_frame_num[i];
474                         if (frame_num != 0) {
475                                 return frame_num-1;
476                         }
477                 }
478
479                 /*
480                  * If not found we select the last frame
481                  */
482                 frame_num=io->last_frame_num;
483         }
484
485         return frame_num;
486 }
487
488 static guint32
489 get_it_value(io_stat_t *io, int graph_id, int idx)
490 {
491         double value=0;
492         int adv_type;
493         io_item_t *it;
494
495         it=&io->graphs[graph_id].items[idx];
496
497         switch(io->count_type){
498         case COUNT_TYPE_FRAMES:
499                 return it->frames;
500         case COUNT_TYPE_BYTES:
501                 return it->bytes;
502         case COUNT_TYPE_BITS:
503                 return (it->bytes * 8);
504         }
505
506
507         adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
508         switch(adv_type){
509         case FT_NONE:
510                 switch(io->graphs[graph_id].calc_type){
511                 case CALC_TYPE_COUNT:
512                         value=it->frames;
513                         break;
514                 default:
515                         break;
516                 }
517                 break;
518         case FT_UINT8:
519         case FT_UINT16:
520         case FT_UINT24:
521         case FT_UINT32:
522         case FT_INT8:
523         case FT_INT16:
524         case FT_INT24:
525         case FT_INT32:
526                 switch(io->graphs[graph_id].calc_type){
527                 case CALC_TYPE_SUM:
528                         value=it->int_tot;
529                         break;
530                 case CALC_TYPE_COUNT:
531                         value=it->frames;
532                         break;
533                 case CALC_TYPE_MAX:
534                         value=it->int_max;
535                         break;
536                 case CALC_TYPE_MIN:
537                         value=it->int_min;
538                         break;
539                 case CALC_TYPE_AVG:
540                         if(it->fields){
541                                 value=it->int_tot/it->fields;
542                         } else {
543                                 value=0;
544                         }
545                         break;
546                 default:
547                         break;
548                 }
549                 break;
550         case FT_FLOAT:
551                 switch(io->graphs[graph_id].calc_type){
552                 case CALC_TYPE_SUM:
553                         value=it->float_tot;
554                         break;
555                 case CALC_TYPE_COUNT:
556                         value=it->frames;
557                         break;
558                 case CALC_TYPE_MAX:
559                         value=it->float_max;
560                         break;
561                 case CALC_TYPE_MIN:
562                         value=it->float_min;
563                         break;
564                 case CALC_TYPE_AVG:
565                         if(it->fields){
566                                 value=it->float_tot/it->fields;
567                         } else {
568                                 value=0;
569                         }
570                         break;
571                 default:
572                         break;
573                 }
574                 break;
575         case FT_DOUBLE:
576                 switch(io->graphs[graph_id].calc_type){
577                 case CALC_TYPE_SUM:
578                         value=it->double_tot;
579                         break;
580                 case CALC_TYPE_COUNT:
581                         value=it->frames;
582                         break;
583                 case CALC_TYPE_MAX:
584                         value=it->double_max;
585                         break;
586                 case CALC_TYPE_MIN:
587                         value=it->double_min;
588                         break;
589                 case CALC_TYPE_AVG:
590                         if(it->fields){
591                                 value=it->double_tot/it->fields;
592                         } else {
593                                 value=0;
594                         }
595                         break;
596                 default:
597                         break;
598                 }
599                 break;
600         case FT_RELATIVE_TIME:
601                 switch(io->graphs[graph_id].calc_type){
602                 case CALC_TYPE_COUNT:
603                         value=it->frames;
604                         break;
605                 case CALC_TYPE_MAX:
606                         value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
607                         break;
608                 case CALC_TYPE_MIN:
609                         value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
610                         break;
611                 case CALC_TYPE_SUM:
612                         value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
613                         break;
614                 case CALC_TYPE_AVG:
615                         if(it->fields){
616                                 guint64 t; /* time in us */
617
618                                 t=it->time_tot.secs;
619                                 t=t*1000000+it->time_tot.nsecs/1000;
620                                 value=(guint32) (t/it->fields);
621                         } else {
622                                 value=0;
623                         }
624                         break;
625                 case CALC_TYPE_LOAD:
626                         value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
627                         break;
628                 default:
629                         break;
630                 }
631                 break;
632         default:
633                 break;
634         }
635         return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
636 }
637
638 static void
639 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
640 {
641         if(t_max>=10000000 || (log_flag && t_max>=1000000)){
642                 g_snprintf(buf, buf_len, "%ds",t/1000000);
643         } else if(t_max>=1000000){
644                 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
645         } else if(t_max>=10000 || (log_flag && t_max>=1000)){
646                 g_snprintf(buf, buf_len, "%dms",t/1000);
647         } else if(t_max>=1000){
648                 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
649         } else {
650                 g_snprintf(buf, buf_len, "%dus",t);
651         }
652 }
653
654 static void
655 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
656                       gboolean ext)
657 {
658         if (io->view_as_time) {
659                 struct tm *tmp;
660                 time_t sec_val = interval/1000 + io->start_time.secs;
661                 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
662
663                 if(nsec_val >= 1000) {
664                         sec_val++;
665                         nsec_val -= 1000;
666                 }
667                 tmp = localtime (&sec_val);
668                 if(io->interval>=1000){
669                         g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
670                 } else if(io->interval>=100){
671                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
672                 } else if(io->interval>=10){
673                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
674                 } else {
675                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
676                 }
677         } else {
678                 if (!ext) {
679                         g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
680                 } else if(io->interval>=60000){
681                         g_snprintf(buf, buf_len, "%dm", interval/60000);
682                 } else if(io->interval>=1000){
683                         g_snprintf(buf, buf_len, "%ds", interval/1000);
684                 } else if(io->interval>=100){
685                         g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
686                 } else if(io->interval>=10){
687                         g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
688                 } else {
689                         g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
690                 }
691         }
692 }
693
694 static void
695 io_stat_draw(io_stat_t *io)
696 {
697         int i, tics, ystart, ys;
698         guint32 last_interval, first_interval, interval_delta;
699         gint32 current_interval;
700         guint32 top_y_border;
701         guint32 bottom_y_border;
702         PangoLayout  *layout;
703         int label_width, label_height;
704         guint32 draw_width, draw_height;
705         char label_string[45];
706         GtkAllocation widget_alloc;
707
708         /* new variables */
709         guint32 num_time_intervals;
710         guint32 max_value;              /* max value of seen data */
711         guint32 max_y;                  /* max value of the Y scale */
712         gboolean draw_y_as_time;
713         cairo_t *cr;
714         if(!io->needs_redraw){
715                 return;
716         }
717         io->needs_redraw=FALSE;
718
719         /*
720          * Find the length of the intervals we have data for
721          * so we know how large arrays we need to malloc()
722          */
723         num_time_intervals=io->num_items+1;
724
725         /* XXX move this check to _packet() */
726         if(num_time_intervals>NUM_IO_ITEMS){
727                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
728                 return;
729         }
730
731
732         /*
733          * find the max value so we can autoscale the y axis
734          */
735         max_value=0;
736         for(i=0;i<MAX_GRAPHS;i++){
737                 int idx;
738
739                 if(!io->graphs[i].display){
740                         continue;
741                 }
742                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
743                         guint32 val;
744
745                         val=get_it_value(io, i, idx);
746
747                         /* keep track of the max value we have encountered */
748                         if(val>max_value){
749                                 max_value=val;
750                         }
751                 }
752         }
753
754
755
756         /*
757          * Clear out old plot
758          */
759 #if GTK_CHECK_VERSION(2,22,0)
760         cr = cairo_create (io->surface);
761 #else
762         cr = gdk_cairo_create (io->pixmap);
763 #endif
764         cairo_set_source_rgb (cr, 1, 1, 1);
765         gtk_widget_get_allocation(io->draw_area, &widget_alloc);
766         cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
767         cairo_fill (cr);
768         cairo_destroy (cr);
769         /*
770          * Calculate the y scale we should use
771          */
772         if(io->max_y_units==AUTO_MAX_YSCALE){
773                 max_y=yscale_max[MAX_YSCALE-1];
774                 for(i=MAX_YSCALE-1;i>1;i--){
775                         if(max_value<yscale_max[i]){
776                                 max_y=yscale_max[i];
777                         }
778                 }
779         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
780                 max_y=1000000000;
781                 for(i=1000000000;i>1;i/=10){
782                         if(max_value<(guint32)i){
783                                 max_y=i;
784                         }
785                 }
786         } else {
787                 /* the user had specified an explicit y scale to use */
788                 max_y=io->max_y_units;
789         }
790
791
792         /*
793          * If we use ADVANCED and all the graphs are plotting
794          * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
795          * then we will do some some special processing for the
796          * labels for the Y axis below:
797          *   we will append the time unit " s" " ms" or " us"
798          *   and we will present the unit in decimal
799          */
800         draw_y_as_time=FALSE;
801         if(io->count_type==COUNT_TYPE_ADVANCED){
802                 draw_y_as_time=TRUE;
803                 for(i=0;i<MAX_GRAPHS;i++){
804                         int adv_type;
805
806                         if(!io->graphs[i].display){
807                                 continue;
808                         }
809                         adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
810                         switch(adv_type){
811                         case FT_RELATIVE_TIME:
812                                 switch(io->graphs[i].calc_type){
813                                 case CALC_TYPE_SUM:
814                                 case CALC_TYPE_MAX:
815                                 case CALC_TYPE_MIN:
816                                 case CALC_TYPE_AVG:
817                                         break;
818                                 default:
819                                         draw_y_as_time=FALSE;
820                                 }
821                                 break;
822                         default:
823                                 draw_y_as_time=FALSE;
824                         }
825                 }
826         }
827
828
829
830         /*
831          * Calculate size of borders surrounding the plot
832          * The border on the right side needs to be adjusted depending
833          * on the width of the text labels. For simplicity we assume that the
834          * top y scale label will be the widest one
835          */
836         if(draw_y_as_time){
837                 if(io->max_y_units==LOGARITHMIC_YSCALE){
838                         print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
839                 } else {
840                         print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
841                 }
842         } else {
843                 g_snprintf(label_string, 15, "%d", max_y);
844         }
845         layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
846         pango_layout_get_pixel_size(layout, &label_width, &label_height);
847
848         io->left_x_border=10;
849         io->right_x_border=label_width+20;
850         top_y_border=10;
851         bottom_y_border=label_height+20;
852
853
854         /*
855          * Calculate the size of the drawing area for the actual plot
856          */
857         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
858         draw_height=io->pixmap_height-top_y_border-bottom_y_border;
859
860
861         /*
862          * Add a warning if too many entries
863          */
864         if (num_time_intervals == NUM_IO_ITEMS) {
865                 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
866                 pango_layout_set_text(layout, label_string, -1);
867
868 #if GTK_CHECK_VERSION(2,22,0)
869                 cr = cairo_create (io->surface);
870 #else
871                 cr = gdk_cairo_create (io->pixmap);
872 #endif
873                 cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
874                 pango_cairo_show_layout (cr, layout);
875                 cairo_destroy (cr);
876                 cr = NULL;
877         }
878
879         /*
880          * Draw the y axis and labels
881          * (we always draw the y scale with 11 ticks along the axis)
882          */
883 #if GTK_CHECK_VERSION(2,22,0)
884         cr = cairo_create (io->surface);
885 #else
886         cr = gdk_cairo_create (io->pixmap);
887 #endif
888         cairo_set_line_width (cr, 1.0);
889         cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5);
890         cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5);
891         cairo_stroke(cr);
892         cairo_destroy(cr);
893         if(io->max_y_units==LOGARITHMIC_YSCALE){
894                 tics=(int)log10((double)max_y);
895                 ystart=draw_height/10;
896                 ys=-1;
897         } else {
898                 tics=10;
899                 ystart=ys=0;
900         }
901
902         for(i=ys;i<=tics;i++){
903                 int xwidth, lwidth, ypos;
904
905                 xwidth=5;
906                 if(io->max_y_units==LOGARITHMIC_YSCALE){
907                         if(i==ys) {
908                                 /* position for the 0 value */
909                                 ypos=io->pixmap_height-bottom_y_border;
910                         } else if(i==tics) {
911                                 /* position for the top value, do not draw logarithmic tics above graph */
912                                 ypos=io->pixmap_height-bottom_y_border-draw_height;
913                         } else {
914                                 int j;
915                                 /* draw the logarithmic tics */
916                                 for(j=2;j<10;j++) {
917                                         ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
918                                         /* draw the tick */
919 #if GTK_CHECK_VERSION(2,22,0)
920                                         cr = cairo_create (io->surface);
921 #else
922                                         cr = gdk_cairo_create (io->pixmap);
923 #endif
924                                         cairo_set_line_width (cr, 1.0);
925                                         cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
926                                         cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
927                                         cairo_stroke(cr);
928                                         cairo_destroy(cr);
929                                 }
930                                 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
931                         }
932                         /* all "main" logarithmic lines are slightly longer */
933                         xwidth=10;
934                 } else {
935                         if(!(i%5)){
936                                 /* first, middle and last tick are slightly longer */
937                                 xwidth=10;
938                         }
939                         ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
940                 }
941                 /* draw the tick */
942 #if GTK_CHECK_VERSION(2,22,0)
943                 cr = cairo_create (io->surface);
944 #else
945                 cr = gdk_cairo_create (io->pixmap);
946 #endif
947                 cairo_set_line_width (cr, 1.0);
948                 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
949                 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
950                 cairo_stroke(cr);
951                 cairo_destroy(cr);
952                 /* draw the labels */
953                 if(xwidth==10) {
954                         guint32 value;
955                         if(io->max_y_units==LOGARITHMIC_YSCALE){
956                                 value=(guint32)(max_y/pow(10,tics-i));
957                                 if(draw_y_as_time){
958                                         print_time_scale_string(label_string, 15, value, value, TRUE);
959                                 } else {
960                                         g_snprintf(label_string, 15, "%d", value);
961                                 }
962                         } else {
963                                 value=(max_y/10)*i;
964                                 if(draw_y_as_time){
965                                         print_time_scale_string(label_string, 15, value, max_y, FALSE);
966                                 } else {
967                                         g_snprintf(label_string, 15, "%d", value);
968                                 }
969                         }
970
971                 pango_layout_set_text(layout, label_string, -1);
972                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
973
974 #if GTK_CHECK_VERSION(2,22,0)
975                         cr = cairo_create (io->surface);
976 #else
977                         cr = gdk_cairo_create (io->pixmap);
978 #endif
979                         cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
980                         pango_cairo_show_layout (cr, layout);
981                         cairo_destroy (cr);
982                         cr = NULL;
983
984                 }
985         }
986
987         /*
988          * if we have not specified the last_interval via the gui,
989          * then just pick the current end of the capture so that is scrolls
990          * nicely when doing live captures
991          */
992         if(io->last_interval==0xffffffff){
993                 last_interval=io->max_interval;
994         } else {
995                 last_interval=io->last_interval;
996         }
997
998
999
1000
1001 /*XXX*/
1002         /* plot the x-scale */
1003 #if GTK_CHECK_VERSION(2,22,0)
1004                 cr = cairo_create (io->surface);
1005 #else
1006                 cr = gdk_cairo_create (io->pixmap);
1007 #endif
1008                 cairo_set_line_width (cr, 1.0);
1009                 cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5);
1010                 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5);
1011                 cairo_stroke(cr);
1012                 cairo_destroy(cr);
1013         if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
1014                 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
1015                 first_interval*=io->interval;
1016         } else {
1017                 first_interval=0;
1018         }
1019
1020         interval_delta=(100/io->pixels_per_tick)*io->interval;
1021         for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
1022                 int x, xlen;
1023
1024                 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
1025                 /* if pixels_per_tick is 5, only draw every 5 ticks */
1026                 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
1027                    ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
1028                         continue;
1029                 }
1030
1031                 if(!(current_interval%interval_delta)){
1032                         xlen=10;
1033                 } else if(!(current_interval%(interval_delta/2))){
1034                         xlen=8;
1035                 } else {
1036                         xlen=5;
1037                 }
1038                 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
1039 #if GTK_CHECK_VERSION(2,22,0)
1040                 cr = cairo_create (io->surface);
1041 #else
1042                 cr = gdk_cairo_create (io->pixmap);
1043 #endif
1044                 cairo_set_line_width (cr, 1.0);
1045                 cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5);
1046                 cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5);
1047                 cairo_stroke(cr);
1048                 cairo_destroy(cr);
1049                 if(xlen==10){
1050                         int lwidth, x_pos;
1051                         print_interval_string (label_string, 15, current_interval, io, TRUE);
1052                         pango_layout_set_text(layout, label_string, -1);
1053                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1054
1055                         if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1056                                 x_pos=5;
1057                         } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1058                                 x_pos=io->pixmap_width-lwidth-5;
1059                         } else {
1060                                 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1061                         }
1062 #if GTK_CHECK_VERSION(2,22,0)
1063                         cr = cairo_create (io->surface);
1064 #else
1065                         cr = gdk_cairo_create (io->pixmap);
1066 #endif
1067                         cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
1068                         pango_cairo_show_layout (cr, layout);
1069                         cairo_destroy (cr);
1070                         cr = NULL;
1071                 }
1072
1073         }
1074         g_object_unref(G_OBJECT(layout));
1075
1076
1077         /*
1078          * Loop over all graphs and draw them
1079          */
1080         for(i=MAX_GRAPHS-1;i>=0;i--){
1081                 guint64 val;
1082                 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1083
1084                 if(!io->graphs[i].display){
1085                         continue;
1086                 }
1087
1088                 /* initialize prev x/y to the value of the first interval */
1089                 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1090                 val=get_it_value(io, i, first_interval/io->interval);
1091                 if(val>max_y){
1092                         prev_y_pos=0;
1093                 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1094                         if (val==0) {
1095                                 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1096                         } else {
1097                                 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1098                         }
1099                 } else {
1100                         prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1101                 }
1102
1103                 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1104                         x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1105
1106                         val=get_it_value(io, i, interval/io->interval);
1107                         if(val>max_y){
1108                                 y_pos=0;
1109                         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1110                                 if (val==0) {
1111                                         y_pos=(guint32)(draw_height-1+top_y_border);
1112                                 } else {
1113                                         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                                 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1117                         }
1118
1119                         switch(io->graphs[i].plot_style){
1120                         case PLOT_STYLE_LINE:
1121                                 /* dont need to draw anything if the segment
1122                                  * is entirely above the top of the graph
1123                                  */
1124                                 if( (prev_y_pos!=0) || (y_pos!=0) ){
1125 #if GTK_CHECK_VERSION(2,22,0)
1126                                         cr = cairo_create (io->surface);
1127 #else
1128                                         cr = gdk_cairo_create (io->pixmap);
1129 #endif
1130                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1131                                         cairo_set_line_width (cr, 1.0);
1132                                         cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
1133                                         cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1134                                         cairo_stroke(cr);
1135                                         cairo_destroy(cr);
1136                                 }
1137                                 break;
1138                         case PLOT_STYLE_IMPULSE:
1139                                 if(val){
1140 #if GTK_CHECK_VERSION(2,22,0)
1141                                         cr = cairo_create (io->surface);
1142 #else
1143                                         cr = gdk_cairo_create (io->pixmap);
1144 #endif
1145                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1146                                         cairo_set_line_width (cr, 1.0);
1147                                         cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1148                                         cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1149                                         cairo_stroke(cr);
1150                                         cairo_destroy(cr);
1151                                 }
1152                                 break;
1153                         case PLOT_STYLE_FILLED_BAR:
1154                                 if(val){
1155 #if GTK_CHECK_VERSION(2,22,0)
1156                                         cr = cairo_create (io->surface);
1157 #else
1158                                         cr = gdk_cairo_create (io->pixmap);
1159 #endif
1160                                         cairo_rectangle (cr,
1161                                                 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1162                                                 y_pos+0.5,
1163                                                 io->pixels_per_tick,
1164                                                 draw_height-1+top_y_border-y_pos);
1165                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1166                                         cairo_fill (cr);
1167                                         cairo_destroy (cr);
1168                                 }
1169                                 break;
1170                         case PLOT_STYLE_DOT:
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                                         cairo_arc (cr,
1178                                                 x_pos+0.5,
1179                                                 y_pos+0.5,
1180                                                 (gdouble)io->pixels_per_tick/2,
1181                                                 0,
1182                                                 2 * G_PI);
1183                                         gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1184                                         cairo_fill (cr);
1185                                         cairo_destroy (cr);
1186                                 }
1187                                 break;
1188                         }
1189
1190                         prev_y_pos=y_pos;
1191                         prev_x_pos=x_pos;
1192                 }
1193         }
1194
1195         cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1196
1197 #if GTK_CHECK_VERSION(2,22,0)
1198         cairo_set_source_surface (cr, io->surface, 0, 0); 
1199 #else
1200         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1201 #endif
1202         cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1203         cairo_fill (cr);
1204
1205         cairo_destroy (cr);
1206
1207         /* update the scrollbar */
1208         if (io->max_interval == 0) {
1209                 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
1210                 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
1211                 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
1212         } else {
1213                 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
1214                 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1215                 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1216         }
1217         gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
1218         gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
1219         gtk_adjustment_changed(io->scrollbar_adjustment);
1220         gtk_adjustment_value_changed(io->scrollbar_adjustment);
1221
1222 }
1223
1224 static void
1225 io_stat_redraw(io_stat_t *io)
1226 {
1227         io->needs_redraw=TRUE;
1228         io_stat_draw(io);
1229 }
1230
1231 static void
1232 tap_iostat_draw(void *g)
1233 {
1234         io_stat_graph_t *git=g;
1235
1236         io_stat_draw(git->io);
1237 }
1238
1239 /* ok we get called with both the filter and the field.
1240    make sure the field is part of the filter.
1241    (make sure and make sure  just append it)
1242    the field MUST be part of the filter or else we wont
1243    be able to pick up the field values after the edt tree has been
1244    pruned
1245 */
1246 static GString *
1247 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1248 {
1249         char real_filter[262];
1250
1251         gio->display=TRUE;
1252
1253         real_filter[0]=0;
1254         if(filter){
1255                 /* skip all whitespaces */
1256                 while(*filter){
1257                         if(*filter==' '){
1258                                 filter++;
1259                                 continue;
1260                         }
1261                         if(*filter=='\t'){
1262                                 filter++;
1263                                 continue;
1264                         }
1265                         break;
1266                 }
1267                 if(*filter){
1268                         g_snprintf(real_filter, 257, "(%s)", filter);
1269                         real_filter[257]=0;
1270                 }
1271         }
1272         if(field){
1273                 /* skip all whitespaces */
1274                 while(*field){
1275                         if(*field==' '){
1276                                 field++;
1277                                 continue;
1278                         }
1279                         if(*field=='\t'){
1280                                 field++;
1281                                 continue;
1282                         }
1283                         break;
1284                 }
1285                 if(*field){
1286                         if(real_filter[0]!=0){
1287                                 g_strlcat(real_filter, " && ", 262);
1288                         }
1289                         g_strlcat(real_filter, field, 262);
1290                 }
1291         }
1292         return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1293                                      TL_REQUIRES_PROTO_TREE,
1294                                      tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1295 }
1296
1297 static void
1298 disable_graph(io_stat_graph_t *gio)
1299 {
1300         if (gio->display) {
1301                 gio->display=FALSE;
1302                 protect_thread_critical_region();
1303                 remove_tap_listener(gio);
1304                 unprotect_thread_critical_region();
1305                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1306                     FALSE);
1307         }
1308 }
1309
1310 static void
1311 iostat_init(const char *optarg _U_, void* userdata _U_)
1312 {
1313         io_stat_t *io;
1314         int i=0;
1315         static GdkColor col[MAX_GRAPHS] = {
1316                 {0,     0x0000, 0x0000, 0x0000},
1317                 {0,     0xffff, 0x0000, 0x0000},
1318                 {0,     0x0000, 0xffff, 0x0000},
1319                 {0,     0x0000, 0x0000, 0xffff},
1320                 {0,     0xffff, 0x5000, 0xffff}
1321         };
1322         GString *error_string;
1323
1324         io=g_malloc(sizeof(io_stat_t));
1325         io->needs_redraw=TRUE;
1326         io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1327         io->window=NULL;
1328         io->draw_area=NULL;
1329 #if GTK_CHECK_VERSION(2,22,0)
1330         io->surface=NULL;
1331 #else
1332         io->pixmap=NULL;
1333 #endif
1334         io->scrollbar=NULL;
1335         io->scrollbar_adjustment=NULL;
1336         io->pixmap_width=500;
1337         io->pixmap_height=200;
1338         io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1339         io->max_y_units=AUTO_MAX_YSCALE;
1340         io->count_type=0;
1341         io->last_interval=0xffffffff;
1342         io->max_interval=0;
1343         io->num_items=0;
1344         io->left_x_border=0;
1345         io->right_x_border=500;
1346         io->view_as_time=FALSE;
1347         io->start_time.secs=0;
1348         io->start_time.nsecs=0;
1349
1350         for(i=0;i<MAX_GRAPHS;i++){
1351                 io->graphs[i].color.pixel=col[i].pixel;
1352                 io->graphs[i].color.red=col[i].red;
1353                 io->graphs[i].color.green=col[i].green;
1354                 io->graphs[i].color.blue=col[i].blue;
1355                 io->graphs[i].display=0;
1356                 io->graphs[i].display_button=NULL;
1357                 io->graphs[i].filter_field=NULL;
1358                 io->graphs[i].advanced_buttons=NULL;
1359                 io->graphs[i].io=io;
1360
1361                 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1362                 io->graphs[i].args->title = NULL;
1363                 io->graphs[i].args->wants_apply_button=TRUE;
1364                 io->graphs[i].args->activate_on_ok=TRUE;
1365                 io->graphs[i].args->modal_and_transient=FALSE;
1366
1367                 io->graphs[i].filter_bt=NULL;
1368         }
1369         io_stat_reset(io);
1370
1371         error_string=enable_graph(&io->graphs[0], NULL, NULL);
1372         g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1373 #if 0
1374         if(error_string){
1375
1376                 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1377                     error_string->str);
1378                 g_string_free(error_string, TRUE);
1379                 io->graphs[0].display=0;
1380                 io->graphs[0].display_button=NULL;
1381                 io->graphs[0].filter_field=NULL;
1382                 io->graphs[0].advanced_buttons=NULL;
1383                 exit(10);
1384         }
1385 #endif
1386         /* build the GUI */
1387         init_io_stat_window(io);
1388
1389         cf_retap_packets(&cfile);
1390         gdk_window_raise(gtk_widget_get_window(io->window));
1391         io_stat_redraw(io);
1392 }
1393
1394 static void
1395 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1396 {
1397         io_stat_t *io = user_data;
1398         int i;
1399         GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1400         surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
1401
1402         g_free(surface_info);
1403
1404         for(i=0;i<MAX_GRAPHS;i++){
1405                 if(io->graphs[i].display){
1406                         protect_thread_critical_region();
1407                         remove_tap_listener(&io->graphs[i]);
1408                         unprotect_thread_critical_region();
1409
1410                         g_free( (gpointer) (io->graphs[i].args->title) );
1411                         io->graphs[i].args->title=NULL;
1412
1413                         g_free(io->graphs[i].args);
1414                         io->graphs[i].args=NULL;
1415                 }
1416         }
1417         g_free(io);
1418
1419         return;
1420 }
1421
1422 static gboolean
1423 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1424 {
1425         io_stat_t *io = user_data;
1426         guint32 draw_width, interval, last_interval;
1427         guint frame_num;
1428
1429         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1430
1431         if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1432             (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1433               /* Outside draw area */
1434               return FALSE;
1435         }
1436
1437 #if GTK_CHECK_VERSION(2,22,0)
1438         if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1439 #else
1440         if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1441 #endif
1442                 /*
1443                  * Button 1 selects the first package in the interval.
1444                  * Button 3 selects the last package in the interval.
1445                  */
1446                 if (io->last_interval==0xffffffff) {
1447                         last_interval=io->max_interval;
1448                 } else {
1449                         last_interval=io->last_interval;
1450                 }
1451
1452                 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1453                 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1454                 if (frame_num != 0) {
1455                         cf_goto_frame(&cfile, frame_num);
1456                 }
1457         }
1458
1459         return TRUE;
1460 }
1461
1462 /* create a new backing pixmap of the appropriate size */
1463 static gboolean
1464 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1465 {
1466         io_stat_t *io = user_data;
1467         GtkWidget *save_bt;
1468         GtkAllocation widget_alloc;
1469         cairo_t *cr;
1470 #if GTK_CHECK_VERSION(2,22,0)
1471         surface_info_t *surface_info = g_new(surface_info_t, 1);
1472 #endif
1473
1474 #if GTK_CHECK_VERSION(2,22,0)
1475         if(io->surface){
1476                  cairo_surface_destroy (io->surface);
1477                 io->surface=NULL;
1478         }
1479 #else
1480         if(io->pixmap){
1481                 g_object_unref(io->pixmap);
1482                 io->pixmap=NULL;
1483         }
1484 #endif
1485
1486         gtk_widget_get_allocation(widget, &widget_alloc);
1487 #if GTK_CHECK_VERSION(2,22,0)
1488         io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1489                         CAIRO_CONTENT_COLOR,
1490                         widget_alloc.width,
1491                         widget_alloc.height);
1492
1493 #else
1494         io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1495                         widget_alloc.width,
1496                         widget_alloc.height,
1497                         -1);
1498 #endif
1499         io->pixmap_width=widget_alloc.width;
1500         io->pixmap_height=widget_alloc.height;
1501
1502         save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1503 #if GTK_CHECK_VERSION(2,22,0)
1504         surface_info->surface = io->surface;
1505         surface_info->width = widget_alloc.width;
1506         surface_info->height = widget_alloc.height;
1507         g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
1508         gtk_widget_set_sensitive(save_bt, TRUE);
1509
1510         cr = cairo_create (io->surface);
1511 #else
1512         g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1513         gtk_widget_set_sensitive(save_bt, TRUE);
1514
1515         cr = gdk_cairo_create (io->pixmap);
1516 #endif
1517         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1518         cairo_set_source_rgb (cr, 1, 1, 1);
1519         cairo_fill (cr);
1520         cairo_destroy (cr);
1521
1522         io_stat_redraw(io);
1523         return TRUE;
1524 }
1525
1526 static void
1527 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1528 {
1529         io_stat_t *io = user_data;
1530         guint32 mi;
1531
1532         mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1533         if(io->last_interval==mi){
1534                 return;
1535         }
1536         if( (io->last_interval==0xffffffff)
1537         &&  (mi==io->max_interval) ){
1538                 return;
1539         }
1540
1541         io->last_interval=(mi/io->interval)*io->interval;
1542         io_stat_redraw(io);
1543
1544         return;
1545 }
1546
1547 /* redraw the screen from the backing pixmap */
1548 static gboolean
1549 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1550 {
1551         io_stat_t *io = user_data;
1552         cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1553
1554 #if GTK_CHECK_VERSION(2,22,0)
1555         cairo_set_source_surface (cr, io->surface, 0, 0); 
1556 #else
1557         gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1558 #endif
1559         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1560         cairo_fill (cr);
1561
1562         cairo_destroy (cr);
1563
1564         return FALSE;
1565 }
1566
1567 static void
1568 create_draw_area(io_stat_t *io, GtkWidget *box)
1569 {
1570         io->draw_area=gtk_drawing_area_new();
1571         g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1572
1573         gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1574
1575         /* signals needed to handle backing pixmap */
1576         g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1577         g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1578         gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1579         g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1580
1581         gtk_widget_show(io->draw_area);
1582         gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1583
1584         /* create the associated scrollbar */
1585         io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1586         io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1587         gtk_widget_show(io->scrollbar);
1588         gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1589         g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1590 }
1591
1592 static void
1593 tick_interval_select(GtkWidget *item, gpointer user_data)
1594 {
1595         io_stat_t *io = user_data;
1596         int i;
1597
1598         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1599
1600         io->interval=tick_interval_values[i];
1601         cf_retap_packets(&cfile);
1602         gdk_window_raise(gtk_widget_get_window(io->window));
1603         io_stat_redraw(io);
1604 }
1605
1606 static void
1607 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1608 {
1609         io_stat_t *io = user_data;
1610         int i;
1611
1612         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1613         io->pixels_per_tick=pixels_per_tick[i];
1614         io_stat_redraw(io);
1615 }
1616
1617 static void
1618 plot_style_select(GtkWidget *item, gpointer user_data)
1619 {
1620         io_stat_graph_t *ppt = user_data;
1621         int val;
1622
1623         val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1624
1625         ppt->plot_style=val;
1626
1627         io_stat_redraw(ppt->io);
1628 }
1629
1630 static GtkWidget *
1631 create_pixels_per_tick_menu_items(io_stat_t *io)
1632 {
1633         char str[5];
1634         GtkWidget *combo_box;
1635         int i;
1636         combo_box = gtk_combo_box_text_new ();
1637
1638         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1639                 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1640                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1641         }
1642         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1643         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1644
1645         return combo_box;
1646 }
1647
1648 static void
1649 yscale_select(GtkWidget *item, gpointer user_data)
1650 {
1651         io_stat_t *io = user_data;
1652         int i;
1653
1654         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1655
1656         io->max_y_units = yscale_max[i];
1657         io_stat_redraw(io);
1658 }
1659
1660 static GtkWidget *
1661 create_tick_interval_menu_items(io_stat_t *io)
1662 {
1663         GtkWidget *combo_box;
1664         char str[15];
1665         int i;
1666
1667         combo_box = gtk_combo_box_text_new ();
1668
1669         for(i=0;i<MAX_TICK_VALUES;i++){
1670                 if(tick_interval_values[i]>=60000){
1671                         g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1672                 } else if(tick_interval_values[i]>=1000){
1673                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1674                 } else if(tick_interval_values[i]>=100){
1675                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1676                 } else if(tick_interval_values[i]>=10){
1677                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1678                 } else {
1679                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1680                 }
1681                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1682         }
1683         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1684         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1685
1686         return combo_box;
1687 }
1688
1689 static GtkWidget *
1690 create_yscale_max_menu_items(io_stat_t *io)
1691 {
1692         char str[15];
1693         GtkWidget *combo_box;
1694         int i;
1695
1696         combo_box = gtk_combo_box_text_new ();
1697         for(i=0;i<MAX_YSCALE;i++){
1698                 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1699                         g_strlcpy(str, "Logarithmic", 15);
1700                 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1701                         g_strlcpy(str, "Auto", 15);
1702                 } else {
1703                         g_snprintf(str, 15, "%u", yscale_max[i]);
1704                 }
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_YSCALE_INDEX);
1708         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1709         return combo_box;
1710 }
1711
1712 static void
1713 count_type_select(GtkWidget *item, gpointer user_data)
1714 {
1715         io_stat_t *io = user_data;
1716         static gboolean advanced_visible=FALSE;
1717         int i;
1718         GtkAllocation widget_alloc;
1719
1720         io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1721
1722         if(io->count_type==COUNT_TYPE_ADVANCED){
1723                 for(i=0;i<MAX_GRAPHS;i++){
1724                         disable_graph(&io->graphs[i]);
1725                         gtk_widget_show(io->graphs[i].advanced_buttons);
1726                         /* redraw the entire window so the unhidden widgets show up, hopefully */
1727                         gtk_widget_get_allocation(io->window, &widget_alloc);
1728                         gtk_widget_queue_draw_area(io->window,
1729                                                    0,
1730                                                    0,
1731                                                    widget_alloc.width,
1732                                                    widget_alloc.height);
1733                 }
1734                 advanced_visible=TRUE;
1735                 io_stat_redraw(io);
1736         } else if (advanced_visible) {
1737                 for(i=0;i<MAX_GRAPHS;i++){
1738                         gtk_widget_hide(io->graphs[i].advanced_buttons);
1739                         filter_callback(item, &io->graphs[i]);
1740                 }
1741                 advanced_visible=FALSE;
1742         } else {
1743                 io_stat_redraw(io);
1744         }
1745 }
1746
1747 static GtkWidget *
1748 create_frames_or_bytes_menu_items(io_stat_t *io)
1749 {
1750         GtkWidget *combo_box;
1751         int i;
1752
1753         combo_box = gtk_combo_box_text_new ();
1754
1755         for(i=0;i<MAX_COUNT_TYPES;i++){
1756                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
1757         }
1758         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1759         g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1760         return combo_box;
1761 }
1762
1763 static void
1764 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1765 {
1766         GtkWidget *hbox;
1767         GtkWidget *label;
1768         GtkWidget *combo_box;
1769
1770         hbox=gtk_hbox_new(FALSE, 0);
1771         gtk_container_add(GTK_CONTAINER(box), hbox);
1772         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1773         gtk_widget_show(hbox);
1774
1775         label=gtk_label_new(name);
1776         gtk_widget_show(label);
1777         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1778
1779         combo_box = (*func)(io);
1780         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1781         gtk_widget_show(combo_box);
1782 }
1783
1784 static void
1785 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1786 {
1787         io_stat_t *io = user_data;
1788
1789         io->view_as_time = io->view_as_time ? FALSE : TRUE;
1790
1791         io_stat_redraw(io);
1792 }
1793
1794 static void
1795 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1796 {
1797         GtkWidget *frame_vbox;
1798         GtkWidget *frame;
1799         GtkWidget *vbox;
1800         GtkWidget *view_cb;
1801
1802         frame_vbox=gtk_vbox_new(FALSE, 0);
1803         gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1804         gtk_widget_show(frame_vbox);
1805
1806         frame = gtk_frame_new("X Axis");
1807         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1808         gtk_widget_show(frame);
1809
1810         vbox=gtk_vbox_new(FALSE, 0);
1811         gtk_container_add(GTK_CONTAINER(frame), vbox);
1812         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1813         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1814         gtk_widget_show(vbox);
1815
1816         create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1817         create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1818
1819         view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1820         gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1821         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1822         g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1823         gtk_widget_show(view_cb);
1824
1825         frame = gtk_frame_new("Y Axis");
1826         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1827         gtk_widget_show(frame);
1828
1829         vbox=gtk_vbox_new(FALSE, 0);
1830         gtk_container_add(GTK_CONTAINER(frame), vbox);
1831         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1832         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1833         gtk_widget_show(vbox);
1834
1835         create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1836         create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1837
1838         return;
1839 }
1840
1841 static void
1842 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1843 {
1844         io_stat_graph_t *gio = user_data;
1845         const char *filter;
1846         const char *field=NULL;
1847         header_field_info *hfi;
1848         dfilter_t *dfilter;
1849
1850         /* this graph is not active, just update display and redraw */
1851         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1852                 disable_graph(gio);
1853                 io_stat_redraw(gio->io);
1854                 return;
1855         }
1856
1857         /* first check if the field string is valid */
1858         if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1859                 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1860
1861                 /* warn and bail out if there was no field specified */
1862                 if(field==NULL || field[0]==0){
1863                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1864                         disable_graph(gio);
1865                         io_stat_redraw(gio->io);
1866                         return;
1867                 }
1868                 /* warn and bail out if the field could not be found */
1869                 hfi=proto_registrar_get_byname(field);
1870                 if(hfi==NULL){
1871                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1872                         disable_graph(gio);
1873                         io_stat_redraw(gio->io);
1874                         return;
1875                 }
1876                 gio->hf_index=hfi->id;
1877                 /* check that the type is compatible */
1878                 switch(hfi->type){
1879                 case FT_UINT8:
1880                 case FT_UINT16:
1881                 case FT_UINT24:
1882                 case FT_UINT32:
1883                 case FT_INT8:
1884                 case FT_INT16:
1885                 case FT_INT24:
1886                 case FT_INT32:
1887                 case FT_FLOAT:
1888                 case FT_DOUBLE:
1889                         /* these values support all calculations except LOAD */
1890                         switch(gio->calc_type){
1891                         case CALC_TYPE_LOAD:
1892                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1893                                     "LOAD(*) is only supported for relative-time fields.");
1894                                 disable_graph(gio);
1895                                 io_stat_redraw(gio->io);
1896                                 return;
1897                         }
1898                         /* these types support all calculations */
1899                         break;
1900                 case FT_RELATIVE_TIME:
1901                         /* this type only supports COUNT, MAX, MIN, AVG */
1902                         switch(gio->calc_type){
1903                         case CALC_TYPE_SUM:
1904                         case CALC_TYPE_COUNT:
1905                         case CALC_TYPE_MAX:
1906                         case CALC_TYPE_MIN:
1907                         case CALC_TYPE_AVG:
1908                         case CALC_TYPE_LOAD:
1909                                 break;
1910                         default:
1911                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1912                                     "%s is a relative-time field, so %s calculations are not supported on it.",
1913                                     field,
1914                                     calc_type_names[gio->calc_type]);
1915                                 disable_graph(gio);
1916                                 io_stat_redraw(gio->io);
1917                                 return;
1918                         }
1919                         break;
1920                 case FT_UINT64:
1921                 case FT_INT64:
1922                         /*
1923                          * XXX - support this if gint64/guint64 are
1924                          * available?
1925                          */
1926                         if(gio->calc_type!=CALC_TYPE_COUNT){
1927                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1928                                     "%s is a 64-bit integer, so %s calculations are not supported on it.",
1929                                     field,
1930                                     calc_type_names[gio->calc_type]);
1931                                 disable_graph(gio);
1932                                 io_stat_redraw(gio->io);
1933                                 return;
1934                         }
1935                         break;
1936                 default:
1937                         if(gio->calc_type!=CALC_TYPE_COUNT){
1938                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1939                                     "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1940                                     field,
1941                                     calc_type_names[gio->calc_type]);
1942                                 disable_graph(gio);
1943                                 io_stat_redraw(gio->io);
1944                                 return;
1945                         }
1946                         break;
1947                 }
1948         }
1949
1950         /* first check if the filter string is valid. */
1951         filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1952         if(!dfilter_compile(filter, &dfilter)) {
1953                 bad_dfilter_alert_box(filter);
1954                 disable_graph(gio);
1955                 io_stat_redraw(gio->io);
1956                 return;
1957         }
1958         if (dfilter != NULL)
1959                 dfilter_free(dfilter);
1960
1961         /* ok, we have a valid filter and the graph is active.
1962            first just try to delete any previous settings and then apply
1963            the new ones.
1964         */
1965         protect_thread_critical_region();
1966         remove_tap_listener(gio);
1967         unprotect_thread_critical_region();
1968
1969         io_stat_reset(gio->io);
1970         enable_graph(gio, filter, field);
1971         cf_retap_packets(&cfile);
1972         gdk_window_raise(gtk_widget_get_window(gio->io->window));
1973         io_stat_redraw(gio->io);
1974
1975         return;
1976 }
1977
1978 static void
1979 calc_type_select(GtkWidget *item, gpointer user_data)
1980 {
1981         io_stat_graph_t *gio = user_data;
1982
1983         gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1984
1985         /* disable the graph */
1986         disable_graph(gio);
1987         io_stat_redraw(gio->io);
1988 }
1989
1990 static GtkWidget *
1991 create_calc_types_menu_items(io_stat_graph_t *gio)
1992 {
1993         GtkWidget *combo_box;
1994         int i;
1995
1996         combo_box = gtk_combo_box_text_new ();
1997         for(i=0;i<MAX_CALC_TYPES;i++){
1998                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
1999         }
2000         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
2001         g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
2002         return combo_box;
2003 }
2004
2005 static void
2006 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name,  GtkWidget *(*func)(io_stat_graph_t *io))
2007 {
2008         GtkWidget *hbox;
2009         GtkWidget *label;
2010         GtkWidget *combo_box;
2011
2012         hbox=gtk_hbox_new(FALSE, 0);
2013         gtk_container_add(GTK_CONTAINER(box), hbox);
2014         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2015         gtk_widget_show(hbox);
2016
2017         label=gtk_label_new(name);
2018         gtk_widget_show(label);
2019         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2020
2021         combo_box = (*func)(gio);
2022         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2023         gtk_widget_show(combo_box);
2024 }
2025
2026 static void
2027 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2028 {
2029
2030         gio->calc_field=gtk_entry_new();
2031         gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
2032         gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
2033         gtk_widget_show(gio->calc_field);
2034         g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
2035         g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
2036         g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2037         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2038         g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2039         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2040         colorize_filter_te_as_empty(gio->calc_field);
2041 }
2042
2043 static void
2044 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2045 {
2046         GtkWidget *hbox;
2047
2048         hbox=gtk_hbox_new(FALSE, 0);
2049         gio->advanced_buttons=hbox;
2050         gtk_container_add(GTK_CONTAINER(box), hbox);
2051         gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
2052         gtk_widget_hide(hbox);
2053
2054         gio->calc_type=CALC_TYPE_SUM;
2055         create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
2056         create_advanced_field(gio, hbox);
2057 }
2058
2059 static void
2060 filter_button_clicked(GtkWidget *w, gpointer user_data)
2061 {
2062         io_stat_graph_t *gio = user_data;
2063
2064         display_filter_construct_cb(w, gio->args);
2065         return;
2066 }
2067
2068 static void
2069 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2070 {
2071         GtkWidget *combo_box;
2072         GtkWidget *hbox;
2073         GtkWidget *label;
2074         char str[256];
2075         int i;
2076
2077         hbox=gtk_hbox_new(FALSE, 3);
2078         gtk_container_add(GTK_CONTAINER(box), hbox);
2079         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2080         gtk_widget_show(hbox);
2081
2082         g_snprintf(str, 256, "Graph %d", num);
2083         gio->display_button=gtk_toggle_button_new_with_label(str);
2084         gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2085         gtk_widget_show(gio->display_button);
2086         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2087         g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2088
2089         label=gtk_label_new("Color");
2090         gtk_widget_show(label);
2091         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2092
2093 #if GTK_CHECK_VERSION(3,0,0)
2094         /* Fix me: Add the GTK 3.0 equivalents */
2095 #else   
2096         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2097         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2098         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2099         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2100         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2101 #endif
2102 /*      g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2103
2104
2105         /* filter prefs dialog */
2106         gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2107
2108         g_snprintf(str, 256, "Wireshark: Display Filter  IO-Stat (Filter:%d)", num);
2109         g_free( (gpointer) (gio->args->title) );
2110         gio->args->title=g_strdup(str);
2111
2112         g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2113         g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2114
2115         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2116         gtk_widget_show(gio->filter_bt);
2117
2118         gio->filter_field=gtk_entry_new();
2119         gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2120         /* filter prefs dialog */
2121         g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2122         /* filter prefs dialog */
2123
2124         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2125         gtk_widget_show(gio->filter_field);
2126         g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2127         g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2128         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2129         g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2130         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2131         colorize_filter_te_as_empty(gio->filter_field);
2132
2133         create_advanced_box(gio, hbox);
2134
2135         /*
2136          * create PlotStyle menu
2137          */
2138         g_snprintf(str, 256, " Style:");
2139         label=gtk_label_new(str);
2140         gtk_widget_show(label);
2141         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2142
2143         combo_box = gtk_combo_box_text_new ();
2144         for(i=0;i<MAX_PLOT_STYLES;i++){
2145                 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
2146         }
2147         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2148         g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2149
2150         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2151         gtk_widget_show(combo_box);
2152
2153         return;
2154 }
2155
2156 static void
2157 create_filter_area(io_stat_t *io, GtkWidget *box)
2158 {
2159         GtkWidget *frame;
2160         GtkWidget *vbox;
2161         int i;
2162
2163         frame=gtk_frame_new("Graphs");
2164         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2165         gtk_widget_show(frame);
2166
2167         vbox=gtk_vbox_new(FALSE, 1);
2168         gtk_container_add(GTK_CONTAINER(frame), vbox);
2169         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2170         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2171         gtk_widget_show(vbox);
2172
2173         for(i=0;i<MAX_GRAPHS;i++){
2174                 create_filter_box(&io->graphs[i], vbox, i+1);
2175         }
2176
2177         return;
2178 }
2179
2180 static void
2181 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2182 {
2183         guint32         i, interval, val;
2184         char            string[15];
2185         GtkClipboard    *cb;
2186         GString         *CSV_str=g_string_new("");
2187         io_stat_t       *io = user_data;
2188
2189         g_string_append(CSV_str, "\"Interval start\"");
2190         for(i=0;i<MAX_GRAPHS;i++) {
2191                 if (io->graphs[i].display) {
2192                         g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2193                 }
2194         }
2195         g_string_append(CSV_str,"\n");
2196
2197         for(interval=0; interval<io->max_interval; interval+=io->interval) {
2198                 print_interval_string (string, 15, interval, io, FALSE);
2199                 g_string_append_printf(CSV_str, "\"%s\"", string);
2200                 for(i=0;i<MAX_GRAPHS;i++) {
2201                         if (io->graphs[i].display) {
2202                                 val=get_it_value(io, i, interval/io->interval);
2203                                 g_string_append_printf(CSV_str, ",\"%d\"", val);
2204                         }
2205                 }
2206                 g_string_append(CSV_str,"\n");
2207         }
2208
2209         /* Now that we have the CSV data, copy it into the default clipboard */
2210         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);    /* Get the default clipboard */
2211         gtk_clipboard_set_text(cb, CSV_str->str, -1);       /* Copy the CSV data into the clipboard */
2212         g_string_free(CSV_str, TRUE);                       /* Free the memory */
2213 }
2214
2215 static void
2216 init_io_stat_window(io_stat_t *io)
2217 {
2218         GtkWidget *vbox;
2219         GtkWidget *hbox;
2220         GtkWidget *bbox;
2221         GtkWidget *close_bt, *help_bt;
2222         GtkWidget *copy_bt;
2223         GtkWidget *save_bt;
2224
2225         /* create the main window, transient_for top_level */
2226         io->window = dlg_window_new("I/O Graphs");
2227         gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2228
2229         vbox=gtk_vbox_new(FALSE, 0);
2230         gtk_container_add(GTK_CONTAINER(io->window), vbox);
2231         gtk_widget_show(vbox);
2232
2233         create_draw_area(io, vbox);
2234
2235         hbox=gtk_hbox_new(FALSE, 3);
2236         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2237         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2238         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2239         gtk_widget_show(hbox);
2240
2241         create_filter_area(io, hbox);
2242         create_ctrl_area(io, hbox);
2243
2244         io_stat_set_title(io);
2245
2246         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2247                                   GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2248         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2249         gtk_widget_show(bbox);
2250
2251         close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2252         window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2253         gtk_widget_set_tooltip_text(close_bt,  "Close this dialog");
2254         save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2255         gtk_widget_set_sensitive(save_bt, FALSE);
2256         gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
2257         g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2258         g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2259
2260         copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2261         gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
2262         g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2263
2264         help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2265         g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2266         gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
2267         g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2268
2269         gtk_widget_show(io->window);
2270         window_present(io->window);
2271 }
2272
2273 #ifdef MAIN_MENU_USE_UIMANAGER
2274 void
2275 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2276 {
2277         iostat_init(NULL,NULL);
2278 }
2279 #else
2280 static void
2281 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2282 {
2283         iostat_init(NULL,NULL);
2284 }
2285 #endif
2286
2287 void
2288 register_tap_listener_gtk_iostat(void)
2289 {
2290         register_stat_cmd_arg("io,stat", iostat_init,NULL);
2291
2292 #ifdef MAIN_MENU_USE_UIMANAGER
2293 #else
2294         register_stat_menu_item_stock("_IO Graphs",
2295                                       REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2296                                       gui_iostat_cb, NULL, NULL, NULL);
2297 #endif
2298 }