/* This file is part of GNUnet Copyright (C) 2004, 2005, 2006 GNUnet e.V. GNUnet 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 3, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Portions of this code were adopted from the gnome-system-monitor v2.0.5, Copyright (C) 2001 Kevin Vandersloot */ #include "platform.h" #include "gnunetgtk_common.h" #include #include #include "functions.h" #define MAX_COLOR 6 #define GNOME_PAD_SMALL 2 #define FRAME_WIDTH 0 typedef struct { gint type; guint count; guint speed; guint draw_width, draw_height; guint num_points; guint allocated; GdkColor *colors; gfloat **data; gfloat **odata; guint data_size; gint colors_allocated; GtkWidget *main_widget; GtkWidget *disp; GtkWidget *label; GdkPixmap *pixmap; GdkGC *gc; int timer_index; gboolean draw; int statIdx; } LoadGraph; typedef struct { GdkColor bg_color; GdkColor frame_color; GdkColor mem_color[MAX_COLOR]; } ProcConfig; static unsigned long long UPDATE_INTERVAL; /** * Redraws the backing pixmap for the load graph and updates the window */ static void load_graph_draw (LoadGraph * g) { guint i; guint j; gint dely; float delx; float max; GdkFont *font; if (!g->disp->window) return; g->draw_width = g->disp->allocation.width; g->draw_height = g->disp->allocation.height; if (!g->pixmap) g->pixmap = gdk_pixmap_new (g->disp->window, g->draw_width, g->draw_height, gtk_widget_get_visual (g->disp)->depth); /* Create GC if necessary. */ if (!g->gc) { g->gc = gdk_gc_new (g->disp->window); gdk_gc_copy (g->gc, g->disp->style->white_gc); } /* Allocate colors. */ if (!g->colors_allocated) { GdkColormap *colormap; colormap = gdk_window_get_colormap (g->disp->window); for (i = 0; i < 2 + g->count; i++) gdk_color_alloc (colormap, &(g->colors[i])); g->colors_allocated = 1; } /* Erase Rectangle */ gdk_gc_set_foreground (g->gc, &(g->colors[0])); gdk_draw_rectangle (g->pixmap, g->gc, TRUE, 0, 0, g->disp->allocation.width, g->disp->allocation.height); /* draw frame */ gdk_gc_set_foreground (g->gc, &(g->colors[1])); gdk_draw_rectangle (g->pixmap, g->gc, FALSE, 0, 0, g->draw_width, g->disp->allocation.height); max = 0.26; /* force showing at least the 25% line */ for (i = 0; i < g->num_points - 1; i++) for (j = 0; j < g->count; j++) if (g->data[i][j] > max) max = g->data[i][j]; max = max * 1.01; /* leave top 1% free */ font = gdk_font_load ("fixed"); /* deprecated, but pango is far more than * what we need here -- fix later? */ /* draw lines at 25%, 50%, 75% and 100% of max */ dely = g->draw_height / max / 4; for (i = 1; i < 5; i++) { gint y1 = g->draw_height + 1 - i * dely; if ((dely < 30) && (i != 4)) continue; /* only print additional * lines if there is enough space! */ if (y1 > 0) { const gchar *label[] = { NULL, " 25%", " 50%", " 75%", "100%", }; gdk_draw_string (g->pixmap, font, g->gc, 10, y1 - 8, label[i]); gdk_draw_line (g->pixmap, g->gc, 0, y1, g->draw_width, y1); if (i == 4) { /* extra-thick line at 100% */ gdk_draw_line (g->pixmap, g->gc, 0, y1 - 1, g->draw_width, y1 - 1); gdk_draw_line (g->pixmap, g->gc, 0, y1 + 1, g->draw_width, y1 + 1); } } } gdk_gc_set_line_attributes (g->gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_MITER); delx = (float) g->draw_width / (g->num_points - 1); for (j = 0; j < g->count; j++) { gdk_gc_set_foreground (g->gc, &(g->colors[j + 2])); for (i = 0; i < g->num_points - 1; i++) { gint x1 = i * delx; gint x2 = (i + 1) * delx; gint y1 = g->data[i][j] / max * g->draw_height - 1; gint y2 = g->data[i + 1][j] / max * g->draw_height - 1; if ((g->data[i][j] != -1) && (g->data[i + 1][j] != -1)) { if (stats[g->statIdx].fill <= j) { gdk_draw_line (g->pixmap, g->gc, g->draw_width - x2, g->draw_height - y2, g->draw_width - x1, g->draw_height - y1); } else { GdkPoint points[4]; points[0].x = g->draw_width - x2; points[0].y = g->draw_height - y2; points[1].x = g->draw_width - x1; points[1].y = g->draw_height - y1; points[2].x = g->draw_width - x1; points[3].x = g->draw_width - x2; if (j == 0) { points[2].y = g->draw_height; points[3].y = g->draw_height; } else { gint ly1 = g->data[i][j - 1] / max * g->draw_height - 1; gint ly2 = g->data[i + 1][j - 1] / max * g->draw_height - 1; points[2].y = g->draw_height - ly1; points[3].y = g->draw_height - ly2; } gdk_draw_polygon (g->pixmap, g->gc, 1, points, 4); } } } } gdk_gc_set_line_attributes (g->gc, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_MITER); gdk_draw_pixmap (g->disp->window, g->disp->style->fg_gc[GTK_WIDGET_STATE (g->disp)], g->pixmap, 0, 0, 0, 0, g->disp->allocation.width, g->disp->allocation.height); } /* Updates the load graph when the timeout expires */ static gboolean load_graph_update (gpointer ptr) { LoadGraph *g = ptr; guint i; guint j; for (i = 0; i < g->num_points; i++) memcpy (g->odata[i], g->data[i], g->data_size * g->count); stats[g->statIdx].getData (stats[g->statIdx].get_closure, g->data); for (i = 0; i < g->num_points - 1; i++) for (j = 0; j < g->count; j++) g->data[i + 1][j] = g->odata[i][j]; if (g->draw) load_graph_draw (g); return TRUE; } static void load_graph_unalloc (LoadGraph * g) { unsigned int i; if (!g->allocated) return; for (i = 0; i < g->num_points; i++) { GNUNET_free (g->data[i]); GNUNET_free (g->odata[i]); } GNUNET_free (g->data); GNUNET_free (g->odata); g->data = g->odata = NULL; if (g->pixmap) { gdk_pixmap_unref (g->pixmap); g->pixmap = NULL; } g->allocated = FALSE; } static void load_graph_alloc (LoadGraph * g) { unsigned int i; unsigned int j; if (g->allocated) return; g->data = GNUNET_malloc (sizeof (gfloat *) * g->num_points); g->odata = GNUNET_malloc (sizeof (gfloat *) * g->num_points); g->data_size = sizeof (gfloat); for (i = 0; i < g->num_points; i++) { g->data[i] = GNUNET_malloc (g->data_size * g->count); g->odata[i] = GNUNET_malloc (g->data_size * g->count); } for (i = 0; i < g->num_points; i++) for (j = 0; j < g->count; j++) g->data[i][j] = -1; g->allocated = TRUE; } static gint load_graph_configure (GtkWidget * widget, GdkEventConfigure * event, gpointer data_ptr) { LoadGraph *c = (LoadGraph *) data_ptr; if (c->pixmap) { gdk_pixmap_unref (c->pixmap); c->pixmap = NULL; } if (!c->pixmap) c->pixmap = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, gtk_widget_get_visual (c->disp)->depth); gdk_draw_rectangle (c->pixmap, widget->style->black_gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); gdk_draw_pixmap (widget->window, c->disp->style->fg_gc[GTK_WIDGET_STATE (widget)], c->pixmap, 0, 0, 0, 0, c->disp->allocation.width, c->disp->allocation.height); load_graph_draw (c); return TRUE; } static gint load_graph_expose (GtkWidget * widget, GdkEventExpose * event, gpointer data_ptr) { LoadGraph *g = (LoadGraph *) data_ptr; gdk_draw_pixmap (widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], g->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } static void load_graph_stop (LoadGraph * g) { if (!g) return; if (g->timer_index != -1) { gtk_timeout_remove (g->timer_index); g->timer_index = -1; } g->draw = FALSE; } static void load_graph_destroy (GtkWidget * widget, gpointer data_ptr) { LoadGraph *g = (LoadGraph *) data_ptr; load_graph_stop (g); if (g->timer_index != -1) gtk_timeout_remove (g->timer_index); load_graph_unalloc (g); GNUNET_free (g->colors); GNUNET_free (g); } static LoadGraph * load_graph_new (int statIdx, const ProcConfig * config) { LoadGraph *g; unsigned int i; if (stats[statIdx].count > MAX_COLOR) { GNUNET_GE_BREAK (NULL, 0); return NULL; } g = GNUNET_malloc (sizeof (LoadGraph)); g->statIdx = statIdx; g->count = stats[statIdx].count; g->speed = UPDATE_INTERVAL / GNUNET_CRON_MILLISECONDS; g->num_points = 600; g->colors = GNUNET_malloc (sizeof (GdkColor) * (2 + g->count)); g->colors[0] = config->bg_color; g->colors[1] = config->frame_color; for (i = 0; i < g->count; i++) g->colors[2 + i] = config->mem_color[i]; g->timer_index = -1; g->draw = FALSE; g->main_widget = gtk_vbox_new (FALSE, FALSE); gtk_widget_show (g->main_widget); g->disp = gtk_drawing_area_new (); gtk_widget_show (g->disp); gtk_signal_connect (GTK_OBJECT (g->disp), "expose_event", GTK_SIGNAL_FUNC (load_graph_expose), g); gtk_signal_connect (GTK_OBJECT (g->disp), "configure_event", GTK_SIGNAL_FUNC (load_graph_configure), g); gtk_signal_connect (GTK_OBJECT (g->disp), "destroy", GTK_SIGNAL_FUNC (load_graph_destroy), g); gtk_widget_add_events (g->disp, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK); gtk_box_pack_start (GTK_BOX (g->main_widget), g->disp, TRUE, TRUE, 0); load_graph_alloc (g); gtk_widget_show_all (g->main_widget); g->timer_index = gtk_timeout_add (g->speed, &load_graph_update, g); return g; } static GtkWidget * create_sys_view (int statIdx, const ProcConfig * config) { GtkWidget *mem_frame; LoadGraph *mem_graph; mem_graph = load_graph_new (statIdx, config); if (mem_graph == NULL) return NULL; /* oops */ mem_frame = gtk_frame_new (_(stats[statIdx].frameName)); gtk_container_add (GTK_CONTAINER (mem_frame), mem_graph->main_widget); gtk_container_set_border_width (GTK_CONTAINER (mem_graph->main_widget), GNOME_PAD_SMALL); gtk_container_set_border_width (GTK_CONTAINER (mem_frame), GNOME_PAD_SMALL); gtk_widget_show (mem_frame); if (mem_graph->timer_index == -1) mem_graph->timer_index = gtk_timeout_add (mem_graph->speed, &load_graph_update, mem_graph); mem_graph->draw = TRUE; return mem_frame; } void init_stats (struct GNUNET_GE_Context *ectx, struct GNUNET_GC_Configuration *cfg) { GtkWidget *statusConnexionsBox; GtkWidget *statusConnexionsLabel; GtkWidget *sys_box; GtkWidget *label; GtkWidget *notebook; ProcConfig config; int i; statusConnexionsLabel = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "statusConnexionsLabel"); gtk_label_set_width_chars (GTK_LABEL (statusConnexionsLabel), g_utf8_strlen (_("Connected to %Lu peers"), 40) - 1); statusConnexionsBox = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "statusConnexionsBox"); gtk_widget_set_sensitive (statusConnexionsBox, TRUE); GNUNET_GC_get_configuration_value_number (cfg, "GNUNET-GTK", "STATS-INTERVAL", 1, 99 * GNUNET_CRON_YEARS, 30 * GNUNET_CRON_SECONDS, &UPDATE_INTERVAL); init_functions (ectx, cfg); notebook = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "statsNotebook"); memset (&config, 0, sizeof (ProcConfig)); gdk_color_parse ("black", &config.bg_color); gdk_color_parse ("gray", &config.frame_color); gdk_color_parse ("red", &config.mem_color[0]); gdk_color_parse ("green", &config.mem_color[1]); gdk_color_parse ("yellow", &config.mem_color[2]); gdk_color_parse ("blue", &config.mem_color[3]); gdk_color_parse ("gray", &config.mem_color[4]); gdk_color_parse ("magenta", &config.mem_color[5]); GNUNET_GE_ASSERT (ectx, MAX_COLOR == 6); i = -1; while (stats[++i].paneName != NULL) { sys_box = create_sys_view (i, &config); if (sys_box == NULL) continue; /* oops */ label = gtk_label_new (gettext (stats[i].paneName)); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sys_box, label); } gtk_widget_show (notebook); } void done_stats () { done_functions (); } /* end of statistics.c */