Removed duplicated #include lines
[metze/wireshark/wip.git] / ui / gtk / memory_dlg.c
1 /* memory_dlg.c
2  *
3  * Based on
4  * io_stat   2002 Ronnie Sahlberg
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25
26 #include "config.h"
27
28 #include <math.h>
29 #include <gtk/gtk.h>
30
31 #include "ui/gtk/dlg_utils.h"
32 #include "ui/simple_dialog.h"
33 #include "ui/gtk/gui_utils.h"
34 #include "ui/gtk/old-gtk-compat.h"
35
36 #include "wsutil/str_util.h"
37 #include "epan/app_mem_usage.h"
38
39 enum {
40     MAX_GRAPHS = 10
41 };
42
43 #define MAX_YSCALE          28
44 static guint32 yscale_max[MAX_YSCALE] = {0, 1, 10, 20,
45                                          50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000,
46                                          50000, 100000, 200000, 500000, 1000000, 2000000,
47                                          5000000, 10000000, 20000000, 50000000, 100000000,
48                                          200000000, 500000000, 1000000000, 2000000000};
49
50 #define DEFAULT_PIXELS_PER_TICK 5
51
52 #define NUM_IO_ITEMS 100000
53 typedef struct _io_item_t {
54     gsize bytes;
55 } io_item_t;
56
57 typedef struct _io_stat_graph_t {
58     struct _io_stat_t *io;
59
60     io_item_t         *items[NUM_IO_ITEMS];
61     gboolean           display;
62     GtkWidget         *display_button;
63 } io_stat_graph_t;
64
65 typedef struct _io_stat_t {
66     gboolean       needs_redraw;
67     guint32        num_items;   /* total number of items in all intervals (zero relative) */
68     guint32        left_x_border;
69     guint32        right_x_border;
70     nstime_t       start_time;
71
72     struct _io_stat_graph_t graphs[MAX_GRAPHS];
73     GtkWidget     *window;
74     GtkWidget     *draw_area;
75 #if GTK_CHECK_VERSION(2,22,0)
76     cairo_surface_t *surface;
77 #else
78     GdkPixmap       *pixmap;
79 #endif
80     int            surface_width;
81     int            surface_height;
82     int            pixels_per_tick;
83
84     guint timer_id;
85 } io_stat_t;
86
87 #define INTERVAL 1000
88
89 static void
90 io_stat_reset(io_stat_t *io)
91 {
92     int i, j;
93
94     io->needs_redraw = TRUE;
95     for (i=0; i<MAX_GRAPHS; i++) {
96         for (j=0; j<NUM_IO_ITEMS; j++) {
97             io_item_t *ioi;
98             ioi = (io_item_t *)io->graphs[i].items[j];
99
100             ioi->bytes      = 0;
101         }
102     }
103     io->num_items        = 0;
104     io->start_time.secs  = time(NULL);
105     io->start_time.nsecs = 0;
106 }
107
108 static guint64
109 get_it_value(io_stat_t *io, int graph, int idx)
110 {
111     io_item_t *it;
112
113     g_assert(graph < MAX_GRAPHS);
114     g_assert(idx < NUM_IO_ITEMS);
115
116     it = (io_item_t *)io->graphs[graph].items[idx];
117
118         return it->bytes;
119 }
120
121 static void
122 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io)
123 {
124         struct tm *tmp;
125         time_t sec_val = interval/1000 + io->start_time.secs;
126         gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
127
128         if (nsec_val >= 1000) {
129             sec_val++;
130             nsec_val -= 1000;
131         }
132         tmp = localtime (&sec_val);
133         if (INTERVAL >= 1000) {
134             g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
135         } else if (INTERVAL >= 100) {
136             g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
137         } else if (INTERVAL >= 10) {
138             g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
139         } else {
140             g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
141         }
142 }
143
144 static void
145 io_stat_draw(io_stat_t *io)
146 {
147     int            i;
148     guint32        last_interval, first_interval, interval_delta;
149     gint32         current_interval;
150     guint32        top_y_border;
151     guint32        bottom_y_border;
152     PangoLayout   *layout;
153     int            label_width, label_height;
154     guint32        draw_width, draw_height;
155     GtkAllocation  widget_alloc;
156
157     /* new variables */
158     guint32        num_time_intervals; /* number of intervals relative to 1 */
159     guint64        max_value;   /* max value of seen data */
160     guint32        max_y;       /* max value of the Y scale */
161     cairo_t       *cr;
162
163     if (!io->needs_redraw) {
164         return;
165     }
166     io->needs_redraw = FALSE;
167     /*
168     * Find the length of the intervals we have data for
169     * so we know how large arrays we need to malloc()
170     */
171     num_time_intervals = io->num_items+1;
172
173     /* XXX move this check to _packet() */
174     if (num_time_intervals > NUM_IO_ITEMS) {
175         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
176         return;
177     }
178
179     /*
180     * find the max value so we can autoscale the y axis
181     */
182     max_value = 0;
183     for (i=0; i<MAX_GRAPHS; i++) {
184         int idx;
185
186         if (!io->graphs[i].display) {
187             continue;
188         }
189         for (idx=0; (guint32)(idx) < num_time_intervals; idx++) {
190             guint64 val;
191
192             val = get_it_value(io, i, idx);
193
194             /* keep track of the max value we have encountered */
195             if (val>max_value) {
196                 max_value = val;
197             }
198         }
199     }
200
201     /*
202     * Clear out old plot
203     */
204 #if GTK_CHECK_VERSION(2,22,0)
205     cr = cairo_create (io->surface);
206 #else
207     cr = gdk_cairo_create (io->pixmap);
208 #endif
209     cairo_set_source_rgb (cr, 1, 1, 1);
210     gtk_widget_get_allocation(io->draw_area, &widget_alloc);
211     cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
212     cairo_fill (cr);
213     cairo_destroy (cr);
214     /*
215     * Calculate the y scale we should use
216     */
217     max_y = yscale_max[MAX_YSCALE-1];
218     for (i=MAX_YSCALE-1; i>1; i--) {
219         if (max_value < yscale_max[i]) {
220             max_y = yscale_max[i];
221         }
222     }
223
224     layout = gtk_widget_create_pango_layout(io->draw_area, "99999 T bytes");
225     pango_layout_get_pixel_size(layout, &label_width, &label_height);
226
227     io->left_x_border = 10;
228     io->right_x_border = label_width + 20;
229     top_y_border = 10;
230     bottom_y_border = label_height + 20;
231
232     /*
233     * Calculate the size of the drawing area for the actual plot
234     */
235     draw_width = io->surface_width-io->right_x_border - io->left_x_border;
236     draw_height = io->surface_height-top_y_border - bottom_y_border;
237
238     /* Draw the y axis and labels
239     * (we always draw the y scale with 11 ticks along the axis)
240     */
241 #if GTK_CHECK_VERSION(2,22,0)
242     cr = cairo_create(io->surface);
243 #else
244     cr = gdk_cairo_create(io->pixmap);
245 #endif
246     cairo_set_line_width(cr, 1.0);
247     cairo_move_to(cr, io->surface_width-io->right_x_border+1.5, top_y_border + 0.5);
248     cairo_line_to(cr, io->surface_width-io->right_x_border+1.5, io->surface_height-bottom_y_border + 0.5);
249     cairo_stroke(cr);
250
251     for (i=0; i<=10; i++) {
252         int xwidth, lwidth, ypos;
253
254         xwidth = 5;
255             if (!(i%5)) {
256                 /* first, middle and last tick are slightly longer */
257                 xwidth = 10;
258             }
259             ypos = io->surface_height-bottom_y_border-draw_height*i/10;
260         /* draw the tick */
261         cairo_move_to(cr, io->surface_width-io->right_x_border+1.5, ypos+0.5);
262         cairo_line_to(cr, io->surface_width-io->right_x_border+1.5+xwidth,ypos+0.5);
263         cairo_stroke(cr);
264         /* draw the labels */
265         if (xwidth == 10) {
266             guint32 value = (max_y/10)*i;
267             char *label_tmp;
268
269             label_tmp = format_size(value, format_size_unit_bytes);
270
271             pango_layout_set_text(layout, label_tmp, -1);
272             pango_layout_get_pixel_size(layout, &lwidth, NULL);
273
274             cairo_move_to (cr, io->surface_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
275             pango_cairo_show_layout (cr, layout);
276
277             g_free(label_tmp);
278         }
279     }
280
281     last_interval = (io->num_items) * INTERVAL;
282
283     /*XXX*/
284     /* plot the x-scale */
285     cairo_move_to(cr, io->left_x_border+0.5, io->surface_height-bottom_y_border+1.5);
286     cairo_line_to(cr, io->surface_width-io->right_x_border+1.5,io->surface_height-bottom_y_border+1.5);
287     cairo_stroke(cr);
288     if ((last_interval/INTERVAL) >= draw_width/io->pixels_per_tick) {
289         first_interval  = (last_interval/INTERVAL)-draw_width/io->pixels_per_tick+1;
290         first_interval *= INTERVAL;
291     } else {
292         first_interval = 0;
293     }
294
295     interval_delta = (100/io->pixels_per_tick)*INTERVAL;
296     for (current_interval = last_interval;
297          current_interval >= (gint32)first_interval;
298          current_interval = current_interval-INTERVAL) {
299         int x, xlen;
300
301         /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
302         /* if pixels_per_tick is 5, only draw every 5 ticks */
303         if (((io->pixels_per_tick < 5) && (current_interval % (10*INTERVAL))) ||
304             ((io->pixels_per_tick == 5) && (current_interval % (5*INTERVAL)))) {
305                 continue;
306         }
307
308         if (!(current_interval%interval_delta)) {
309             xlen = 10;
310         } else if (!(current_interval%(interval_delta/2))) {
311             xlen = 8;
312         } else {
313             xlen = 5;
314         }
315         x = draw_width+io->left_x_border-((last_interval-current_interval)/INTERVAL)*io->pixels_per_tick;
316         cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->surface_height-bottom_y_border+1.5);
317         cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->surface_height-bottom_y_border+xlen+1.5);
318         cairo_stroke(cr);
319         if (xlen == 10) {
320             char label_string[64];
321             int lwidth, x_pos;
322             print_interval_string (label_string, sizeof(label_string), current_interval, io);
323             pango_layout_set_text(layout, label_string, -1);
324             pango_layout_get_pixel_size(layout, &lwidth, NULL);
325
326             if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
327                 x_pos = 5;
328             } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->surface_width-5)) {
329                 x_pos = io->surface_width-lwidth-5;
330             } else {
331                 x_pos = x-1-io->pixels_per_tick/2-lwidth/2;
332             }
333             cairo_move_to (cr, x_pos, io->surface_height-bottom_y_border+15);
334             pango_cairo_show_layout (cr, layout);
335         }
336
337     }
338     cairo_destroy (cr);
339     cr = NULL;
340     g_object_unref(G_OBJECT(layout));
341
342     /*
343     * Loop over all graphs and draw them
344     */
345 #if GTK_CHECK_VERSION(2,22,0)
346     cr = cairo_create (io->surface);
347 #else
348     cr = gdk_cairo_create (io->pixmap);
349 #endif
350     cairo_set_line_width (cr, 1.0);
351
352     for (i=MAX_GRAPHS-1; i>=0; i--) {
353         guint64 val;
354         guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
355
356         if (!io->graphs[i].display) {
357             continue;
358         }
359
360         /* initialize prev x/y to the value of the first interval */
361         prev_x_pos = draw_width-1 -
362             io->pixels_per_tick * ((last_interval - first_interval) / INTERVAL) +
363             io->left_x_border;
364         val = get_it_value(io, i, first_interval / INTERVAL);
365
366         if (val>max_y) {
367             prev_y_pos = 0;
368         } else {
369             prev_y_pos = (guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
370         }
371
372         for (interval = first_interval;
373              interval < last_interval;
374              interval += INTERVAL) {
375                 x_pos = draw_width-1-io->pixels_per_tick*((last_interval-interval)/INTERVAL)+io->left_x_border;
376
377                 val = get_it_value(io, i, interval/INTERVAL);
378                 /* Moving average calculation */
379
380                 if (val>max_y) {
381                     y_pos = 0;
382                 } else {
383                     y_pos = (guint32)(draw_height - 1 -
384                         ((val * draw_height) / max_y) +
385                         top_y_border);
386                 }
387
388                     /* Don't draw anything if the segment entirely above the top of the graph
389                     */
390                     if ( (prev_y_pos != 0) || (y_pos != 0) ) {
391                         static GdkRGBA red_color = {1.0, 0.0, 0.1, 1.0};
392
393                         cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
394                         cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
395                         gdk_cairo_set_source_rgba(cr, &red_color);
396                         cairo_stroke(cr);
397                     }
398
399                 prev_y_pos = y_pos;
400                 prev_x_pos = x_pos;
401         }
402     }
403     cairo_destroy(cr);
404
405     cr = gdk_cairo_create(gtk_widget_get_window(io->draw_area));
406
407 #if GTK_CHECK_VERSION(2,22,0)
408     cairo_set_source_surface(cr, io->surface, 0, 0);
409 #else
410     gdk_cairo_set_source_pixmap(cr, io->pixmap, 0, 0);
411 #endif
412     cairo_rectangle(cr, 0, 0, io->surface_width, io->surface_height);
413     cairo_fill (cr);
414
415     cairo_destroy (cr);
416 }
417
418 static void
419 io_stat_redraw(io_stat_t *io)
420 {
421     io->needs_redraw = TRUE;
422     io_stat_draw(io);
423 }
424
425
426 static void
427 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
428 {
429     io_stat_t      *io           = (io_stat_t *)user_data;
430     int             i,j;
431
432     for (i=0; i<MAX_GRAPHS; i++) {
433         if (io->graphs[i].display) {
434
435             for (j=0; j<NUM_IO_ITEMS; j++) {
436                 g_free(io->graphs[i].items[j]);
437                 io->graphs[i].items[j] = NULL;
438             }
439         }
440     }
441
442     g_source_remove(io->timer_id);
443
444     g_free(io);
445 }
446
447 /* create a new backing pixmap of the appropriate size */
448 static gboolean
449 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
450 {
451     io_stat_t      *io           = (io_stat_t *)user_data;
452     GtkAllocation   widget_alloc;
453     cairo_t        *cr;
454
455 #if GTK_CHECK_VERSION(2,22,0)
456     if (io->surface) {
457          cairo_surface_destroy (io->surface);
458         io->surface = NULL;
459     }
460 #else
461     if (io->pixmap) {
462         g_object_unref(io->pixmap);
463         io->pixmap = NULL;
464     }
465 #endif
466
467     gtk_widget_get_allocation(widget, &widget_alloc);
468 #if GTK_CHECK_VERSION(2,22,0)
469     io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
470             CAIRO_CONTENT_COLOR,
471             widget_alloc.width,
472             widget_alloc.height);
473
474 #else
475     io->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
476             widget_alloc.width,
477             widget_alloc.height,
478             -1);
479 #endif
480     io->surface_width = widget_alloc.width;
481     io->surface_height = widget_alloc.height;
482
483 #if GTK_CHECK_VERSION(2,22,0)
484     cr = cairo_create(io->surface);
485 #else
486     cr = gdk_cairo_create(io->pixmap);
487 #endif
488     cairo_rectangle(cr, 0, 0, widget_alloc.width, widget_alloc.height);
489     cairo_set_source_rgb(cr, 1, 1, 1);
490     cairo_fill(cr);
491     cairo_destroy(cr);
492
493     io_stat_redraw(io);
494     return TRUE;
495 }
496
497 #if GTK_CHECK_VERSION(3,0,0)
498 static gboolean
499 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
500 {
501     io_stat_t     *io = (io_stat_t *)user_data;
502     GtkAllocation  allocation;
503
504     gtk_widget_get_allocation(widget, &allocation);
505     cairo_set_source_surface(cr, io->surface, 0, 0);
506     cairo_rectangle(cr, 0, 0, allocation.width, allocation.width);
507     cairo_fill (cr);
508
509     return FALSE;
510 }
511 #else
512 /* redraw the screen from the backing pixmap */
513 static gboolean
514 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
515 {
516     io_stat_t *io = (io_stat_t *)user_data;
517     cairo_t   *cr = gdk_cairo_create (gtk_widget_get_window(widget));
518
519 #if GTK_CHECK_VERSION(2,22,0)
520     cairo_set_source_surface (cr, io->surface, 0, 0);
521 #else
522     gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
523 #endif
524     cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
525     cairo_fill (cr);
526
527     cairo_destroy (cr);
528
529     return FALSE;
530 }
531 #endif
532 static void
533 create_draw_area(io_stat_t *io, GtkWidget *box)
534 {
535     io->draw_area = gtk_drawing_area_new();
536     g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
537
538     gtk_widget_set_size_request(io->draw_area, io->surface_width, io->surface_height);
539
540     /* signals needed to handle backing pixmap */
541 #if GTK_CHECK_VERSION(3,0,0)
542     g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io);
543 #else
544     g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
545 #endif
546     g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
547     gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
548
549     gtk_widget_show(io->draw_area);
550     gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
551 }
552
553 static void
554 filter_callback(GtkWidget *widget _U_, gpointer user_data)
555 {
556     io_stat_graph_t   *gio   = (io_stat_graph_t *)user_data;
557
558     /* this graph is not active, just update display and redraw */
559     if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))) {
560         gio->display = FALSE;
561         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), FALSE);
562     } else {
563         gio->display = TRUE;
564         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), TRUE);
565     }
566
567     gdk_window_raise(gtk_widget_get_window(gio->io->window));
568     io_stat_redraw(gio->io);
569 }
570
571 static void
572 create_filter_area(io_stat_t *io, GtkWidget *box)
573 {
574     GtkWidget *frame;
575     GtkWidget *hbox;
576     int i;
577
578     frame = gtk_frame_new("Memory Graphs");
579     gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
580     gtk_widget_show(frame);
581
582     hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
583     gtk_container_add(GTK_CONTAINER(frame), hbox);
584     gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
585     gtk_widget_show(hbox);
586
587     for (i=0; i<MAX_GRAPHS; i++) {
588         const char *label = memory_usage_get(i, NULL);
589         GtkWidget *display_button;
590
591         if (!label)
592             break;
593
594         display_button = gtk_toggle_button_new_with_label(label);
595         gtk_box_pack_start(GTK_BOX(hbox), display_button, FALSE, FALSE, 0);
596         g_signal_connect(display_button, "toggled", G_CALLBACK(filter_callback), &io->graphs[i]);
597         gtk_widget_show(display_button);
598
599         io->graphs[i].display_button = display_button;
600     }
601 }
602
603 static void
604 init_io_stat_window(io_stat_t *io)
605 {
606     GtkWidget *vbox;
607     GtkWidget *hbox;
608     GtkWidget *bbox;
609     GtkWidget *close_bt;
610
611     /* create the main window, transient_for top_level */
612     io->window = dlg_window_new("Wireshark memory usage");
613     gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
614
615     vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
616     gtk_container_add(GTK_CONTAINER(io->window), vbox);
617     gtk_widget_show(vbox);
618
619     create_draw_area(io, vbox);
620
621     hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
622     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
623     gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
624     gtk_widget_show(hbox);
625
626     create_filter_area(io, hbox);
627
628     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
629     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
630     gtk_widget_show(bbox);
631
632     close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
633     window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
634     gtk_widget_set_tooltip_text(close_bt,  "Close this dialog");
635
636     gtk_widget_show(io->window);
637     window_present(io->window);
638 }
639
640
641 static gboolean
642 call_it(gpointer user_data)
643 {
644     io_stat_t *io = (io_stat_t *) user_data;
645     char buf[64];
646     char *tmp;
647     int idx, i;
648
649     io->needs_redraw = TRUE;
650
651     idx = io->num_items++;
652
653     /* some sanity checks */
654     if ((idx < 0) || (idx >= NUM_IO_ITEMS)) {
655         io->num_items = NUM_IO_ITEMS-1;
656         return FALSE;
657     }
658
659
660     for (i = 0; i < MAX_GRAPHS; i++) {
661         const char *label;
662
663         label = memory_usage_get(i, &io->graphs[i].items[idx]->bytes);
664
665         if (!label)
666            break;
667
668         tmp = format_size(io->graphs[i].items[idx]->bytes, format_size_unit_bytes);
669         g_snprintf(buf, sizeof(buf), "%s [%s]", label, tmp);
670         gtk_button_set_label(GTK_BUTTON(io->graphs[i].display_button), buf);
671         g_free(tmp);
672     }
673
674     io_stat_draw(io);
675
676     return TRUE;
677 }
678
679 void
680 memory_stat_init(void)
681 {
682     io_stat_t *io;
683     int i = 0, j = 0;
684
685     io = g_new(io_stat_t,1);
686     io->needs_redraw         = TRUE;
687     io->window               = NULL;
688     io->draw_area            = NULL;
689 #if GTK_CHECK_VERSION(2,22,0)
690     io->surface              = NULL;
691 #else
692     io->pixmap               = NULL;
693 #endif
694     io->surface_width        = 500;
695     io->surface_height       = 200;
696     io->pixels_per_tick      = DEFAULT_PIXELS_PER_TICK;
697     io->num_items            = 0;
698     io->left_x_border        = 0;
699     io->right_x_border       = 500;
700     io->start_time.secs      = time(NULL);
701     io->start_time.nsecs     = 0;
702
703     for (i=0; i<MAX_GRAPHS; i++) {
704         io->graphs[i].display                   = 0;
705         io->graphs[i].display_button            = NULL;
706         io->graphs[i].io                        = io;
707
708         for (j=0; j<NUM_IO_ITEMS; j++) {
709             io->graphs[i].items[j] = g_new(io_item_t,1);
710         }
711     }
712     io_stat_reset(io);
713
714     /* build the GUI */
715     init_io_stat_window(io);
716     io->graphs[0].display = TRUE;
717     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(io->graphs[0].display_button), TRUE);
718
719     gdk_window_raise(gtk_widget_get_window(io->window));
720     io_stat_redraw(io);
721
722     io->timer_id = g_timeout_add(INTERVAL, call_it, io);
723 }
724