diff options
Diffstat (limited to 'ui/gtk/io_stat.c')
-rw-r--r-- | ui/gtk/io_stat.c | 2431 |
1 files changed, 2431 insertions, 0 deletions
diff --git a/ui/gtk/io_stat.c b/ui/gtk/io_stat.c new file mode 100644 index 0000000000..a19e1f75d6 --- /dev/null +++ b/ui/gtk/io_stat.c @@ -0,0 +1,2431 @@ +/* io_stat.c + * io_stat 2002 Ronnie Sahlberg + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <ctype.h> + +#include <gtk/gtk.h> + +#include <epan/epan_dissect.h> +#include <epan/packet_info.h> +#include <epan/stat_cmd_args.h> +#include <epan/tap.h> +#include <epan/strutil.h> + +#include "../stat_menu.h" +#include "../alert_box.h" +#include "../simple_dialog.h" + +#include "ui/gtk/gtkglobals.h" +#include "ui/gtk/gui_utils.h" +#include "ui/gtk/gui_stat_menu.h" +#include "ui/gtk/stock_icons.h" +#include "ui/gtk/dlg_utils.h" +#include "ui/gtk/filter_dlg.h" +#include "ui/gtk/help_dlg.h" +#include "ui/gtk/pixmap_save.h" +#include "ui/gtk/main.h" +#include "ui/gtk/filter_autocomplete.h" + +#include "ui/gtk/old-gtk-compat.h" + +#define MAX_GRAPHS 5 + +#define MAX_YSCALE 28 +#define LOGARITHMIC_YSCALE 0 +#define AUTO_MAX_YSCALE 1 +#define DEFAULT_YSCALE_INDEX 1 +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}; + +#define NO_FILTER_ORDER 0 +#define MAX_MOVING_AVERAGE_ORDER 10 +static guint32 moving_average_orders[MAX_MOVING_AVERAGE_ORDER] = {NO_FILTER_ORDER, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; +#define NO_FILTER 0 +#define MOVING_AVERAGE_FILTER 1 + +#define MAX_PIXELS_PER_TICK 4 +#define DEFAULT_PIXELS_PER_TICK_INDEX 2 +static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10}; + + +#define DEFAULT_PLOT_STYLE 0 +#define PLOT_STYLE_LINE 0 +#define PLOT_STYLE_IMPULSE 1 +#define PLOT_STYLE_FILLED_BAR 2 +#define PLOT_STYLE_DOT 3 +#define MAX_PLOT_STYLES 4 +static const char *plot_style_name[MAX_PLOT_STYLES] = { + "Line", + "Impulse", + "FBar", + "Dot", +}; + +#define DEFAULT_COUNT_TYPE 0 +#define COUNT_TYPE_FRAMES 0 +#define COUNT_TYPE_BYTES 1 +#define COUNT_TYPE_BITS 2 +#define COUNT_TYPE_ADVANCED 3 +#define MAX_COUNT_TYPES 4 +static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."}; + +/* unit is in ms */ +#define MAX_TICK_VALUES 7 +#define DEFAULT_TICK_VALUE_INDEX 3 +static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 }; + +#define CALC_TYPE_SUM 0 +#define CALC_TYPE_COUNT 1 +#define CALC_TYPE_MAX 2 +#define CALC_TYPE_MIN 3 +#define CALC_TYPE_AVG 4 +#define CALC_TYPE_LOAD 5 +#define MAX_CALC_TYPES 6 +#define DEFAULT_CALC_TYPE 0 +static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"}; + + +typedef struct _io_stat_calc_type_t { + struct _io_stat_graph_t *gio; + int calc_type; +} io_stat_calc_type_t; + +#define NUM_IO_ITEMS 100000 +typedef struct _io_item_t { + guint32 frames; /* always calculated, will hold number of frames*/ + guint32 bytes; /* always calculated, will hold number of bytes*/ + guint32 fields; + gint32 int_max; + gint32 int_min; + gint32 int_tot; + gfloat float_max; + gfloat float_min; + gfloat float_tot; + gdouble double_max; + gdouble double_min; + gdouble double_tot; + nstime_t time_max; + nstime_t time_min; + nstime_t time_tot; +} io_item_t; + +typedef struct _io_stat_graph_t { + struct _io_stat_t *io; + io_item_t items[NUM_IO_ITEMS]; + int plot_style; + gboolean display; + GtkWidget *display_button; + GtkWidget *filter_field; + GtkWidget *advanced_buttons; + int calc_type; + int hf_index; + GtkWidget *calc_field; + GdkColor color; +#if GTK_CHECK_VERSION(3,0,0) + GdkRGBA rgba_color; +#endif + construct_args_t *args; + GtkWidget *filter_bt; +} io_stat_graph_t; + + +typedef struct _io_stat_t { + gboolean needs_redraw; + gint32 interval; /* measurement interval in ms */ + guint32 last_interval; + guint32 max_interval; /* XXX max_interval and num_items are redundant */ + guint32 num_items; + guint32 left_x_border; + guint32 right_x_border; + gboolean view_as_time; + nstime_t start_time; + + struct _io_stat_graph_t graphs[MAX_GRAPHS]; + GtkWidget *window; + GtkWidget *draw_area; +#if GTK_CHECK_VERSION(2,22,0) + cairo_surface_t *surface; +#else + GdkPixmap *pixmap; +#endif + GtkAdjustment *scrollbar_adjustment; + GtkWidget *scrollbar; + guint first_frame_num[NUM_IO_ITEMS]; + guint last_frame_num; + int pixmap_width; + int pixmap_height; + int pixels_per_tick; + int max_y_units; + int count_type; + + guint32 filter_order; + int filter_type; +} io_stat_t; + + +static void init_io_stat_window(io_stat_t *io); +static void filter_callback(GtkWidget *widget _U_, gpointer user_data); + +static void +io_stat_set_title(io_stat_t *io) +{ + char *title; + + if(!io->window){ + return; + } + title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile)); + gtk_window_set_title(GTK_WINDOW(io->window), title); + g_free(title); +} + +static void +io_stat_reset(io_stat_t *io) +{ + int i, j; + + io->needs_redraw=TRUE; + for(i=0;i<MAX_GRAPHS;i++){ + for(j=0;j<NUM_IO_ITEMS;j++){ + io_item_t *ioi; + ioi=&io->graphs[i].items[j]; + + ioi->frames=0; + ioi->bytes=0; + ioi->fields=0; + ioi->int_max=0; + ioi->int_min=0; + ioi->int_tot=0; + ioi->float_max=0; + ioi->float_min=0; + ioi->float_tot=0; + ioi->double_max=0; + ioi->double_min=0; + ioi->double_tot=0; + nstime_set_zero(&ioi->time_max); + nstime_set_zero(&ioi->time_min); + nstime_set_zero(&ioi->time_tot); + } + } + io->last_interval=0xffffffff; + io->max_interval=0; + io->num_items=0; + io->start_time.secs=0; + io->start_time.nsecs=0; + for(j=0;j<NUM_IO_ITEMS;j++) { + io->first_frame_num[j]=0; + } + io->last_frame_num=0; + + io_stat_set_title(io); +} + +static void +tap_iostat_reset(void *g) +{ + io_stat_graph_t *gio=g; + + io_stat_reset(gio->io); +} + +static gboolean +tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_) +{ + io_stat_graph_t *git=g; + io_item_t *it; + nstime_t time_delta; + int idx; + + /* we sometimes get called when git is disabled. + this is a bug since the tap listener should be removed first */ + if(!git->display){ + return FALSE; + } + + git->io->needs_redraw=TRUE; + + /* + * Find which interval this is supposed to go in and store the + * interval index as idx + */ + time_delta=pinfo->fd->rel_ts; + if(time_delta.nsecs<0){ + time_delta.secs--; + time_delta.nsecs+=1000000000; + } + if(time_delta.secs<0){ + return FALSE; + } + idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval); + + /* some sanity checks */ + if((idx<0)||(idx>=NUM_IO_ITEMS)){ + git->io->num_items = NUM_IO_ITEMS-1; + return FALSE; + } + + /* update num_items */ + if((guint32)idx > git->io->num_items){ + git->io->num_items=idx; + git->io->max_interval=(idx+1)*git->io->interval; + } + + /* set start time */ + if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) { + nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts); + } + + /* set first and last frame num in current interval */ + if (git->io->first_frame_num[idx] == 0) { + git->io->first_frame_num[idx]=pinfo->fd->num; + } + git->io->last_frame_num=pinfo->fd->num; + + /* + * Find the appropriate io_item_t structure + */ + it=&git->items[idx]; + + + /* + * For ADVANCED mode we need to keep track of some more stuff + * than just frame and byte counts + */ + if(git->io->count_type==COUNT_TYPE_ADVANCED){ + GPtrArray *gp; + guint i; + + gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index); + if(!gp){ + return FALSE; + } + + /* update the appropriate counters, make sure that if + * fields==0 then this is the first seen value so + * set any min/max values accordingly + */ + for(i=0;i<gp->len;i++){ + int new_int; + float new_float; + double new_double; + nstime_t *new_time; + + switch(proto_registrar_get_ftype(git->hf_index)){ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value); + + if((new_int>it->int_max)||(it->fields==0)){ + it->int_max=new_int; + } + if((new_int<it->int_min)||(it->fields==0)){ + it->int_min=new_int; + } + it->int_tot+=new_int; + it->fields++; + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value); + if((new_int>it->int_max)||(it->fields==0)){ + it->int_max=new_int; + } + if((new_int<it->int_min)||(it->fields==0)){ + it->int_min=new_int; + } + it->int_tot+=new_int; + it->fields++; + break; + case FT_FLOAT: + new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value); + if((new_float>it->float_max)||(it->fields==0)){ + it->float_max=new_float; + } + if((new_float<it->float_min)||(it->fields==0)){ + it->float_min=new_float; + } + it->float_tot+=new_float; + it->fields++; + break; + case FT_DOUBLE: + new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value); + if((new_double>it->double_max)||(it->fields==0)){ + it->double_max=new_double; + } + if((new_double<it->double_min)||(it->fields==0)){ + it->double_min=new_double; + } + it->double_tot+=new_double; + it->fields++; + break; + case FT_RELATIVE_TIME: + new_time=fvalue_get(&((field_info *)gp->pdata[0])->value); + + switch(git->calc_type){ + guint64 t, pt; /* time in us */ + int j; + case CALC_TYPE_LOAD: + /* it is a LOAD calculation of a relative time field. + * add the time this call spanned to each + * interval it spanned according to its contribution + * to that interval. + */ + t=new_time->secs; + t=t*1000000+new_time->nsecs/1000; + j=idx; + /* handle current interval */ + pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000; + pt=pt%(git->io->interval*1000); + if(pt>t){ + pt=t; + } + while(t){ + git->items[j].time_tot.nsecs+=(int) (pt*1000); + if(git->items[j].time_tot.nsecs>1000000000){ + git->items[j].time_tot.secs++; + git->items[j].time_tot.nsecs-=1000000000; + } + + if(j==0){ + break; + } + j--; + t-=pt; + if(t > (guint32) (git->io->interval*1000)){ + pt=git->io->interval*1000; + } else { + pt=t; + } + } + break; + default: + if( (new_time->secs>it->time_max.secs) + ||( (new_time->secs==it->time_max.secs) + &&(new_time->nsecs>it->time_max.nsecs)) + ||(it->fields==0)){ + it->time_max=*new_time; + } + if( (new_time->secs<it->time_min.secs) + ||( (new_time->secs==it->time_min.secs) + &&(new_time->nsecs<it->time_min.nsecs)) + ||(it->fields==0)){ + it->time_min=*new_time; + } + nstime_add(&it->time_tot, new_time); + it->fields++; + } + } + } + } + + it->frames++; + it->bytes+=pinfo->fd->pkt_len; + + return TRUE; +} + +static guint +get_frame_num(io_stat_t *io, guint32 idx, gboolean first) +{ + guint i, frame_num=0; + + if (idx>io->num_items) { + return 0; + } + + if (first) { + frame_num=io->first_frame_num[idx]; + } + + if (frame_num==0) { + /* + * If first frame not found we select the last + * frame in the previous interval + * + * If selecting the last frame we select the frame + * before the first frame in the next interval + */ + for(i=idx+1;i<=io->num_items;i++) { + frame_num=io->first_frame_num[i]; + if (frame_num != 0) { + return frame_num-1; + } + } + + /* + * If not found we select the last frame + */ + frame_num=io->last_frame_num; + } + + return frame_num; +} + +static guint32 +get_it_value(io_stat_t *io, int graph_id, int idx) +{ + double value=0; + int adv_type; + io_item_t *it; + + it=&io->graphs[graph_id].items[idx]; + + switch(io->count_type){ + case COUNT_TYPE_FRAMES: + return it->frames; + case COUNT_TYPE_BYTES: + return it->bytes; + case COUNT_TYPE_BITS: + return (it->bytes * 8); + } + + + adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index); + switch(adv_type){ + case FT_NONE: + switch(io->graphs[graph_id].calc_type){ + case CALC_TYPE_COUNT: + value=it->frames; + break; + default: + break; + } + break; + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + switch(io->graphs[graph_id].calc_type){ + case CALC_TYPE_SUM: + value=it->int_tot; + break; + case CALC_TYPE_COUNT: + value=it->frames; + break; + case CALC_TYPE_MAX: + value=it->int_max; + break; + case CALC_TYPE_MIN: + value=it->int_min; + break; + case CALC_TYPE_AVG: + if(it->fields){ + value=it->int_tot/it->fields; + } else { + value=0; + } + break; + default: + break; + } + break; + case FT_FLOAT: + switch(io->graphs[graph_id].calc_type){ + case CALC_TYPE_SUM: + value=it->float_tot; + break; + case CALC_TYPE_COUNT: + value=it->frames; + break; + case CALC_TYPE_MAX: + value=it->float_max; + break; + case CALC_TYPE_MIN: + value=it->float_min; + break; + case CALC_TYPE_AVG: + if(it->fields){ + value=it->float_tot/it->fields; + } else { + value=0; + } + break; + default: + break; + } + break; + case FT_DOUBLE: + switch(io->graphs[graph_id].calc_type){ + case CALC_TYPE_SUM: + value=it->double_tot; + break; + case CALC_TYPE_COUNT: + value=it->frames; + break; + case CALC_TYPE_MAX: + value=it->double_max; + break; + case CALC_TYPE_MIN: + value=it->double_min; + break; + case CALC_TYPE_AVG: + if(it->fields){ + value=it->double_tot/it->fields; + } else { + value=0; + } + break; + default: + break; + } + break; + case FT_RELATIVE_TIME: + switch(io->graphs[graph_id].calc_type){ + case CALC_TYPE_COUNT: + value=it->frames; + break; + case CALC_TYPE_MAX: + value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000); + break; + case CALC_TYPE_MIN: + value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000); + break; + case CALC_TYPE_SUM: + value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000); + break; + case CALC_TYPE_AVG: + if(it->fields){ + guint64 t; /* time in us */ + + t=it->time_tot.secs; + t=t*1000000+it->time_tot.nsecs/1000; + value=(guint32) (t/it->fields); + } else { + value=0; + } + break; + case CALC_TYPE_LOAD: + value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval); + break; + default: + break; + } + break; + default: + break; + } + return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */ +} + +static void +print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag) +{ + if(t_max>=10000000 || (log_flag && t_max>=1000000)){ + g_snprintf(buf, buf_len, "%ds",t/1000000); + } else if(t_max>=1000000){ + g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000); + } else if(t_max>=10000 || (log_flag && t_max>=1000)){ + g_snprintf(buf, buf_len, "%dms",t/1000); + } else if(t_max>=1000){ + g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100); + } else { + g_snprintf(buf, buf_len, "%dus",t); + } +} + +static void +print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io, + gboolean ext) +{ + if (io->view_as_time) { + struct tm *tmp; + time_t sec_val = interval/1000 + io->start_time.secs; + gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000; + + if(nsec_val >= 1000) { + sec_val++; + nsec_val -= 1000; + } + tmp = localtime (&sec_val); + if(io->interval>=1000){ + g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + } else if(io->interval>=100){ + g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100); + } else if(io->interval>=10){ + g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10); + } else { + g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val); + } + } else { + if (!ext) { + g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000); + } else if(io->interval>=60000){ + g_snprintf(buf, buf_len, "%dm", interval/60000); + } else if(io->interval>=1000){ + g_snprintf(buf, buf_len, "%ds", interval/1000); + } else if(io->interval>=100){ + g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10); + } else if(io->interval>=10){ + g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100); + } else { + g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000); + } + } +} + +static void +io_stat_draw(io_stat_t *io) +{ + int i, tics, ystart, ys; + guint32 last_interval, first_interval, interval_delta; + gint32 current_interval; + guint32 top_y_border; + guint32 bottom_y_border; + PangoLayout *layout; + int label_width, label_height; + guint32 draw_width, draw_height; + char label_string[45]; + GtkAllocation widget_alloc; + + /* new variables */ + guint32 num_time_intervals; + guint32 max_value; /* max value of seen data */ + guint32 max_y; /* max value of the Y scale */ + gboolean draw_y_as_time; + cairo_t *cr; + if(!io->needs_redraw){ + return; + } + io->needs_redraw=FALSE; + + /* + * Find the length of the intervals we have data for + * so we know how large arrays we need to malloc() + */ + num_time_intervals=io->num_items+1; + + /* XXX move this check to _packet() */ + if(num_time_intervals>NUM_IO_ITEMS){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out"); + return; + } + + + /* + * find the max value so we can autoscale the y axis + */ + max_value=0; + for(i=0;i<MAX_GRAPHS;i++){ + int idx; + + if(!io->graphs[i].display){ + continue; + } + for(idx=0;(guint32) (idx) < num_time_intervals;idx++){ + guint32 val; + + val=get_it_value(io, i, idx); + + /* keep track of the max value we have encountered */ + if(val>max_value){ + max_value=val; + } + } + } + + + + /* + * Clear out old plot + */ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_source_rgb (cr, 1, 1, 1); + gtk_widget_get_allocation(io->draw_area, &widget_alloc); + cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height); + cairo_fill (cr); + cairo_destroy (cr); + /* + * Calculate the y scale we should use + */ + if(io->max_y_units==AUTO_MAX_YSCALE){ + max_y=yscale_max[MAX_YSCALE-1]; + for(i=MAX_YSCALE-1;i>1;i--){ + if(max_value<yscale_max[i]){ + max_y=yscale_max[i]; + } + } + } else if(io->max_y_units==LOGARITHMIC_YSCALE){ + max_y=1000000000; + for(i=1000000000;i>1;i/=10){ + if(max_value<(guint32)i){ + max_y=i; + } + } + } else { + /* the user had specified an explicit y scale to use */ + max_y=io->max_y_units; + } + + + /* + * If we use ADVANCED and all the graphs are plotting + * either MIN/MAX/AVG of an FT_RELATIVE_TIME field + * then we will do some some special processing for the + * labels for the Y axis below: + * we will append the time unit " s" " ms" or " us" + * and we will present the unit in decimal + */ + draw_y_as_time=FALSE; + if(io->count_type==COUNT_TYPE_ADVANCED){ + draw_y_as_time=TRUE; + for(i=0;i<MAX_GRAPHS;i++){ + int adv_type; + + if(!io->graphs[i].display){ + continue; + } + adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index); + switch(adv_type){ + case FT_RELATIVE_TIME: + switch(io->graphs[i].calc_type){ + case CALC_TYPE_SUM: + case CALC_TYPE_MAX: + case CALC_TYPE_MIN: + case CALC_TYPE_AVG: + break; + default: + draw_y_as_time=FALSE; + } + break; + default: + draw_y_as_time=FALSE; + } + } + } + + + + /* + * Calculate size of borders surrounding the plot + * The border on the right side needs to be adjusted depending + * on the width of the text labels. For simplicity we assume that the + * top y scale label will be the widest one + */ + if(draw_y_as_time){ + if(io->max_y_units==LOGARITHMIC_YSCALE){ + print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */ + } else { + print_time_scale_string(label_string, 15, max_y, max_y, FALSE); + } + } else { + g_snprintf(label_string, 15, "%d", max_y); + } + layout = gtk_widget_create_pango_layout(io->draw_area, label_string); + pango_layout_get_pixel_size(layout, &label_width, &label_height); + + io->left_x_border=10; + io->right_x_border=label_width+20; + top_y_border=10; + bottom_y_border=label_height+20; + + + /* + * Calculate the size of the drawing area for the actual plot + */ + draw_width=io->pixmap_width-io->right_x_border-io->left_x_border; + draw_height=io->pixmap_height-top_y_border-bottom_y_border; + + + /* + * Add a warning if too many entries + */ + if (num_time_intervals == NUM_IO_ITEMS) { + g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS); + pango_layout_set_text(layout, label_string, -1); + +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2); + pango_cairo_show_layout (cr, layout); + cairo_destroy (cr); + cr = NULL; + } + + /* + * Draw the y axis and labels + * (we always draw the y scale with 11 ticks along the axis) + */ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5); + cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5); + cairo_stroke(cr); + cairo_destroy(cr); + if(io->max_y_units==LOGARITHMIC_YSCALE){ + tics=(int)log10((double)max_y); + ystart=draw_height/10; + ys=-1; + } else { + tics=10; + ystart=ys=0; + } + + for(i=ys;i<=tics;i++){ + int xwidth, lwidth, ypos; + + xwidth=5; + if(io->max_y_units==LOGARITHMIC_YSCALE){ + if(i==ys) { + /* position for the 0 value */ + ypos=io->pixmap_height-bottom_y_border; + } else if(i==tics) { + /* position for the top value, do not draw logarithmic tics above graph */ + ypos=io->pixmap_height-bottom_y_border-draw_height; + } else { + int j; + /* draw the logarithmic tics */ + for(j=2;j<10;j++) { + ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart); + /* draw the tick */ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5); + cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5); + cairo_stroke(cr); + cairo_destroy(cr); + } + ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart; + } + /* all "main" logarithmic lines are slightly longer */ + xwidth=10; + } else { + if(!(i%5)){ + /* first, middle and last tick are slightly longer */ + xwidth=10; + } + ypos=io->pixmap_height-bottom_y_border-draw_height*i/10; + } + /* draw the tick */ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5); + cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5); + cairo_stroke(cr); + cairo_destroy(cr); + /* draw the labels */ + if(xwidth==10) { + guint32 value; + if(io->max_y_units==LOGARITHMIC_YSCALE){ + value=(guint32)(max_y/pow(10,tics-i)); + if(draw_y_as_time){ + print_time_scale_string(label_string, 15, value, value, TRUE); + } else { + g_snprintf(label_string, 15, "%d", value); + } + } else { + value=(max_y/10)*i; + if(draw_y_as_time){ + print_time_scale_string(label_string, 15, value, max_y, FALSE); + } else { + g_snprintf(label_string, 15, "%d", value); + } + } + + pango_layout_set_text(layout, label_string, -1); + pango_layout_get_pixel_size(layout, &lwidth, NULL); + +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2); + pango_cairo_show_layout (cr, layout); + cairo_destroy (cr); + cr = NULL; + + } + } + + /* + * if we have not specified the last_interval via the gui, + * then just pick the current end of the capture so that is scrolls + * nicely when doing live captures + */ + if(io->last_interval==0xffffffff){ + last_interval=io->max_interval; + } else { + last_interval=io->last_interval; + } + + + + +/*XXX*/ + /* plot the x-scale */ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5); + cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5); + cairo_stroke(cr); + cairo_destroy(cr); + if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){ + first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1; + first_interval*=io->interval; + } else { + first_interval=0; + } + + interval_delta=(100/io->pixels_per_tick)*io->interval; + for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){ + int x, xlen; + + /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */ + /* if pixels_per_tick is 5, only draw every 5 ticks */ + if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) || + ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){ + continue; + } + + if(!(current_interval%interval_delta)){ + xlen=10; + } else if(!(current_interval%(interval_delta/2))){ + xlen=8; + } else { + xlen=5; + } + x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick; +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5); + cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5); + cairo_stroke(cr); + cairo_destroy(cr); + if(xlen==10){ + int lwidth, x_pos; + print_interval_string (label_string, 15, current_interval, io, TRUE); + pango_layout_set_text(layout, label_string, -1); + pango_layout_get_pixel_size(layout, &lwidth, NULL); + + if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) { + x_pos=5; + } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) { + x_pos=io->pixmap_width-lwidth-5; + } else { + x_pos=x-1-io->pixels_per_tick/2-lwidth/2; + } +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15); + pango_cairo_show_layout (cr, layout); + cairo_destroy (cr); + cr = NULL; + } + + } + g_object_unref(G_OBJECT(layout)); + + + /* + * Loop over all graphs and draw them + */ + for(i=MAX_GRAPHS-1;i>=0;i--){ + guint64 val; + guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos; + /* Moving average variables */ + guint32 mavg_in_average_count = 0, mavg_left = 0, mavg_right = 0; + guint64 mavg_cumulated = 0; + guint32 mavg_to_remove = 0, mavg_to_add = 0; + + if(!io->graphs[i].display){ + continue; + } + + if(io->filter_type == MOVING_AVERAGE_FILTER){ + /* "Warm-up phase" - calculate average on some data not displayed; + just to make sure average on leftmost and rightmost displayed + values is as reliable as possible + */ + guint32 warmup_interval; + + if(first_interval/io->interval > io->filter_order/2){ + warmup_interval = first_interval/io->interval - io->filter_order/2; + warmup_interval*= io->interval; + } else { + warmup_interval = 0; + } + mavg_to_remove = warmup_interval; + for(;warmup_interval<first_interval;warmup_interval+=io->interval){ + mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval); + mavg_in_average_count++; + mavg_left++; + } + mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval); + mavg_in_average_count++; + for(warmup_interval+=io->interval;warmup_interval<(first_interval+(io->filter_order/2)*io->interval)&&(warmup_interval<=(io->num_items*io->interval));warmup_interval+=io->interval){ + mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval); + mavg_in_average_count++; + mavg_right++; + } + mavg_to_add = warmup_interval; + } + + /* initialize prev x/y to the value of the first interval */ + prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border; + val=get_it_value(io, i, first_interval/io->interval); + if(io->filter_type == MOVING_AVERAGE_FILTER){ + if(mavg_in_average_count>0){ + val = mavg_cumulated/mavg_in_average_count; + } + } + if(val>max_y){ + prev_y_pos=0; + } else if(io->max_y_units==LOGARITHMIC_YSCALE){ + if (val==0) { + prev_y_pos=(guint32)(draw_height-1+top_y_border); + } else { + prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border); + } + } else { + prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border); + } + + for(interval=first_interval;interval<last_interval;interval+=io->interval){ + x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border; + + val=get_it_value(io, i, interval/io->interval); + /* Moving average calculation */ + if(io->filter_type == MOVING_AVERAGE_FILTER){ + if(interval!=first_interval){ + mavg_left++; + if(mavg_left > io->filter_order/2){ + mavg_left--; + mavg_in_average_count--; + mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval); + mavg_to_remove += io->interval; + } + if(mavg_to_add<=io->num_items*io->interval){ + mavg_in_average_count++; + mavg_cumulated += get_it_value(io, i, mavg_to_add/io->interval); + mavg_to_add += io->interval; + }else{ + mavg_right--; + } + } + val = mavg_cumulated / mavg_in_average_count; + } + + if(val>max_y){ + y_pos=0; + } else if(io->max_y_units==LOGARITHMIC_YSCALE){ + if (val==0) { + y_pos=(guint32)(draw_height-1+top_y_border); + } else { + y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border); + } + } else { + y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border); + } + + switch(io->graphs[i].plot_style){ + case PLOT_STYLE_LINE: + /* dont need to draw anything if the segment + * is entirely above the top of the graph + */ + if( (prev_y_pos!=0) || (y_pos!=0) ){ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + gdk_cairo_set_source_color (cr, &io->graphs[i].color); + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5); + cairo_line_to(cr, x_pos+0.5, y_pos+0.5); + cairo_stroke(cr); + cairo_destroy(cr); + } + break; + case PLOT_STYLE_IMPULSE: + if(val){ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + gdk_cairo_set_source_color (cr, &io->graphs[i].color); + cairo_set_line_width (cr, 1.0); + cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5); + cairo_line_to(cr, x_pos+0.5, y_pos+0.5); + cairo_stroke(cr); + cairo_destroy(cr); + } + break; + case PLOT_STYLE_FILLED_BAR: + if(val){ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_rectangle (cr, + x_pos-(gdouble)io->pixels_per_tick/2+0.5, + y_pos+0.5, + io->pixels_per_tick, + draw_height-1+top_y_border-y_pos); + gdk_cairo_set_source_color (cr, &io->graphs[i].color); + cairo_fill (cr); + cairo_destroy (cr); + } + break; + case PLOT_STYLE_DOT: + if(val){ +#if GTK_CHECK_VERSION(2,22,0) + cr = cairo_create (io->surface); +#else + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_arc (cr, + x_pos+0.5, + y_pos+0.5, + (gdouble)io->pixels_per_tick/2, + 0, + 2 * G_PI); + gdk_cairo_set_source_color (cr, &io->graphs[i].color); + cairo_fill (cr); + cairo_destroy (cr); + } + break; + } + + prev_y_pos=y_pos; + prev_x_pos=x_pos; + } + } + + cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area)); + +#if GTK_CHECK_VERSION(2,22,0) + cairo_set_source_surface (cr, io->surface, 0, 0); +#else + gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0); +#endif + cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height); + cairo_fill (cr); + + cairo_destroy (cr); + + /* update the scrollbar */ + if (io->max_interval == 0) { + gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval); + gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10)); + gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval); + } else { + gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval); + gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10)); + gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval)); + } + gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment)); + gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval); + gtk_adjustment_changed(io->scrollbar_adjustment); + gtk_adjustment_value_changed(io->scrollbar_adjustment); + +} + +static void +io_stat_redraw(io_stat_t *io) +{ + io->needs_redraw=TRUE; + io_stat_draw(io); +} + +static void +tap_iostat_draw(void *g) +{ + io_stat_graph_t *git=g; + + io_stat_draw(git->io); +} + +/* ok we get called with both the filter and the field. + make sure the field is part of the filter. + (make sure and make sure just append it) + the field MUST be part of the filter or else we wont + be able to pick up the field values after the edt tree has been + pruned +*/ +static GString * +enable_graph(io_stat_graph_t *gio, const char *filter, const char *field) +{ + char real_filter[262]; + + gio->display=TRUE; + + real_filter[0]=0; + if(filter){ + /* skip all whitespaces */ + while(*filter){ + if(*filter==' '){ + filter++; + continue; + } + if(*filter=='\t'){ + filter++; + continue; + } + break; + } + if(*filter){ + g_snprintf(real_filter, 257, "(%s)", filter); + real_filter[257]=0; + } + } + if(field){ + /* skip all whitespaces */ + while(*field){ + if(*field==' '){ + field++; + continue; + } + if(*field=='\t'){ + field++; + continue; + } + break; + } + if(*field){ + if(real_filter[0]!=0){ + g_strlcat(real_filter, " && ", 262); + } + g_strlcat(real_filter, field, 262); + } + } + return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL, + TL_REQUIRES_PROTO_TREE, + tap_iostat_reset, tap_iostat_packet, tap_iostat_draw); +} + +static void +disable_graph(io_stat_graph_t *gio) +{ + if (gio->display) { + gio->display=FALSE; + protect_thread_critical_region(); + remove_tap_listener(gio); + unprotect_thread_critical_region(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), + FALSE); + } +} + +static void +iostat_init(const char *optarg _U_, void* userdata _U_) +{ + io_stat_t *io; + int i=0; + static GdkColor col[MAX_GRAPHS] = { + {0, 0x0000, 0x0000, 0x0000}, /* Black */ + {0, 0xffff, 0x0000, 0x0000}, /* Red */ + {0, 0x0000, 0xffff, 0x0000}, /* Green */ + {0, 0x0000, 0x0000, 0xffff}, /* Blue */ + {0, 0xffff, 0x5000, 0xffff} /* Light brilliant magenta */ + }; +#if GTK_CHECK_VERSION(3,0,0) + static GdkRGBA rgba_col[MAX_GRAPHS] = { + {0.0, 0.0, 0.0, 1.0}, /* Black */ + {1.0, 0.0, 0.1, 1.0}, /* Red */ + {0.0, 1.0, 0.0, 1.0}, /* Green */ + {0.0, 0.0, 1.0, 1.0}, /* Blue */ + {1.0, 0.314, 1.0, 1.0} /* Light brilliant magenta */ + }; +#endif + GString *error_string; + + io=g_malloc(sizeof(io_stat_t)); + io->needs_redraw=TRUE; + io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX]; + io->window=NULL; + io->draw_area=NULL; +#if GTK_CHECK_VERSION(2,22,0) + io->surface=NULL; +#else + io->pixmap=NULL; +#endif + io->scrollbar=NULL; + io->scrollbar_adjustment=NULL; + io->pixmap_width=500; + io->pixmap_height=200; + io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX]; + io->max_y_units=AUTO_MAX_YSCALE; + io->count_type=0; + io->last_interval=0xffffffff; + io->max_interval=0; + io->num_items=0; + io->left_x_border=0; + io->right_x_border=500; + io->view_as_time=FALSE; + io->start_time.secs=0; + io->start_time.nsecs=0; + + for(i=0;i<MAX_GRAPHS;i++){ + io->graphs[i].color.pixel=col[i].pixel; + io->graphs[i].color.red=col[i].red; + io->graphs[i].color.green=col[i].green; + io->graphs[i].color.blue=col[i].blue; +#if GTK_CHECK_VERSION(3,0,0) + io->graphs[i].rgba_color.red=rgba_col[i].red; + io->graphs[i].rgba_color.green=rgba_col[i].green; + io->graphs[i].rgba_color.blue=rgba_col[i].blue; + io->graphs[i].rgba_color.alpha=rgba_col[i].alpha; +#endif + io->graphs[i].display=0; + io->graphs[i].display_button=NULL; + io->graphs[i].filter_field=NULL; + io->graphs[i].advanced_buttons=NULL; + io->graphs[i].io=io; + + io->graphs[i].args=g_malloc(sizeof(construct_args_t)); + io->graphs[i].args->title = NULL; + io->graphs[i].args->wants_apply_button=TRUE; + io->graphs[i].args->activate_on_ok=TRUE; + io->graphs[i].args->modal_and_transient=FALSE; + + io->graphs[i].filter_bt=NULL; + } + io_stat_reset(io); + + error_string=enable_graph(&io->graphs[0], NULL, NULL); + g_assert((error_string == NULL) && "Can't attach io_stat tap !"); +#if 0 + if(error_string){ + + fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + io->graphs[0].display=0; + io->graphs[0].display_button=NULL; + io->graphs[0].filter_field=NULL; + io->graphs[0].advanced_buttons=NULL; + exit(10); + } +#endif + /* build the GUI */ + init_io_stat_window(io); + + cf_retap_packets(&cfile); + gdk_window_raise(gtk_widget_get_window(io->window)); + io_stat_redraw(io); +} + +static void +draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data) +{ + io_stat_t *io = user_data; + int i; + GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt"); + surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info"); + + g_free(surface_info); + + for(i=0;i<MAX_GRAPHS;i++){ + if(io->graphs[i].display){ + protect_thread_critical_region(); + remove_tap_listener(&io->graphs[i]); + unprotect_thread_critical_region(); + + g_free( (gpointer) (io->graphs[i].args->title) ); + io->graphs[i].args->title=NULL; + + g_free(io->graphs[i].args); + io->graphs[i].args=NULL; + } + } + g_free(io); + + return; +} + +static gboolean +pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data) +{ + io_stat_t *io = user_data; + guint32 draw_width, interval, last_interval; + guint frame_num; + + draw_width=io->pixmap_width-io->right_x_border-io->left_x_border; + + if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) || + (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) { + /* Outside draw area */ + return FALSE; + } + +#if GTK_CHECK_VERSION(2,22,0) + if ((event->button==1 || event->button==3) && io->surface!=NULL) { +#else + if ((event->button==1 || event->button==3) && io->pixmap!=NULL) { +#endif + /* + * Button 1 selects the first package in the interval. + * Button 3 selects the last package in the interval. + */ + if (io->last_interval==0xffffffff) { + last_interval=io->max_interval; + } else { + last_interval=io->last_interval; + } + + interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick); + frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE); + if (frame_num != 0) { + cf_goto_frame(&cfile, frame_num); + } + } + + return TRUE; +} + +/* create a new backing pixmap of the appropriate size */ +static gboolean +draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data) +{ + io_stat_t *io = user_data; + GtkWidget *save_bt; + GtkAllocation widget_alloc; + cairo_t *cr; +#if GTK_CHECK_VERSION(2,22,0) + surface_info_t *surface_info = g_new(surface_info_t, 1); +#endif + +#if GTK_CHECK_VERSION(2,22,0) + if(io->surface){ + cairo_surface_destroy (io->surface); + io->surface=NULL; + } +#else + if(io->pixmap){ + g_object_unref(io->pixmap); + io->pixmap=NULL; + } +#endif + + gtk_widget_get_allocation(widget, &widget_alloc); +#if GTK_CHECK_VERSION(2,22,0) + io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget), + CAIRO_CONTENT_COLOR, + widget_alloc.width, + widget_alloc.height); + +#else + io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget), + widget_alloc.width, + widget_alloc.height, + -1); +#endif + io->pixmap_width=widget_alloc.width; + io->pixmap_height=widget_alloc.height; + + save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt"); +#if GTK_CHECK_VERSION(2,22,0) + surface_info->surface = io->surface; + surface_info->width = widget_alloc.width; + surface_info->height = widget_alloc.height; + g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info); + gtk_widget_set_sensitive(save_bt, TRUE); + + cr = cairo_create (io->surface); +#else + g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap); + gtk_widget_set_sensitive(save_bt, TRUE); + + cr = gdk_cairo_create (io->pixmap); +#endif + cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_fill (cr); + cairo_destroy (cr); + + io_stat_redraw(io); + return TRUE; +} + +static void +scrollbar_changed(GtkWidget *widget _U_, gpointer user_data) +{ + io_stat_t *io = user_data; + guint32 mi; + + mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment)); + if(io->last_interval==mi){ + return; + } + if( (io->last_interval==0xffffffff) + && (mi==io->max_interval) ){ + return; + } + + io->last_interval=(mi/io->interval)*io->interval; + io_stat_redraw(io); + + return; +} +#if GTK_CHECK_VERSION(3,0,0) +static gboolean +draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data) +{ + io_stat_t *io = user_data; + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + cairo_set_source_surface (cr, io->surface, 0, 0); + cairo_rectangle (cr, 0, 0, allocation.width, allocation.width); + cairo_fill (cr); + + return FALSE; +} +#else +/* redraw the screen from the backing pixmap */ +static gboolean +draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + io_stat_t *io = user_data; + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget)); + +#if GTK_CHECK_VERSION(2,22,0) + cairo_set_source_surface (cr, io->surface, 0, 0); +#else + gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0); +#endif + cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height); + cairo_fill (cr); + + cairo_destroy (cr); + + return FALSE; +} +#endif +static void +create_draw_area(io_stat_t *io, GtkWidget *box) +{ + io->draw_area=gtk_drawing_area_new(); + g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io); + + gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height); + + /* signals needed to handle backing pixmap */ +#if GTK_CHECK_VERSION(3,0,0) + g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io); +#else + g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io); +#endif + g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io); + gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK); + g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io); + + gtk_widget_show(io->draw_area); + gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0); + + /* create the associated scrollbar */ + io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0); + io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment); + gtk_widget_show(io->scrollbar); + gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0); + g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io); +} + +static void +tick_interval_select(GtkWidget *item, gpointer user_data) +{ + io_stat_t *io = user_data; + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + io->interval=tick_interval_values[i]; + cf_retap_packets(&cfile); + gdk_window_raise(gtk_widget_get_window(io->window)); + io_stat_redraw(io); +} + +static void +pixels_per_tick_select(GtkWidget *item, gpointer user_data) +{ + io_stat_t *io = user_data; + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + io->pixels_per_tick=pixels_per_tick[i]; + io_stat_redraw(io); +} + +static void +plot_style_select(GtkWidget *item, gpointer user_data) +{ + io_stat_graph_t *ppt = user_data; + int val; + + val=gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + ppt->plot_style=val; + + io_stat_redraw(ppt->io); +} + +static GtkWidget * +create_pixels_per_tick_menu_items(io_stat_t *io) +{ + char str[5]; + GtkWidget *combo_box; + int i; + combo_box = gtk_combo_box_text_new (); + + for(i=0;i<MAX_PIXELS_PER_TICK;i++){ + g_snprintf(str, 5, "%u", pixels_per_tick[i]); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX); + g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io); + + return combo_box; +} + +static void +yscale_select(GtkWidget *item, gpointer user_data) +{ + io_stat_t *io = user_data; + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + io->max_y_units = yscale_max[i]; + io_stat_redraw(io); +} + +static void +filter_select(GtkWidget *item, gpointer user_data) +{ + io_stat_t *io = user_data; + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + if(i==NO_FILTER_ORDER){ + io->filter_type = NO_FILTER; + } else { + io->filter_type = MOVING_AVERAGE_FILTER; + io->filter_order = moving_average_orders[i]; + } + io_stat_redraw(io); +} + +static GtkWidget * +create_tick_interval_menu_items(io_stat_t *io) +{ + GtkWidget *combo_box; + char str[15]; + int i; + + combo_box = gtk_combo_box_text_new (); + + for(i=0;i<MAX_TICK_VALUES;i++){ + if(tick_interval_values[i]>=60000){ + g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000); + } else if(tick_interval_values[i]>=1000){ + g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000); + } else if(tick_interval_values[i]>=100){ + g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10); + } else if(tick_interval_values[i]>=10){ + g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10); + } else { + g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10); + } + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX); + g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io); + + return combo_box; +} + +static GtkWidget * +create_yscale_max_menu_items(io_stat_t *io) +{ + char str[15]; + GtkWidget *combo_box; + int i; + + combo_box = gtk_combo_box_text_new (); + for(i=0;i<MAX_YSCALE;i++){ + if(yscale_max[i]==LOGARITHMIC_YSCALE){ + g_strlcpy(str, "Logarithmic", 15); + } else if(yscale_max[i]==AUTO_MAX_YSCALE){ + g_strlcpy(str, "Auto", 15); + } else { + g_snprintf(str, 15, "%u", yscale_max[i]); + } + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX); + g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io); + return combo_box; +} + +static GtkWidget * +create_filter_menu_items(io_stat_t *io) +{ + char str[15]; + GtkWidget *combo_box; + int i; + + combo_box = gtk_combo_box_text_new (); + + for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){ + if(i==NO_FILTER_ORDER){ + g_strlcpy(str, "No filter", 15); + } else { + g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]); + } + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); + g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io); + return combo_box; +} + +static void +count_type_select(GtkWidget *item, gpointer user_data) +{ + io_stat_t *io = user_data; + static gboolean advanced_visible=FALSE; + int i; + GtkAllocation widget_alloc; + + io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + if(io->count_type==COUNT_TYPE_ADVANCED){ + for(i=0;i<MAX_GRAPHS;i++){ + disable_graph(&io->graphs[i]); + gtk_widget_show(io->graphs[i].advanced_buttons); + /* redraw the entire window so the unhidden widgets show up, hopefully */ + gtk_widget_get_allocation(io->window, &widget_alloc); + gtk_widget_queue_draw_area(io->window, + 0, + 0, + widget_alloc.width, + widget_alloc.height); + } + advanced_visible=TRUE; + io_stat_redraw(io); + } else if (advanced_visible) { + for(i=0;i<MAX_GRAPHS;i++){ + gtk_widget_hide(io->graphs[i].advanced_buttons); + filter_callback(item, &io->graphs[i]); + } + advanced_visible=FALSE; + } else { + io_stat_redraw(io); + } +} + +static GtkWidget * +create_frames_or_bytes_menu_items(io_stat_t *io) +{ + GtkWidget *combo_box; + int i; + + combo_box = gtk_combo_box_text_new (); + + for(i=0;i<MAX_COUNT_TYPES;i++){ + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE); + g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io); + return combo_box; +} + +static void +create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io)) +{ + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo_box; + + hbox=gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(box), hbox); + gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START); + gtk_widget_show(hbox); + + label=gtk_label_new(name); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + combo_box = (*func)(io); + gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0); + gtk_widget_show(combo_box); +} + +static void +view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data) +{ + io_stat_t *io = user_data; + + io->view_as_time = io->view_as_time ? FALSE : TRUE; + + io_stat_redraw(io); +} + +static void +create_ctrl_area(io_stat_t *io, GtkWidget *box) +{ + GtkWidget *frame_vbox; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *view_cb; + + frame_vbox=gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0); + gtk_widget_show(frame_vbox); + + frame = gtk_frame_new("X Axis"); + gtk_container_add(GTK_CONTAINER(frame_vbox), frame); + gtk_widget_show(frame); + + vbox=gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); + gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END); + gtk_widget_show(vbox); + + create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items); + create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items); + + view_cb = gtk_check_button_new_with_mnemonic("_View as time of day"); + gtk_container_add(GTK_CONTAINER(vbox), view_cb); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time); + g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io); + gtk_widget_show(view_cb); + + frame = gtk_frame_new("Y Axis"); + gtk_container_add(GTK_CONTAINER(frame_vbox), frame); + gtk_widget_show(frame); + + vbox=gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); + gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END); + gtk_widget_show(vbox); + + create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items); + create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items); + create_ctrl_menu(io, vbox, "Smooth:", create_filter_menu_items); + + return; +} + +static void +filter_callback(GtkWidget *widget _U_, gpointer user_data) +{ + io_stat_graph_t *gio = user_data; + const char *filter; + const char *field=NULL; + header_field_info *hfi; + dfilter_t *dfilter; + + /* this graph is not active, just update display and redraw */ + if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){ + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + + /* first check if the field string is valid */ + if(gio->io->count_type==COUNT_TYPE_ADVANCED){ + field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field)); + + /* warn and bail out if there was no field specified */ + if(field==NULL || field[0]==0){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name."); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + /* warn and bail out if the field could not be found */ + hfi=proto_registrar_get_byname(field); + if(hfi==NULL){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + gio->hf_index=hfi->id; + /* check that the type is compatible */ + switch(hfi->type){ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_FLOAT: + case FT_DOUBLE: + /* these values support all calculations except LOAD */ + switch(gio->calc_type){ + case CALC_TYPE_LOAD: + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "LOAD(*) is only supported for relative-time fields."); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + /* these types support all calculations */ + break; + case FT_RELATIVE_TIME: + /* this type only supports COUNT, MAX, MIN, AVG */ + switch(gio->calc_type){ + case CALC_TYPE_SUM: + case CALC_TYPE_COUNT: + case CALC_TYPE_MAX: + case CALC_TYPE_MIN: + case CALC_TYPE_AVG: + case CALC_TYPE_LOAD: + break; + default: + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s is a relative-time field, so %s calculations are not supported on it.", + field, + calc_type_names[gio->calc_type]); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + break; + case FT_UINT64: + case FT_INT64: + /* + * XXX - support this if gint64/guint64 are + * available? + */ + if(gio->calc_type!=CALC_TYPE_COUNT){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s is a 64-bit integer, so %s calculations are not supported on it.", + field, + calc_type_names[gio->calc_type]); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + break; + default: + if(gio->calc_type!=CALC_TYPE_COUNT){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s doesn't have integral or float values, so %s calculations are not supported on it.", + field, + calc_type_names[gio->calc_type]); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + break; + } + } + + /* first check if the filter string is valid. */ + filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field)); + if(!dfilter_compile(filter, &dfilter)) { + bad_dfilter_alert_box(filter); + disable_graph(gio); + io_stat_redraw(gio->io); + return; + } + if (dfilter != NULL) + dfilter_free(dfilter); + + /* ok, we have a valid filter and the graph is active. + first just try to delete any previous settings and then apply + the new ones. + */ + protect_thread_critical_region(); + remove_tap_listener(gio); + unprotect_thread_critical_region(); + + io_stat_reset(gio->io); + enable_graph(gio, filter, field); + cf_retap_packets(&cfile); + gdk_window_raise(gtk_widget_get_window(gio->io->window)); + io_stat_redraw(gio->io); + + return; +} + +static void +calc_type_select(GtkWidget *item, gpointer user_data) +{ + io_stat_graph_t *gio = user_data; + + gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item)); + + /* disable the graph */ + disable_graph(gio); + io_stat_redraw(gio->io); +} + +static GtkWidget * +create_calc_types_menu_items(io_stat_graph_t *gio) +{ + GtkWidget *combo_box; + int i; + + combo_box = gtk_combo_box_text_new (); + for(i=0;i<MAX_CALC_TYPES;i++){ + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE); + g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio); + return combo_box; +} + +static void +create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io)) +{ + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo_box; + + hbox=gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(box), hbox); + gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START); + gtk_widget_show(hbox); + + label=gtk_label_new(name); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + combo_box = (*func)(gio); + gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0); + gtk_widget_show(combo_box); +} + +static void +create_advanced_field(io_stat_graph_t *gio, GtkWidget *box) +{ + + gio->calc_field=gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100); + gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0); + gtk_widget_show(gio->calc_field); + g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio); + g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, ""); + g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL); + g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL); + g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL); + g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL); + colorize_filter_te_as_empty(gio->calc_field); +} + +static void +create_advanced_box(io_stat_graph_t *gio, GtkWidget *box) +{ + GtkWidget *hbox; + + hbox=gtk_hbox_new(FALSE, 0); + gio->advanced_buttons=hbox; + gtk_container_add(GTK_CONTAINER(box), hbox); + gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START); + gtk_widget_hide(hbox); + + gio->calc_type=CALC_TYPE_SUM; + create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items); + create_advanced_field(gio, hbox); +} + +static void +filter_button_clicked(GtkWidget *w, gpointer user_data) +{ + io_stat_graph_t *gio = user_data; + + display_filter_construct_cb(w, gio->args); + return; +} + +static void +create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num) +{ + GtkWidget *combo_box; + GtkWidget *hbox; + GtkWidget *label; + char str[256]; + int i; + + hbox=gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(box), hbox); + gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START); + gtk_widget_show(hbox); + + g_snprintf(str, 256, "Graph %d", num); + gio->display_button=gtk_toggle_button_new_with_label(str); + gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0); + gtk_widget_show(gio->display_button); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display); + g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio); + + label=gtk_label_new("Color"); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_override_color(label, GTK_STATE_NORMAL, &gio->rgba_color); + gtk_widget_override_color(label, GTK_STATE_ACTIVE, &gio->rgba_color); + gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &gio->rgba_color); + gtk_widget_override_color(label, GTK_STATE_SELECTED, &gio->rgba_color); + gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &gio->rgba_color); +#else + gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color); + gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color); + gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color); + gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color); + gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color); +#endif +/* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/ + + + /* filter prefs dialog */ + gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY); + + g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num); + g_free( (gpointer) (gio->args->title) ); + gio->args->title=g_strdup(str); + + g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio); + g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL); + + gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0); + gtk_widget_show(gio->filter_bt); + + gio->filter_field=gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256); + /* filter prefs dialog */ + g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field); + /* filter prefs dialog */ + + gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0); + gtk_widget_show(gio->filter_field); + g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio); + g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL); + g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL); + g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL); + g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL); + colorize_filter_te_as_empty(gio->filter_field); + + create_advanced_box(gio, hbox); + + /* + * create PlotStyle menu + */ + g_snprintf(str, 256, " Style:"); + label=gtk_label_new(str); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + combo_box = gtk_combo_box_text_new (); + for(i=0;i<MAX_PLOT_STYLES;i++){ + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE); + g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]); + + gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0); + gtk_widget_show(combo_box); + + return; +} + +static void +create_filter_area(io_stat_t *io, GtkWidget *box) +{ + GtkWidget *frame; + GtkWidget *vbox; + int i; + + frame=gtk_frame_new("Graphs"); + gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); + gtk_widget_show(frame); + + vbox=gtk_vbox_new(FALSE, 1); + gtk_container_add(GTK_CONTAINER(frame), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); + gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START); + gtk_widget_show(vbox); + + for(i=0;i<MAX_GRAPHS;i++){ + create_filter_box(&io->graphs[i], vbox, i+1); + } + + return; +} + +static void +copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data) +{ + guint32 i, interval, val; + char string[15]; + GtkClipboard *cb; + GString *CSV_str=g_string_new(""); + io_stat_t *io = user_data; + + g_string_append(CSV_str, "\"Interval start\""); + for(i=0;i<MAX_GRAPHS;i++) { + if (io->graphs[i].display) { + g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1); + } + } + g_string_append(CSV_str,"\n"); + + for(interval=0; interval<io->max_interval; interval+=io->interval) { + print_interval_string (string, 15, interval, io, FALSE); + g_string_append_printf(CSV_str, "\"%s\"", string); + for(i=0;i<MAX_GRAPHS;i++) { + if (io->graphs[i].display) { + val=get_it_value(io, i, interval/io->interval); + g_string_append_printf(CSV_str, ",\"%d\"", val); + } + } + g_string_append(CSV_str,"\n"); + } + + /* Now that we have the CSV data, copy it into the default clipboard */ + cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */ + gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */ + g_string_free(CSV_str, TRUE); /* Free the memory */ +} + +static void +init_io_stat_window(io_stat_t *io) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *bbox; + GtkWidget *close_bt, *help_bt; + GtkWidget *copy_bt; + GtkWidget *save_bt; + + /* create the main window, transient_for top_level */ + io->window = dlg_window_new("I/O Graphs"); + gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE); + + vbox=gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(io->window), vbox); + gtk_widget_show(vbox); + + create_draw_area(io, vbox); + + hbox=gtk_hbox_new(FALSE, 3); + gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START); + gtk_widget_show(hbox); + + create_filter_area(io, hbox); + create_ctrl_area(io, hbox); + + io_stat_set_title(io); + + bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, + GTK_STOCK_COPY, GTK_STOCK_HELP, NULL); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + gtk_widget_show(bbox); + + close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE); + window_set_cancel_button(io->window, close_bt, window_cancel_button_cb); + gtk_widget_set_tooltip_text(close_bt, "Close this dialog"); + save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE); + gtk_widget_set_sensitive(save_bt, FALSE); + gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file"); + g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL); + g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt); + + copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY); + gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format"); + g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io); + + help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP); + g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG); + gtk_widget_set_tooltip_text (help_bt, "Show topic specific help"); + g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL); + + gtk_widget_show(io->window); + window_present(io->window); +} + +void +gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_) +{ + iostat_init(NULL,NULL); +} + +void +register_tap_listener_gtk_iostat(void) +{ + register_stat_cmd_arg("io,stat", iostat_init,NULL); +} |