Again, some warnings removed.
[obnox/wireshark/wip.git] / gtk / io_stat.c
1 /* io_stat.c
2  * io_stat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 <string.h>
35
36 #include <ctype.h>
37
38 #include <gtk/gtk.h>
39
40 #include <epan/epan_dissect.h>
41 #include <epan/packet_info.h>
42
43 #include "gtkglobals.h"
44 #include "ui_util.h"
45 #include "tap_menu.h"
46 #include <epan/tap.h>
47 #include "../register.h"
48 #include "alert_box.h"
49 #include "simple_dialog.h"
50 #include "../globals.h"
51 #include "../color.h"
52 #include "compat_macros.h"
53 #include "dlg_utils.h"
54 #include "filter_dlg.h"
55
56 void protect_thread_critical_region(void);
57 void unprotect_thread_critical_region(void);
58
59 #define MAX_GRAPHS 5
60
61 #define MAX_YSCALE 22
62 #define AUTO_MAX_YSCALE 0
63 static guint32 yscale_max[MAX_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};
64
65 #define MAX_PIXELS_PER_TICK 4
66 #define DEFAULT_PIXELS_PER_TICK 2
67 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
68
69
70 #define DEFAULT_PLOT_STYLE      0
71 #define PLOT_STYLE_LINE         0
72 #define PLOT_STYLE_IMPULSE      1
73 #define PLOT_STYLE_FILLED_BAR   2
74 #define MAX_PLOT_STYLES         3
75 static char *plot_style_name[MAX_PLOT_STYLES] = {
76         "Line",
77         "Impulse",
78         "FBar",
79 };
80
81
82 #define COUNT_TYPE_FRAMES   0
83 #define COUNT_TYPE_BYTES    1
84 #define COUNT_TYPE_ADVANCED 2
85 #define MAX_COUNT_TYPES 3
86 static char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Advanced..."};
87
88 /* unit is in ms */
89 #define MAX_TICK_VALUES 5
90 #define DEFAULT_TICK_VALUE 3
91 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
92
93 #define CALC_TYPE_SUM   0
94 #define CALC_TYPE_COUNT 1
95 #define CALC_TYPE_MAX   2
96 #define CALC_TYPE_MIN   3
97 #define CALC_TYPE_AVG   4
98 #define CALC_TYPE_LOAD  5
99 #define MAX_CALC_TYPES 6
100 static char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
101
102
103 typedef struct _io_stat_calc_type_t {
104         struct _io_stat_graph_t *gio;
105         int calc_type;
106 } io_stat_calc_type_t;
107
108 #define NUM_IO_ITEMS 100000
109 typedef struct _io_item_t {
110         guint32 frames; /* always calculated, will hold number of frames*/
111         guint32 bytes;  /* always calculated, will hold number of bytes*/
112         gint32 int_max;
113         gint32 int_min;
114         gint32 int_tot;
115         nstime_t time_max;
116         nstime_t time_min;
117         nstime_t time_tot;
118 } io_item_t;
119
120 typedef struct _io_stat_graph_t {
121         struct _io_stat_t *io;
122         io_item_t items[NUM_IO_ITEMS];
123         int plot_style;
124         gboolean display;
125         GtkWidget *display_button;
126         GtkWidget *filter_field;
127         GtkWidget *advanced_buttons;
128         int calc_type;
129         io_stat_calc_type_t calc_types[MAX_CALC_TYPES];
130         int hf_index;
131         GtkWidget *calc_field;
132         GdkColor color;
133         GdkGC *gc;
134         construct_args_t *args;
135         GtkWidget *filter_bt;
136 } io_stat_graph_t;
137
138
139 typedef struct _io_stat_t {
140         gboolean needs_redraw;
141         gint32 interval;    /* measurement interval in ms */
142         guint32 last_interval; 
143         guint32 max_interval; /* XXX max_interval and num_items are redundant */
144         guint32 num_items;
145
146         struct _io_stat_graph_t graphs[MAX_GRAPHS];
147         GtkWidget *window;
148         GtkWidget *draw_area;
149         GdkPixmap *pixmap;
150         GtkAdjustment *scrollbar_adjustment;
151         GtkWidget *scrollbar;
152         int pixmap_width;
153         int pixmap_height;
154         int pixels_per_tick;
155         int max_y_units;
156         int count_type;
157 } io_stat_t;    
158
159 #if GTK_MAJOR_VERSION < 2
160 GtkRcStyle *rc_style;
161 GdkColormap *colormap;
162 #endif
163
164
165
166 static void init_io_stat_window(io_stat_t *io);
167
168 static void
169 io_stat_set_title(io_stat_t *io)
170 {
171         char            *title;
172
173         if(!io->window){
174                 return;
175         }
176         title = g_strdup_printf("Ethereal IO Graphs: %s", cf_get_display_name(&cfile));
177         gtk_window_set_title(GTK_WINDOW(io->window), title);
178         g_free(title);
179 }
180
181 static void
182 io_stat_reset(io_stat_t *io)
183 {
184         int i, j;
185
186         io->needs_redraw=TRUE;
187         for(i=0;i<MAX_GRAPHS;i++){
188                 for(j=0;j<NUM_IO_ITEMS;j++){
189                         io_item_t *ioi;
190                         ioi=&io->graphs[i].items[j];
191
192                         ioi->frames=0;
193                         ioi->bytes=0;
194                         ioi->int_max=0;
195                         ioi->int_min=0;
196                         ioi->int_tot=0;
197                         ioi->time_max.secs=0;
198                         ioi->time_max.nsecs=0;
199                         ioi->time_min.secs=0;
200                         ioi->time_min.nsecs=0;
201                         ioi->time_tot.secs=0;
202                         ioi->time_tot.nsecs=0;
203                 }
204         }
205         io->last_interval=0xffffffff;
206         io->max_interval=0;
207         io->num_items=0;
208
209         io_stat_set_title(io);
210 }
211
212 static void
213 gtk_iostat_reset(void *g)
214 {
215         io_stat_graph_t *gio=g;
216
217         io_stat_reset(gio->io);
218 }
219
220 static int
221 gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy _U_)
222 {
223         io_stat_graph_t *git=g;
224         io_item_t *it;
225         nstime_t time_delta;
226         int idx;
227
228         /* we sometimes get called when git is disabled.
229            this is a bug since the tap listener should be removed first */
230         if(!git->display){
231                 return 0;
232         }
233
234         git->io->needs_redraw=TRUE;
235
236         /* 
237          * Find which interval this is supposed to to in and store the
238          * interval index as idx
239          */
240         time_delta.secs=pinfo->fd->rel_secs;
241         time_delta.nsecs=pinfo->fd->rel_usecs*1000;
242         if(time_delta.nsecs<0){
243                 time_delta.secs--;
244                 time_delta.nsecs+=1000000000;
245         }
246         if(time_delta.secs<0){
247                 return FALSE;
248         }
249         idx=(time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval;
250
251         /* some sanity checks */
252         if((idx<0)||(idx>=NUM_IO_ITEMS)){
253                 return FALSE;
254         }
255
256         /* update num_items */
257         if((guint32)idx > git->io->num_items){
258                 git->io->num_items=idx;
259                 git->io->max_interval=idx*git->io->interval;
260         }
261
262         /*
263          * Find the appropriate io_item_t structure 
264          */
265         it=&git->items[idx];
266
267
268         /*
269          * For ADVANCED mode we need to keep track of some more stuff
270          * than just frame and byte counts
271          */
272         if(git->io->count_type==COUNT_TYPE_ADVANCED){
273                 GPtrArray *gp;
274                 guint i;
275
276                 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
277                 if(!gp){
278                         return FALSE;
279                 }
280
281                 /* update the appropriate counters, make sure that if 
282                  * frames==0 then this is the first seen value so
283                  * set any min/max values accordingly 
284                  */
285                 for(i=0;i<gp->len;i++){
286                         int new_int;
287                         nstime_t *new_time;
288
289                         switch(proto_registrar_get_ftype(git->hf_index)){
290                         case FT_UINT8:
291                         case FT_UINT16:
292                         case FT_UINT24:
293                         case FT_UINT32:
294                         case FT_INT8:
295                         case FT_INT16:
296                         case FT_INT24:
297                         case FT_INT32:
298                                 new_int=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
299
300                                 if((new_int>it->int_max)||(it->frames==0)){
301                                         it->int_max=new_int;
302                                 }
303                                 if((new_int<it->int_min)||(it->frames==0)){
304                                         it->int_min=new_int;
305                                 }
306                                 it->int_tot+=new_int;
307                                 break;
308                         case FT_RELATIVE_TIME:
309                                 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
310
311                                 switch(git->calc_type){
312 #ifdef G_HAVE_UINT64
313                                         guint64 t, pt; /* time in us */
314 #else
315                                         guint32 t, pt;
316 #endif
317                                         int i;
318                                 case CALC_TYPE_LOAD:
319                                         /* it is a LOAD calculation of a relative time field. 
320                                          * add the time this call spanned to each
321                                          * interval it spanned according to its contribution 
322                                          * to that interval.
323                                          */
324                                         t=new_time->secs;
325                                         t=t*1000000+new_time->nsecs/1000;
326                                         i=idx;
327                                         /* handle current interval */
328                                         pt=pinfo->fd->rel_secs*1000000+pinfo->fd->rel_usecs;
329                                         pt=pt%(git->io->interval*1000);
330                                         if(pt>t){
331                                                 pt=t;
332                                         }
333                                         while(t){
334                                                 git->items[i].time_tot.nsecs+=pt*1000;
335                                                 if(git->items[i].time_tot.nsecs>1000000000){
336                                                         git->items[i].time_tot.secs++;
337                                                         git->items[i].time_tot.nsecs-=1000000000;
338                                                 }
339
340                                                 if(i==0){
341                                                         break;
342                                                 }
343                                                 i--;
344                                                 t-=pt;
345                                                 if(t > (guint32) (git->io->interval*1000)){
346                                                         pt=git->io->interval*1000;
347                                                 } else {
348                                                         pt=t;
349                                                 }
350                                         }
351                                         break;
352                                 default:
353                                         if( (new_time->secs>it->time_max.secs)
354                                         ||( (new_time->secs==it->time_max.secs)
355                                           &&(new_time->nsecs>it->time_max.nsecs))
356                                         ||(it->frames==0)){
357                                                 it->time_max.secs=new_time->secs;
358                                                 it->time_max.nsecs=new_time->nsecs;
359                                         }
360                                         if( (new_time->secs<it->time_min.secs)
361                                         ||( (new_time->secs==it->time_min.secs)
362                                           &&(new_time->nsecs<it->time_min.nsecs))
363                                         ||(it->frames==0)){
364                                                 it->time_min.secs=new_time->secs;
365                                                 it->time_min.nsecs=new_time->nsecs;
366                                         }
367                                         it->time_tot.secs+=new_time->secs;
368                                         it->time_tot.nsecs+=new_time->nsecs;
369                                         if(it->time_tot.nsecs>=1000000000){
370                                                 it->time_tot.nsecs-=1000000000;
371                                                 it->time_tot.secs++;
372                                         }
373                                 }
374
375                         }
376                 }
377         }
378
379         it->frames++;
380         it->bytes+=pinfo->fd->pkt_len;
381         
382         return TRUE;
383 }
384
385
386 static guint32
387 get_it_value(io_stat_t *io, int graph_id, int idx)
388 {
389         guint32 value=0;
390         int adv_type;
391         io_item_t *it;
392
393         it=&io->graphs[graph_id].items[idx];
394
395         switch(io->count_type){
396         case COUNT_TYPE_FRAMES:
397                 return it->frames;
398         case COUNT_TYPE_BYTES:
399                 return it->bytes;
400         }
401
402
403         adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
404         switch(adv_type){
405         case FT_NONE:
406                 switch(io->graphs[graph_id].calc_type){
407                 case CALC_TYPE_COUNT:
408                         value=it->frames;
409                         break;
410                 default:
411                         break;
412                 }
413                 break;
414         case FT_UINT8:
415         case FT_UINT16:
416         case FT_UINT24:
417         case FT_UINT32:
418         case FT_INT8:
419         case FT_INT16:
420         case FT_INT24:
421         case FT_INT32:
422                 switch(io->graphs[graph_id].calc_type){
423                 case CALC_TYPE_SUM:
424                         value=it->int_tot;
425                         break;
426                 case CALC_TYPE_COUNT:
427                         value=it->frames;
428                         break;
429                 case CALC_TYPE_MAX:
430                         value=it->int_max;
431                         break;
432                 case CALC_TYPE_MIN:
433                         value=it->int_min;
434                         break;
435                 case CALC_TYPE_AVG:
436                         if(it->frames){
437                                 value=it->int_tot/it->frames;
438                         } else {
439                                 value=0;
440                         }
441                         break;
442                 default:
443                         break;
444                 }
445                 break;
446         case FT_RELATIVE_TIME:
447                 switch(io->graphs[graph_id].calc_type){
448                 case CALC_TYPE_COUNT:
449                         value=it->frames;
450                         break;
451                 case CALC_TYPE_MAX:
452                         value=it->time_max.secs*1000000+it->time_max.nsecs/1000;
453                         break;
454                 case CALC_TYPE_MIN:
455                         value=it->time_min.secs*1000000+it->time_min.nsecs/1000;
456                         break;
457                 case CALC_TYPE_AVG:
458                         if(it->frames){
459 #ifdef G_HAVE_UINT64
460                                 guint64 t; /* time in us */
461 #else
462                                 guint32 t;
463 #endif
464                                 t=it->time_tot.secs;
465                                 t=t*1000000+it->time_tot.nsecs/1000;
466                                 value=t/it->frames;
467                         } else {
468                                 value=0;
469                         }
470                         break;
471                 case CALC_TYPE_LOAD:
472                         value=(it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval;
473                         break;
474                 default:
475                         break;
476                 }
477                 break;
478         default:
479                 break;
480         }
481         return value;
482 }
483
484
485 static void
486 print_time_scale_string(char *buf, int buf_len, guint32 t)
487 {
488         if(t>=10000000){
489                 g_snprintf(buf, buf_len, "%ds",t/1000000);
490         } else if(t>=1000000){
491                 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
492         } else if(t>=10000){
493                 g_snprintf(buf, buf_len, "%dms",t/1000);
494         } else if(t>=1000){
495                 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
496         } else {
497                 g_snprintf(buf, buf_len, "%dus",t);
498         }
499 }
500
501 static void
502 io_stat_draw(io_stat_t *io)
503 {
504         int i;
505         guint32 last_interval, first_interval, interval_delta, delta_multiplier;
506         gint32 current_interval;
507         guint32 left_x_border;
508         guint32 right_x_border;
509         guint32 top_y_border;
510         guint32 bottom_y_border;
511 #if GTK_MAJOR_VERSION < 2
512         GdkFont *font;
513 #else
514         PangoLayout  *layout;
515 #endif
516         guint32 label_width, label_height;
517         guint32 draw_width, draw_height;
518         char label_string[15];
519
520         /* new variables */
521         guint32 num_time_intervals;
522         guint32 max_value;              /* max value of seen data */
523         guint32 max_y;                  /* max value of the Y scale */
524         gboolean draw_y_as_time;
525
526 #if GTK_MAJOR_VERSION <2
527         font = io->draw_area->style->font;
528 #endif
529
530         if(!io->needs_redraw){
531                 return;
532         }
533         io->needs_redraw=FALSE;
534
535
536         /* 
537          * Find the length of the intervals we have data for
538          * so we know how large arrays we need to malloc()
539          */
540         num_time_intervals=io->num_items;
541         /* if there isnt anything to do, just return */
542         if(num_time_intervals==0){
543                 return;
544         }
545         num_time_intervals+=1;
546         /* XXX move this check to _packet() */
547         if(num_time_intervals>NUM_IO_ITEMS){
548                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
549                 return;
550         }
551
552
553         /* 
554          * find the max value so we can autoscale the y axis
555          */
556         max_value=0;
557         for(i=0;i<MAX_GRAPHS;i++){
558                 int idx;
559
560                 if(!io->graphs[i].display){
561                         continue;
562                 }
563                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
564                         guint32 val;
565
566                         val=get_it_value(io, i, idx);
567
568                         /* keep track of the max value we have encountered */
569                         if(val>max_value){
570                                 max_value=val;
571                         }
572                 }
573         }
574
575
576
577         /* 
578          * Clear out old plot
579          */
580         gdk_draw_rectangle(io->pixmap,
581                            io->draw_area->style->white_gc,
582                            TRUE,
583                            0, 0,
584                            io->draw_area->allocation.width,
585                            io->draw_area->allocation.height);
586
587
588         /*
589          * Calculate the y scale we should use
590          */
591         if(io->max_y_units==AUTO_MAX_YSCALE){
592                 max_y=yscale_max[MAX_YSCALE-1];
593                 for(i=MAX_YSCALE-1;i>0;i--){
594                         if(max_value<yscale_max[i]){
595                                 max_y=yscale_max[i];
596                         }
597                 }
598         } else {
599                 /* the user had specified an explicit y scale to use */
600                 max_y=io->max_y_units;
601         }
602
603
604         /*
605          * If we use ADVANCED and all the graphs are plotting
606          * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
607          * then we will do some some special processing for the
608          * labels for the Y axis below:
609          *   we will append the time unit " s" " ms" or " us"
610          *   and we will present the unit in decimal
611          */
612         draw_y_as_time=FALSE;
613         if(io->count_type==COUNT_TYPE_ADVANCED){
614                 draw_y_as_time=TRUE;
615                 for(i=0;i<MAX_GRAPHS;i++){
616                         int adv_type;
617
618                         if(!io->graphs[i].display){
619                                 continue;
620                         }
621                         adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
622                         switch(adv_type){
623                         case FT_RELATIVE_TIME:
624                                 switch(io->graphs[i].calc_type){
625                                 case CALC_TYPE_MAX:
626                                 case CALC_TYPE_MIN:
627                                 case CALC_TYPE_AVG:
628                                         break;
629                                 default:
630                                         draw_y_as_time=FALSE;
631                                 }
632                                 break;
633                         default:
634                                 draw_y_as_time=FALSE;
635                         }
636                 }
637         }
638
639
640
641         /* 
642          * Calculate size of borders surrounding the plot 
643          * The border on the right side needs to be adjusted depending
644          * on the width of the text labels. For simplicity we assume that the
645          * top y scale label will be the widest one
646          */
647         if(draw_y_as_time){
648                 print_time_scale_string(label_string, 15, max_y);
649         } else {
650                 g_snprintf(label_string, 15, "%d", max_y);
651         }
652 #if GTK_MAJOR_VERSION < 2
653         label_width=gdk_string_width(font, label_string);
654         label_height=gdk_string_height(font, label_string);
655 #else
656         layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
657         pango_layout_get_pixel_size(layout, &label_width, &label_height);
658 #endif
659         left_x_border=10;
660         right_x_border=label_width+20;
661         top_y_border=10;
662         bottom_y_border=label_height+20;
663
664
665         /*
666          * Calculate the size of the drawing area for the actual plot
667          */
668         draw_width=io->pixmap_width-right_x_border-left_x_border;
669         draw_height=io->pixmap_height-top_y_border-bottom_y_border;
670
671
672         /* 
673          * Draw the y axis and labels
674          * (we always draw the y scale with 11 ticks along the axis)
675          */
676         gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
677                 io->pixmap_width-right_x_border+1, 
678                 top_y_border,
679                 io->pixmap_width-right_x_border+1, 
680                 io->pixmap_height-bottom_y_border);
681         for(i=0;i<=10;i++){
682                 int xwidth, lwidth;
683
684                 xwidth=5;
685                 if(!(i%5)){
686                         /* first, middle and last tick are slightly longer */
687                         xwidth=10;
688                 }
689                 /* draw the tick */
690                 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, 
691                         io->pixmap_width-right_x_border+1, 
692                         io->pixmap_height-bottom_y_border-draw_height*i/10, 
693                         io->pixmap_width-right_x_border+1+xwidth, 
694                         io->pixmap_height-bottom_y_border-draw_height*i/10);
695                 /* draw the labels */
696                 if(i==0){
697                         if(draw_y_as_time){
698                                 print_time_scale_string(label_string, 15, (max_y*i/10));
699                         } else {
700                                 g_snprintf(label_string, 15, "%d", max_y*i/10);
701                         }
702 #if GTK_MAJOR_VERSION < 2
703                         lwidth=gdk_string_width(font, label_string);
704                         gdk_draw_string(io->pixmap,
705                                         font,
706                                         io->draw_area->style->black_gc,
707                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
708                                         io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
709                                         label_string);
710 #else
711                         pango_layout_set_text(layout, label_string, -1);
712                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
713                         gdk_draw_layout(io->pixmap,
714                                         io->draw_area->style->black_gc,
715                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
716                                         io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
717                                         layout);
718 #endif
719                 }
720                 if(i==5){
721                         if(draw_y_as_time){
722                                 print_time_scale_string(label_string, 15, (max_y*i/10));
723                         } else {
724                                 g_snprintf(label_string, 15, "%d", max_y*i/10);
725                         }
726 #if GTK_MAJOR_VERSION < 2
727                         lwidth=gdk_string_width(font, label_string);
728                         gdk_draw_string(io->pixmap,
729                                         font,
730                                         io->draw_area->style->black_gc,
731                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
732                                         io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
733                                         label_string);
734 #else
735                         pango_layout_set_text(layout, label_string, -1);
736                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
737                         gdk_draw_layout(io->pixmap,
738                                         io->draw_area->style->black_gc,
739                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
740                                         io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
741                                         layout);
742 #endif
743                 }
744                 if(i==10){
745                         if(draw_y_as_time){
746                                 print_time_scale_string(label_string, 15, (max_y*i/10));
747                         } else {
748                                 g_snprintf(label_string, 15, "%d", max_y*i/10);
749                         }
750 #if GTK_MAJOR_VERSION < 2
751                         lwidth=gdk_string_width(font, label_string);
752                         gdk_draw_string(io->pixmap,
753                                         font,
754                                         io->draw_area->style->black_gc,
755                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
756                                         io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
757                                         label_string);
758 #else
759                         pango_layout_set_text(layout, label_string, -1);
760                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
761                         gdk_draw_layout(io->pixmap,
762                                         io->draw_area->style->black_gc,
763                                         io->pixmap_width-right_x_border+15+label_width-lwidth,
764                                         io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
765                                         layout);
766 #endif
767                 }
768         }
769
770
771
772         /* 
773          * if we have not specified the last_interval via the gui,
774          * then just pick the current end of the capture so that is scrolls
775          * nicely when doing live captures
776          */
777         if(io->last_interval==0xffffffff){
778                 last_interval=io->max_interval;
779         } else {
780                 last_interval=io->last_interval;
781         }
782         
783
784
785
786 /*XXX*/
787         /* plot the x-scale */
788         gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, left_x_border, io->pixmap_height-bottom_y_border+1, io->pixmap_width-right_x_border+1, io->pixmap_height-bottom_y_border+1);
789
790         if((last_interval/io->interval)>draw_width/io->pixels_per_tick+1){
791                 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
792                 first_interval*=io->interval;
793         } else {
794                 first_interval=0;
795         }
796
797         interval_delta=1;
798         delta_multiplier=5;
799         while(interval_delta<((last_interval-first_interval)/10)){
800                 interval_delta*=delta_multiplier;
801                 if(delta_multiplier==5){
802                         delta_multiplier=2;
803                 } else {
804                         delta_multiplier=5;
805                 }
806         }
807
808         for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-io->interval){
809                 int x, xlen;
810
811                 /* if pixels_per_tick is <5, only draw every 10 ticks */
812                 if((io->pixels_per_tick<10) && (current_interval%(10*io->interval))){
813                         continue;
814                 }
815
816                 if(current_interval%interval_delta){
817                         xlen=5;
818                 } else {
819                         xlen=10;
820                 }
821
822                 x=draw_width+left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
823                 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, 
824                         x-1-io->pixels_per_tick/2,
825                         io->pixmap_height-bottom_y_border+1, 
826                         x-1-io->pixels_per_tick/2,
827                         io->pixmap_height-bottom_y_border+xlen+1);
828
829                 if(xlen==10){
830                         int lwidth;
831                         if(io->interval>=1000){
832                                 g_snprintf(label_string, 15, "%ds", current_interval/1000);
833                         } else if(io->interval>=100){
834                                 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10);
835                         } else if(io->interval>=10){
836                                 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100);
837                         } else {
838                                 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
839                         }
840 #if GTK_MAJOR_VERSION < 2
841                         lwidth=gdk_string_width(font, label_string);
842                         gdk_draw_string(io->pixmap,
843                                         font,
844                                         io->draw_area->style->black_gc,
845                                         x-1-io->pixels_per_tick/2-lwidth/2,
846                                         io->pixmap_height-bottom_y_border+15+label_height,
847                                         label_string);
848 #else
849                         pango_layout_set_text(layout, label_string, -1);
850                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
851                         gdk_draw_layout(io->pixmap,
852                                         io->draw_area->style->black_gc,
853                                         x-1-io->pixels_per_tick/2-lwidth/2,
854                                         io->pixmap_height-bottom_y_border+15,
855                                         layout);
856 #endif
857                 }
858
859         }
860 #if GTK_MAJOR_VERSION >= 2
861         g_object_unref(G_OBJECT(layout));
862 #endif
863
864
865
866         /* 
867          * Loop over all graphs and draw them 
868          */
869         for(i=MAX_GRAPHS-1;i>=0;i--){
870                 guint32 interval;
871                 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
872
873                 if(!io->graphs[i].display){
874                         continue;
875                 }
876
877                 /* initialize prev x/y to the low left corner of the graph */
878                 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval+1)+left_x_border;
879                 prev_y_pos=draw_height-1+top_y_border;
880
881                 for(interval=first_interval+io->interval;interval<=last_interval;interval+=io->interval){
882                         guint32 val;
883
884                         x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval+1)+left_x_border;
885
886                         val=get_it_value(io, i, interval/io->interval);
887                         if(val>max_y){
888                                 y_pos=0;
889                         } else {
890                                 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
891                         }
892
893                         /* dont need to draw anything if the segment
894                          * is entirely above the top of the graph 
895                          */
896                         if( (prev_y_pos==0) && (y_pos==0) ){
897                                 prev_y_pos=y_pos;
898                                 prev_x_pos=x_pos;
899                                 continue;
900                         }
901
902                         switch(io->graphs[i].plot_style){
903                         case PLOT_STYLE_LINE:
904                                 gdk_draw_line(io->pixmap, io->graphs[i].gc, 
905                                         prev_x_pos, prev_y_pos, 
906                                         x_pos, y_pos);
907                                 break;
908                         case PLOT_STYLE_IMPULSE:
909                                 if(val){
910                                         gdk_draw_line(io->pixmap, io->graphs[i].gc, 
911                                                 x_pos, draw_height-1+top_y_border,
912                                                 x_pos, y_pos);
913                                 }
914                                 break;
915                         case PLOT_STYLE_FILLED_BAR:
916                                 if(val){
917                                         gdk_draw_rectangle(io->pixmap,
918                                                 io->graphs[i].gc, TRUE,
919                                                 x_pos-io->pixels_per_tick/2,
920                                                 draw_height-1-(val*draw_height)/max_y+top_y_border,
921                                                 io->pixels_per_tick,
922                                                 (val*draw_height)/max_y);
923                                                 
924                                 }
925                                 break;
926                         }
927
928                         prev_y_pos=y_pos;
929                         prev_x_pos=x_pos;
930                 }
931         }
932
933
934
935         gdk_draw_pixmap(io->draw_area->window,
936                         io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
937                         io->pixmap,
938                         0, 0,
939                         0, 0,
940                         io->pixmap_width, io->pixmap_height);
941
942
943         /* update the scrollbar */
944         io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
945         io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
946         io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
947         if((last_interval-first_interval)*100 < io->max_interval){
948                 io->scrollbar_adjustment->page_size=(gfloat) (io->max_interval/100);
949         } else {
950                 io->scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
951         }
952         io->scrollbar_adjustment->value=last_interval-io->scrollbar_adjustment->page_size;
953         gtk_adjustment_changed(io->scrollbar_adjustment);
954         gtk_adjustment_value_changed(io->scrollbar_adjustment);
955
956 }
957
958 static void
959 io_stat_redraw(io_stat_t *io)
960 {
961         io->needs_redraw=TRUE;
962         io_stat_draw(io);
963 }
964
965 static void
966 gtk_iostat_draw(void *g)
967 {
968         io_stat_graph_t *git=g;
969
970         io_stat_draw(git->io);
971 }
972
973
974 /* ok we get called with both the filter and the field. 
975    make sure the field is part of the filter.
976    (make sure and make sure  just append it)
977    the field MUST be part of the filter or else we wont
978    be able to pick up the field values after the edt tree has been
979    pruned 
980 */
981 static GString *
982 enable_graph(io_stat_graph_t *gio, char *filter, char *field)
983 {
984         char real_filter[260];
985
986         gio->display=TRUE;
987         
988         real_filter[0]=0;
989         if(filter){
990                 /* skip all whitespaces */
991                 while(*filter){
992                         if(*filter==' '){
993                                 filter++;
994                                 continue;
995                         }
996                         if(*filter=='\t'){
997                                 filter++;
998                                 continue;
999                         }
1000                         break;
1001                 }
1002                 if(*filter){
1003                         strncpy(real_filter, filter, 255);
1004                         real_filter[255]=0;
1005                 }
1006         }
1007         if(field){
1008                 /* skip all whitespaces */
1009                 while(*field){
1010                         if(*field==' '){
1011                                 field++;
1012                                 continue;
1013                         }
1014                         if(*field=='\t'){
1015                                 field++;
1016                                 continue;
1017                         }
1018                         break;
1019                 }
1020                 if(*field){
1021                         if(real_filter[0]!=0){
1022                                 strcat(real_filter, " && ");
1023                         }
1024                         strncat(real_filter, field, 259-strlen(real_filter));
1025                         real_filter[259]=0;
1026                 }
1027         }
1028         return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1029             gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
1030 }
1031
1032 static void
1033 disable_graph(io_stat_graph_t *gio)
1034 {
1035         if (gio->display) {
1036                 gio->display=FALSE;
1037                 protect_thread_critical_region();
1038                 remove_tap_listener(gio);
1039                 unprotect_thread_critical_region();
1040                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1041                     FALSE);
1042         }
1043 }
1044
1045 static void
1046 gtk_iostat_init(char *optarg _U_)
1047 {
1048         io_stat_t *io;
1049         int i=0;
1050         static color_t col[MAX_GRAPHS] = {
1051                 {0,     0x0000, 0x0000, 0x0000},
1052                 {0,     0xffff, 0x0000, 0x0000},
1053                 {0,     0x0000, 0xffff, 0x0000},
1054                 {0,     0x0000, 0x0000, 0xffff},
1055                 {0,     0xffff, 0x5000, 0xffff}
1056         };
1057         GString *error_string;
1058
1059         io=g_malloc(sizeof(io_stat_t));
1060         io->needs_redraw=TRUE;
1061         io->interval=1000;
1062         io->window=NULL;
1063         io->draw_area=NULL;
1064         io->pixmap=NULL;
1065         io->scrollbar=NULL;
1066         io->scrollbar_adjustment=NULL;
1067         io->pixmap_width=500;
1068         io->pixmap_height=200;
1069     io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
1070         io->max_y_units=AUTO_MAX_YSCALE;
1071         io->count_type=0;
1072         io->last_interval=0xffffffff;
1073         io->max_interval=0;
1074         io->num_items=0;
1075
1076         for(i=0;i<MAX_GRAPHS;i++){
1077                 io->graphs[i].gc=NULL;
1078                 io->graphs[i].color.pixel=col[i].pixel;
1079                 io->graphs[i].color.red=col[i].red;
1080                 io->graphs[i].color.green=col[i].green;
1081                 io->graphs[i].color.blue=col[i].blue;
1082                 io->graphs[i].display=0;
1083                 io->graphs[i].display_button=NULL;
1084                 io->graphs[i].filter_field=NULL;
1085                 io->graphs[i].advanced_buttons=NULL;
1086                 io->graphs[i].io=io;
1087
1088                 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1089                 io->graphs[i].args->title = NULL;
1090                 io->graphs[i].args->wants_apply_button=TRUE;
1091                 io->graphs[i].args->activate_on_ok=TRUE;
1092
1093                 io->graphs[i].filter_bt=NULL;
1094         }
1095         io_stat_reset(io);
1096
1097         error_string=enable_graph(&io->graphs[0], NULL, NULL);
1098         if(error_string){
1099                 fprintf(stderr, "ethereal: Can't attach io_stat tap: %s\n",
1100                     error_string->str);
1101                 g_string_free(error_string, TRUE);
1102                 io->graphs[0].display=0;
1103                 io->graphs[0].display_button=NULL;
1104                 io->graphs[0].filter_field=NULL;
1105                 io->graphs[0].advanced_buttons=NULL;
1106                 exit(10);
1107         }                       
1108                 
1109         /* build the GUI */
1110         init_io_stat_window(io);
1111
1112         retap_packets(&cfile);
1113         io_stat_redraw(io);
1114 }
1115
1116 static gint
1117 quit(GtkWidget *widget, GdkEventExpose *event _U_)
1118 {
1119         int i;
1120         io_stat_t *io;
1121
1122         io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
1123
1124         for(i=0;i<MAX_GRAPHS;i++){
1125                 if(io->graphs[i].display){
1126                         protect_thread_critical_region();
1127                         remove_tap_listener(&io->graphs[i]);
1128                         unprotect_thread_critical_region();
1129
1130                         free(io->graphs[i].args->title);
1131                         io->graphs[i].args->title=NULL;
1132
1133                         g_free(io->graphs[i].args);
1134                         io->graphs[i].args=NULL;
1135                 }
1136         }
1137         g_free(io);
1138
1139         return TRUE;
1140 }
1141
1142 /* create a new backing pixmap of the appropriate size */
1143 static gint
1144 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1145 {
1146         int i;
1147         io_stat_t *io;
1148
1149         io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
1150         if(!io){
1151                 exit(10);
1152         }
1153
1154         if(io->pixmap){
1155                 gdk_pixmap_unref(io->pixmap);
1156                 io->pixmap=NULL;
1157         }
1158
1159         io->pixmap=gdk_pixmap_new(widget->window,
1160                         widget->allocation.width,
1161                         widget->allocation.height,
1162                         -1);
1163         io->pixmap_width=widget->allocation.width;
1164         io->pixmap_height=widget->allocation.height;
1165
1166         gdk_draw_rectangle(io->pixmap,
1167                         widget->style->white_gc,
1168                         TRUE,
1169                         0, 0,
1170                         widget->allocation.width,
1171                         widget->allocation.height);
1172
1173         /* set up the colors and the GC structs for this pixmap */
1174         for(i=0;i<MAX_GRAPHS;i++){
1175                 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1176 #if GTK_MAJOR_VERSION < 2
1177                 colormap = gtk_widget_get_colormap (widget);
1178                 if (!gdk_color_alloc (colormap, &io->graphs[i].color)){
1179                         g_warning ("Couldn't allocate color");
1180                 }
1181
1182                 gdk_gc_set_foreground(io->graphs[i].gc, &io->graphs[i].color);
1183 #else
1184                 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1185 #endif
1186                 
1187         }
1188
1189         io_stat_redraw(io);
1190         return TRUE;
1191 }
1192
1193 static gint
1194 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1195 {
1196         io_stat_t *io=(io_stat_t *)data;
1197         guint32 mi;
1198
1199         mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1200         if(io->last_interval==mi){
1201                 return TRUE;
1202         }
1203         if( (io->last_interval==0xffffffff)
1204         &&  (mi==io->max_interval) ){
1205                 return TRUE;
1206         }
1207
1208         io->last_interval=(mi/io->interval)*io->interval;
1209         io_stat_redraw(io);
1210
1211         return TRUE;
1212 }
1213
1214 /* redraw the screen from the backing pixmap */
1215 static gint
1216 expose_event(GtkWidget *widget, GdkEventExpose *event)
1217 {
1218         io_stat_t *io;
1219
1220         io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
1221         if(!io){
1222                 exit(10);
1223         }
1224
1225
1226         gdk_draw_pixmap(widget->window,
1227                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1228                         io->pixmap,
1229                         event->area.x, event->area.y,
1230                         event->area.x, event->area.y,
1231                         event->area.width, event->area.height);
1232
1233         return FALSE;
1234 }
1235
1236
1237 static void
1238 create_draw_area(io_stat_t *io, GtkWidget *box)
1239 {
1240         io->draw_area=gtk_drawing_area_new();
1241         SIGNAL_CONNECT(io->draw_area, "destroy", quit, io);
1242         OBJECT_SET_DATA(io->draw_area, "io_stat_t", io);
1243
1244         WIDGET_SET_SIZE(io->draw_area, io->pixmap_width, io->pixmap_height);
1245
1246         /* signals needed to handle backing pixmap */
1247         SIGNAL_CONNECT(io->draw_area, "expose_event", expose_event, NULL);
1248         SIGNAL_CONNECT(io->draw_area, "configure_event", configure_event, io);
1249
1250         gtk_widget_show(io->draw_area);
1251         gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1252
1253         /* create the associated scrollbar */
1254         io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1255         io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1256         gtk_widget_show(io->scrollbar);
1257         gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1258         SIGNAL_CONNECT(io->scrollbar_adjustment, "value_changed", scrollbar_changed, io);
1259 }
1260
1261
1262 static void
1263 tick_interval_select(GtkWidget *item, gpointer key)
1264 {
1265         int val;
1266         io_stat_t *io;
1267
1268         io=(io_stat_t *)key;
1269         val=(int)OBJECT_GET_DATA(item, "tick_interval");
1270
1271         io->interval=val;
1272         retap_packets(&cfile);
1273         io_stat_redraw(io);
1274 }
1275
1276 static void
1277 pixels_per_tick_select(GtkWidget *item, gpointer key)
1278 {
1279         int val;
1280         io_stat_t *io;
1281
1282         io=(io_stat_t *)key;
1283         val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1284         io->pixels_per_tick=val;
1285         io_stat_redraw(io);
1286 }
1287
1288 static void
1289 plot_style_select(GtkWidget *item, gpointer key)
1290 {
1291         int val;
1292         io_stat_graph_t *ppt;
1293
1294         ppt=(io_stat_graph_t *)key;
1295         val=(int)OBJECT_GET_DATA(item, "plot_style");
1296
1297         ppt->plot_style=val;
1298
1299         io_stat_redraw(ppt->io);
1300 }
1301
1302 static void 
1303 create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
1304 {
1305         char str[5];
1306         GtkWidget *menu_item;
1307         int i;
1308
1309         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1310                 g_snprintf(str, 5, "%d", pixels_per_tick[i]);
1311                 menu_item=gtk_menu_item_new_with_label(str);
1312
1313                 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
1314                                 pixels_per_tick[i]);
1315                 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, io);
1316                 gtk_widget_show(menu_item);
1317                 gtk_menu_append(GTK_MENU(menu), menu_item);
1318         }
1319         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1320         return;
1321 }
1322
1323
1324 static void
1325 yscale_select(GtkWidget *item, gpointer key)
1326 {
1327         int val;
1328         io_stat_t *io;
1329
1330         io=(io_stat_t *)key;
1331         val=(int)OBJECT_GET_DATA(item, "yscale_max");
1332
1333         io->max_y_units=val;
1334         io_stat_redraw(io);
1335 }
1336
1337 static void 
1338 create_tick_interval_menu_items(io_stat_t *io, GtkWidget *menu)
1339 {
1340         char str[15];
1341         GtkWidget *menu_item;
1342         int i;
1343
1344         for(i=0;i<MAX_TICK_VALUES;i++){
1345                 if(tick_interval_values[i]>=1000){
1346                         g_snprintf(str, 15, "%d sec", tick_interval_values[i]/1000);
1347                 } else if(tick_interval_values[i]>=100){
1348                         g_snprintf(str, 15, "0.%1d sec", (tick_interval_values[i]/100)%10);
1349                 } else if(tick_interval_values[i]>=10){
1350                         g_snprintf(str, 15, "0.%02d sec", (tick_interval_values[i]/10)%10);
1351                 } else {
1352                         g_snprintf(str, 15, "0.%03d sec", (tick_interval_values[i])%10);
1353                 }
1354
1355                 menu_item=gtk_menu_item_new_with_label(str);
1356                 OBJECT_SET_DATA(menu_item, "tick_interval",
1357                                 tick_interval_values[i]);
1358                 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)io);
1359                 gtk_widget_show(menu_item);
1360                 gtk_menu_append(GTK_MENU(menu), menu_item);
1361         }
1362         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1363         return;
1364 }
1365
1366 static void 
1367 create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
1368 {
1369         char str[15];
1370         GtkWidget *menu_item;
1371         int i;
1372
1373         for(i=0;i<MAX_YSCALE;i++){
1374                 if(yscale_max[i]==AUTO_MAX_YSCALE){
1375                         strcpy(str,"Auto");
1376                 } else {
1377                         g_snprintf(str, 15, "%d", yscale_max[i]);
1378                 }
1379                 menu_item=gtk_menu_item_new_with_label(str);
1380                 OBJECT_SET_DATA(menu_item, "yscale_max", yscale_max[i]);
1381                 SIGNAL_CONNECT(menu_item, "activate", yscale_select, io);
1382                 gtk_widget_show(menu_item);
1383                 gtk_menu_append(GTK_MENU(menu), menu_item);
1384         }
1385         return;
1386 }
1387
1388 static void
1389 count_type_select(GtkWidget *item, gpointer key)
1390 {
1391         int val;
1392         io_stat_t *io;
1393
1394         io=(io_stat_t *)key;
1395         val=(int)OBJECT_GET_DATA(item, "count_type");
1396
1397         io->count_type=val;
1398
1399         if(io->count_type==COUNT_TYPE_ADVANCED){
1400                 int i;
1401                 for(i=0;i<MAX_GRAPHS;i++){
1402                         disable_graph(&io->graphs[i]);
1403                         gtk_widget_show(io->graphs[i].advanced_buttons);
1404 /* redraw the entire window so the unhidden widgets show up, hopefully */
1405 {GdkRectangle update_rect;
1406 update_rect.x=0;
1407 update_rect.y=0;
1408 update_rect.width=io->window->allocation.width;
1409 update_rect.height=io->window->allocation.height;
1410 gtk_widget_draw(io->window, &update_rect);
1411 }
1412                 }
1413         } else {
1414                 int i;
1415                 for(i=0;i<MAX_GRAPHS;i++){
1416                         gtk_widget_hide(io->graphs[i].advanced_buttons);
1417                 }
1418         }
1419
1420         io_stat_redraw(io);
1421 }
1422
1423 static void 
1424 create_frames_or_bytes_menu_items(io_stat_t *io, GtkWidget *menu)
1425 {
1426         GtkWidget *menu_item;
1427         int i;
1428
1429         for(i=0;i<MAX_COUNT_TYPES;i++){
1430                 menu_item=gtk_menu_item_new_with_label(count_type_names[i]);
1431                 OBJECT_SET_DATA(menu_item, "count_type", i);
1432                 SIGNAL_CONNECT(menu_item, "activate", count_type_select, io);
1433                 gtk_widget_show(menu_item);
1434                 gtk_menu_append(GTK_MENU(menu), menu_item);
1435         }
1436         return;
1437 }
1438
1439 static void
1440 create_ctrl_menu(io_stat_t *io, GtkWidget *box, char *name, void (*func)(io_stat_t *io, GtkWidget *menu))
1441 {
1442         GtkWidget *hbox;
1443         GtkWidget *label;
1444         GtkWidget *option_menu;
1445         GtkWidget *menu;
1446
1447         hbox=gtk_hbox_new(FALSE, 0);
1448         gtk_container_add(GTK_CONTAINER(box), hbox);
1449         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1450         gtk_widget_show(hbox);
1451
1452         label=gtk_label_new(name);
1453         gtk_widget_show(label);
1454         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1455
1456         option_menu=gtk_option_menu_new();
1457         menu=gtk_menu_new();
1458         (*func)(io, menu);
1459         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1460         gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1461         gtk_widget_show(option_menu);
1462 }
1463
1464 static void
1465 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1466 {
1467     GtkWidget *frame_vbox;
1468     GtkWidget *frame;
1469         GtkWidget *vbox;
1470
1471         frame_vbox=gtk_vbox_new(FALSE, 0);
1472         gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1473         gtk_widget_show(frame_vbox);
1474
1475     frame = gtk_frame_new("X Axis");
1476         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1477         gtk_widget_show(frame);
1478
1479         vbox=gtk_vbox_new(FALSE, 0);
1480         gtk_container_add(GTK_CONTAINER(frame), vbox);
1481     gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1482         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1483         gtk_widget_show(vbox);
1484
1485         create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1486         create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1487
1488     frame = gtk_frame_new("Y Axis");
1489         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1490         gtk_widget_show(frame);
1491
1492         vbox=gtk_vbox_new(FALSE, 0);
1493         gtk_container_add(GTK_CONTAINER(frame), vbox);
1494     gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1495         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1496         gtk_widget_show(vbox);
1497
1498     create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1499         create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1500
1501         return;
1502 }
1503
1504
1505 static gint
1506 filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
1507 {
1508         char *filter;
1509         char *field;
1510         header_field_info *hfi;
1511         dfilter_t *dfilter;
1512
1513         field=(char *)gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1514
1515         /* this graph is not active, just update display and redraw */
1516         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1517                 disable_graph(gio);
1518                 io_stat_redraw(gio->io);
1519                 return 0;
1520         }
1521
1522         /* first check if the field string is valid */
1523         if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1524                 /* warn and bail out if there was no field specified */
1525                 if(field==NULL || field[0]==0){
1526                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You did not specify a field name.");
1527                         disable_graph(gio);
1528                         io_stat_redraw(gio->io);
1529                         return 0;
1530                 }
1531                 /* warn and bail out if the field could not be found */
1532                 hfi=proto_registrar_get_byname(field);
1533                 if(hfi==NULL){
1534                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "'%s' is not a valid field name.", field);
1535                         disable_graph(gio);
1536                         io_stat_redraw(gio->io);
1537                         return 0;
1538                 }
1539                 gio->hf_index=hfi->id;
1540                 /* check that the type is compatible */
1541                 switch(hfi->type){
1542                 case FT_UINT8:
1543                 case FT_UINT16:
1544                 case FT_UINT24:
1545                 case FT_UINT32:
1546                 case FT_INT8:
1547                 case FT_INT16:
1548                 case FT_INT24:
1549                 case FT_INT32:
1550                         /* these values support all calculations except LOAD */
1551                         switch(gio->calc_type){
1552                         case CALC_TYPE_LOAD:
1553                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1554                                     "LOAD(*) is only supported for relative-time fields.");
1555                                 disable_graph(gio);
1556                                 io_stat_redraw(gio->io);
1557                                 return 0;
1558                         }
1559                         /* these types support all calculations */
1560                         break;
1561                 case FT_RELATIVE_TIME:
1562                         /* this type only supports COUNT, MAX, MIN, AVG */
1563                         switch(gio->calc_type){
1564                         case CALC_TYPE_COUNT:
1565                         case CALC_TYPE_MAX:
1566                         case CALC_TYPE_MIN:
1567                         case CALC_TYPE_AVG:
1568                         case CALC_TYPE_LOAD:
1569                                 break;
1570                         default:
1571                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1572                                     "%s is a relative-time field, so %s calculations are not supported on it.",
1573                                     field,
1574                                     calc_type_names[gio->calc_type]);
1575                                 disable_graph(gio);
1576                                 io_stat_redraw(gio->io);
1577                                 return 0;
1578                         }
1579                         break;
1580                 case FT_UINT64:
1581                 case FT_INT64:
1582                         /*
1583                          * XXX - support this if gint64/guint64 are
1584                          * available?
1585                          */
1586                         if(gio->calc_type!=CALC_TYPE_COUNT){
1587                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1588                                     "%s is a 64-bit integer, so %s calculations are not supported on it.",
1589                                     field,
1590                                     calc_type_names[gio->calc_type]);
1591                                 disable_graph(gio);
1592                                 io_stat_redraw(gio->io);
1593                                 return 0;
1594                         }
1595                         break;
1596                 default:
1597                         /*
1598                          * XXX - support all operations on floating-point
1599                          * numbers?
1600                          */
1601                         if(gio->calc_type!=CALC_TYPE_COUNT){
1602                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1603                                     "%s doesn't have integral values, so %s calculations are not supported on it.",
1604                                     field,
1605                                     calc_type_names[gio->calc_type]);
1606                                 disable_graph(gio);
1607                                 io_stat_redraw(gio->io);
1608                                 return 0;
1609                         }
1610                         break;
1611                 }
1612         }
1613
1614         /* first check if the filter string is valid. */
1615         filter=(char *)gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1616         if(!dfilter_compile(filter, &dfilter)) {
1617                 bad_dfilter_alert_box(filter);
1618                 disable_graph(gio);
1619                 io_stat_redraw(gio->io);
1620                 return 0;
1621         }
1622         if (dfilter != NULL)
1623                 dfilter_free(dfilter);
1624
1625         /* ok, we have a valid filter and the graph is active.
1626            first just try to delete any previous settings and then apply
1627            the new ones.
1628         */
1629         protect_thread_critical_region();
1630         remove_tap_listener(gio);
1631         unprotect_thread_critical_region();
1632         
1633         io_stat_reset(gio->io);
1634         enable_graph(gio, filter, field);
1635         retap_packets(&cfile);
1636         io_stat_redraw(gio->io);
1637
1638         return 0;
1639 }
1640
1641
1642 static void
1643 calc_type_select(GtkWidget *item _U_, gpointer key)
1644 {
1645         io_stat_calc_type_t *ct=(io_stat_calc_type_t *)key;
1646
1647         ct->gio->calc_type=ct->calc_type;
1648
1649         /* disable the graph */
1650         disable_graph(ct->gio);
1651         io_stat_redraw(ct->gio->io);
1652 }
1653
1654
1655 static void 
1656 create_calc_types_menu_items(io_stat_graph_t *gio, GtkWidget *menu)
1657 {
1658         GtkWidget *menu_item;
1659         int i;
1660
1661         for(i=0;i<MAX_CALC_TYPES;i++){
1662                 gio->calc_types[i].gio=gio;
1663                 gio->calc_types[i].calc_type=i;
1664                 menu_item=gtk_menu_item_new_with_label(calc_type_names[i]);
1665                 SIGNAL_CONNECT(menu_item, "activate", calc_type_select, &gio->calc_types[i]);
1666                 gtk_widget_show(menu_item);
1667                 gtk_menu_append(GTK_MENU(menu), menu_item);
1668         }
1669         return;
1670 }
1671
1672
1673 static void
1674 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, char *name, void (*func)(io_stat_graph_t *io, GtkWidget *menu))
1675 {
1676         GtkWidget *hbox;
1677         GtkWidget *label;
1678         GtkWidget *option_menu;
1679         GtkWidget *menu;
1680
1681         hbox=gtk_hbox_new(FALSE, 0);
1682         gtk_container_add(GTK_CONTAINER(box), hbox);
1683         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1684         gtk_widget_show(hbox);
1685
1686         label=gtk_label_new(name);
1687         gtk_widget_show(label);
1688         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1689
1690         option_menu=gtk_option_menu_new();
1691         menu=gtk_menu_new();
1692         (*func)(gio, menu);
1693         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1694         gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1695         gtk_widget_show(option_menu);
1696 }
1697
1698 static void
1699 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1700 {
1701
1702         gio->calc_field=gtk_entry_new_with_max_length(30);
1703         gtk_box_pack_start(GTK_BOX(box), gio->calc_field, FALSE, FALSE, 0);
1704         gtk_widget_show(gio->calc_field);
1705         SIGNAL_CONNECT(gio->calc_field, "activate", filter_callback, gio);
1706 }
1707
1708
1709 static void
1710 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1711 {
1712         GtkWidget *hbox;
1713
1714         hbox=gtk_hbox_new(FALSE, 0);
1715         gio->advanced_buttons=hbox;
1716         gtk_container_add(GTK_CONTAINER(box), hbox);
1717         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1718         gtk_widget_hide(hbox);
1719
1720         gio->calc_type=CALC_TYPE_SUM;
1721         create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1722         create_advanced_field(gio, hbox);
1723 }
1724
1725
1726 static void
1727 filter_button_clicked(GtkWidget *w, gpointer uio)
1728 {
1729         io_stat_graph_t *gio=(io_stat_graph_t *)uio;
1730
1731         display_filter_construct_cb(w, gio->args);
1732         return;
1733 }
1734
1735 static void
1736 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
1737 {
1738         GtkWidget *option_menu;
1739         GtkWidget *menu;
1740         GtkWidget *menu_item;
1741         GtkWidget *hbox;
1742         GtkWidget *label;
1743         char str[256];
1744         int i;
1745
1746         hbox=gtk_hbox_new(FALSE, 3);
1747         gtk_container_add(GTK_CONTAINER(box), hbox);
1748         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1749         gtk_widget_show(hbox);
1750
1751         g_snprintf(str, 256, "Graph %d", num);
1752     gio->display_button=gtk_toggle_button_new_with_label(str);
1753         gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
1754         gtk_widget_show(gio->display_button);
1755         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
1756         SIGNAL_CONNECT(gio->display_button, "toggled", filter_callback, gio);
1757
1758         label=gtk_label_new("Color");
1759         gtk_widget_show(label);
1760         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1761
1762 #if GTK_MAJOR_VERSION < 2
1763     /* setting the color of the display button doesn't work */
1764         rc_style = gtk_rc_style_new ();
1765         rc_style->fg[GTK_STATE_NORMAL] = gio->color;
1766         rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1767         rc_style->fg[GTK_STATE_ACTIVE] = gio->color;
1768         rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1769         rc_style->fg[GTK_STATE_PRELIGHT] = gio->color;
1770         rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1771         rc_style->fg[GTK_STATE_SELECTED] = gio->color;
1772         rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1773         rc_style->fg[GTK_STATE_INSENSITIVE] = gio->color;
1774         rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1775         gtk_widget_modify_style (label, rc_style);
1776         gtk_rc_style_unref (rc_style);
1777 #else
1778         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color); 
1779         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color); 
1780         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color); 
1781         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color); 
1782         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color); 
1783 #endif
1784 /*      gtk_signal_connect(GTK_OBJECT(gio->display_button), "toggled", GTK_SIGNAL_FUNC(filter_callback), gio);*/
1785
1786
1787         /* filter prefs dialog */
1788         gio->filter_bt=BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
1789
1790         g_snprintf(str, 256, "Ethereal: Display Filter  IO-Stat (Filter:%d)", num);
1791         if(gio->args->title){
1792                 free(gio->args->title);
1793         }
1794         gio->args->title=strdup(str);   
1795
1796         SIGNAL_CONNECT(gio->filter_bt, "clicked", filter_button_clicked, gio);
1797         SIGNAL_CONNECT(gio->filter_bt, "destroy", filter_button_destroy_cb, NULL);
1798
1799         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
1800         gtk_widget_show(gio->filter_bt);
1801
1802         gio->filter_field=gtk_entry_new_with_max_length(256);
1803
1804         /* filter prefs dialog */
1805         OBJECT_SET_DATA(gio->filter_bt, E_FILT_TE_PTR_KEY, gio->filter_field);
1806         /* filter prefs dialog */
1807
1808         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, FALSE, FALSE, 0);
1809         gtk_widget_show(gio->filter_field);
1810         SIGNAL_CONNECT(gio->filter_field, "activate", filter_callback, gio);
1811     SIGNAL_CONNECT(gio->filter_field, "changed", filter_te_syntax_check_cb, NULL);
1812
1813         create_advanced_box(gio, hbox);
1814
1815
1816         /*
1817          * create PlotStyle menu
1818          */
1819         g_snprintf(str, 256, " Style:");
1820         label=gtk_label_new(str);
1821         gtk_widget_show(label);
1822         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1823
1824         option_menu=gtk_option_menu_new();
1825         menu=gtk_menu_new();
1826         for(i=0;i<MAX_PLOT_STYLES;i++){
1827                 menu_item=gtk_menu_item_new_with_label(plot_style_name[i]);
1828                 OBJECT_SET_DATA(menu_item, "plot_style", i);
1829                 SIGNAL_CONNECT(menu_item, "activate", plot_style_select, &gio->io->graphs[num-1]);
1830                 gtk_widget_show(menu_item);
1831                 gtk_menu_append(GTK_MENU(menu), menu_item);
1832         }
1833         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PLOT_STYLE);
1834
1835         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1836         gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1837         gtk_widget_show(option_menu);
1838
1839
1840         return;
1841 }
1842
1843 static void
1844 create_filter_area(io_stat_t *io, GtkWidget *box)
1845 {
1846         GtkWidget *frame;
1847         GtkWidget *vbox;
1848         int i;
1849
1850     frame=gtk_frame_new("Graphs");
1851         gtk_container_add(GTK_CONTAINER(box), frame);
1852         gtk_widget_show(frame);
1853
1854         vbox=gtk_vbox_new(FALSE, 1);
1855         gtk_container_add(GTK_CONTAINER(frame), vbox);
1856     gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1857         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1858         gtk_widget_show(vbox);
1859
1860         for(i=0;i<MAX_GRAPHS;i++){
1861                 create_filter_box(&io->graphs[i], vbox, i+1);
1862         }
1863
1864         return;
1865 }
1866
1867
1868 static void 
1869 init_io_stat_window(io_stat_t *io)
1870 {
1871         GtkWidget *vbox;
1872         GtkWidget *hbox;
1873     GtkWidget *bt_close;
1874
1875         /* create the main window */
1876         io->window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1877
1878         vbox=gtk_vbox_new(FALSE, 0);
1879         gtk_container_add(GTK_CONTAINER(io->window), vbox);
1880         gtk_widget_show(vbox);
1881
1882         create_draw_area(io, vbox);
1883
1884         hbox=gtk_hbox_new(FALSE, 3);
1885         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1886     gtk_container_border_width(GTK_CONTAINER(hbox), 3);
1887         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1888         gtk_widget_show(hbox);
1889
1890         create_filter_area(io, hbox);
1891         create_ctrl_area(io, hbox);
1892
1893         io_stat_set_title(io);
1894
1895     hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1896         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1897     gtk_widget_show(hbox);
1898
1899     bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
1900     window_set_cancel_button(io->window, bt_close, window_cancel_button_cb);
1901
1902     SIGNAL_CONNECT(io->window, "delete_event", window_delete_event_cb, NULL);
1903
1904     gtk_widget_show(io->window);
1905     window_present(io->window);
1906 }
1907
1908
1909 static void 
1910 gtk_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
1911 {
1912         gtk_iostat_init(NULL);
1913 }
1914
1915
1916
1917
1918 void
1919 register_tap_listener_gtk_iostat(void)
1920 {
1921         register_ethereal_tap("io,stat", gtk_iostat_init);
1922
1923         register_tap_menu_item("_IO Graphs", REGISTER_TAP_GROUP_GENERIC,
1924         gtk_iostat_cb, NULL, NULL, NULL);
1925 }