/* GKrellM giFT plugin * Copyright (C) 2002, 2003 Tilman Sauerbeck * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * */ #include #include "common.h" #include "event_ids.h" #include "io.h" #include "cfg.h" #include "../pixmaps/arrow_down.h" #include "../pixmaps/arrow_up.h" #define CMD_ATTACH "ATTACH" #define CMD_DETACH "DETACH" #define CMD_STATS "STATS" static giFTStats stats; static GkrellmMonitor monitor; static GkrellmChart *chart = NULL; static GkrellmChartdata *chart_data[DIR_NUM] = {NULL, NULL}; static GtkWidget *plugin_vbox = NULL; static GSList *transfer_list = NULL; static gboolean connected = FALSE; static gint chart_width = 0; static gint style_id = 0; giFTConfig gift_cfg; giFTStats gift_stats_get () { return stats; } void gift_stats_set (giFTStats s) { stats = s; } void gift_panels_set_visibility (gboolean visible) { Transfer *t; GSList *list; void (*func[])(GkrellmPanel *) = {gkrellm_panel_hide, gkrellm_panel_show}; for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; func[visible] (t->panel); } } void gift_chart_set_visibility () { gboolean old = gkrellm_is_chart_visible (chart); gkrellm_chart_enable_visibility (chart, connected && gift_cfg.show_chart, &old); } void gift_try_connect () { if (!connected) { connected = gift_daemon_connect (gift_cfg.host, gift_cfg.port); if (connected) gift_daemon_send (CMD_ATTACH); } } GdkColormap *gift_get_colormap () { static GdkColormap *cmap; if (!cmap) cmap = gdk_colormap_get_system (); return cmap; } static void transfer_free (Transfer *t) { g_assert (t); if (t->visible) gkrellm_panel_destroy (t->panel); g_free (t); } void gift_transfer_remove (Transfer *t) { g_assert (t); stats.transfer_count[t->id->dir]--; transfer_list = g_slist_remove (transfer_list, t); transfer_free (t); } /* this function is usually called when the plugin was disabled by the user * removes all transfers and their panels */ void gift_monitor_disabled () { if (connected) gift_daemon_send (CMD_DETACH); /* wait until connection gets closed and the GIOChannel stuff is freed */ while (connected) connected = gift_daemon_read (); while (transfer_list) gift_transfer_remove ((Transfer *) transfer_list->data); gift_event_id_destroy (); } /* called when GKrellM exits. * removes transfers (and the panels belonging to them) and the chart */ static void cleanup () { gint i; gift_monitor_disabled (); gkrellm_chartconfig_destroy (&gift_cfg.chart_cfg); gkrellm_chart_destroy (chart); g_free (gift_cfg.host); g_free (gift_cfg.chart_lbl_fmt); for (i = 0; i < 2; i++) gdk_colormap_free_colors (gift_get_colormap (), &gift_cfg.transfer_color[i], 1); libgift_finish (); } static gboolean panel_expose (GtkWidget *widget, GdkEventExpose *ev, Transfer *t) { gdk_draw_drawable (widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], t->panel->pixmap, ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height); return FALSE; } static void panel_create (Transfer *t, gboolean first_create) { GkrellmStyle *style; GkrellmTextstyle *textstyle; GkrellmPiximage *img = NULL; const guint8 *pics[] = {arrow_down, arrow_up}; gchar *files[] = {"icon_down", "icon_up"}; g_assert (t); g_assert (t->visible); style = gkrellm_panel_style (style_id); textstyle = gkrellm_meter_textstyle (style_id); textstyle->color = gift_cfg.transfer_color[t->id->dir]; if (first_create) t->panel = gkrellm_panel_new0 (); t->dcl_fname = gkrellm_create_decal_text (t->panel, "Fog", textstyle, style, 14, -1, -1); t->x_scroll = chart_width; /* create the decal showing whether this transfer * is a download or an upload */ gkrellm_load_piximage_from_inline (files[t->id->dir], pics[t->id->dir], &img, PLUGIN_NAME, FALSE); g_assert (img); t->dcl_dir = gkrellm_make_scaled_decal_pixmap (t->panel, img, style, 0, 1, 1, 12, 12); gkrellm_destroy_piximage (img); gkrellm_panel_configure (t->panel, NULL, style); gkrellm_panel_create (plugin_vbox, &monitor, t->panel); if (!gift_cfg.show_panels) gkrellm_panel_hide (t->panel); if (first_create) { g_signal_connect (G_OBJECT (t->panel->drawing_area), "expose_event", G_CALLBACK (panel_expose), (gpointer) t); t->tooltip = gtk_tooltips_new (); } } static gint chart_expose (GtkWidget *widget, GdkEventExpose *ev, GkrellmChart *c) { gdk_draw_drawable (widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], c->pixmap, ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height); return FALSE; } static void chart_click (GtkWidget *widget, GdkEventButton *ev, GkrellmChart *c) { switch (ev->button) { case 1: gift_cfg.show_chart_lbl = !gift_cfg.show_chart_lbl; break; case 3: gkrellm_chartconfig_window_create (c); break; default: break; } } static void chart_draw (gchar *str) { gkrellm_draw_chartdata (chart); if (str) gkrellm_draw_chart_text (chart, style_id, str); gkrellm_draw_chart_to_screen (chart); } static void size_human (gfloat input, gchar *dest, gint size) { g_assert (dest); if (input > (1024 * 1024)) snprintf (dest, size, "%.1fG", (gfloat) input / (1024 * 1024)); else if (input > 1024) snprintf (dest, size, "%.1fM", (gfloat) input / 1024); else snprintf (dest, size, "%.1fK", (gfloat) input); } /* kinda ripped from GKrellM's disk.c. This kinda sucks, rewrite me! * fills @buf with the chart label string using the format string * @format and the transfer speed array @speed */ static void build_chart_lbl (gchar *buf, gint size, gchar *format, gchar speed[][32]) { gchar *s; gchar c[3]; gint len; if (!buf || size < 1) return; --size; *buf = 0; if (!format) return; for (s = format; *s && size > 0; ++s) { len = 1; if (*s == '$' && *(s + 1)) { strncpy(c, &s[1], 2); c[2] = 0; s += 2; if (!strcmp(c, "Sd")) len = snprintf(buf, size, "%s", speed[DIR_DOWN]); else if (!strcmp(c, "Su")) len = snprintf(buf, size, "%s", speed[DIR_UP]); else if (!strcmp(c, "Td")) len = snprintf(buf, size, "%i", stats.transfer_count[DIR_DOWN]); else if (!strcmp(c, "Tu")) len = snprintf(buf, size, "%i", stats.transfer_count[DIR_UP]); else if (!strcmp(c, "Tt")) len = snprintf(buf, size, "%i", stats.transfer_count[DIR_DOWN] + stats.transfer_count[DIR_UP]); else if (!strcmp(c, "Us")) len = snprintf(buf, size, "%lu", stats.users); else { *buf = *s; if (size > 1) { *(buf + 1) = *(s + 1); ++len; } s--; } } else *buf = *s; size -= len; buf += len; } *buf = 0; } static void chart_refresh(gfloat *speed) { gchar speed_str[2][32]; gchar str[512]; gint i; gkrellm_store_chartdata(chart, 0, (gulong) rintf(speed[DIR_DOWN]), (gulong) rintf(speed[DIR_UP])); if (gift_cfg.show_chart_lbl) { for (i = 0; i < 2; i++) size_human(speed[i], speed_str[i], sizeof(speed_str[i])); build_chart_lbl(str, sizeof(str), gift_cfg.chart_lbl_fmt, speed_str); } chart_draw(str); } static void chart_create(gboolean first_create) { gchar *label[DIR_NUM] = {"Download speed", "Upload speed"}; gint i; if (first_create) chart = gkrellm_chart_new0(); gkrellm_chart_create(plugin_vbox, &monitor, chart, &gift_cfg.chart_cfg); gkrellm_set_draw_chart_function(chart, chart_draw, NULL); for (i = 0; i < DIR_NUM; i++) { chart_data[i] = gkrellm_add_default_chartdata(chart, label[i]); gkrellm_monotonic_chartdata(chart_data[i], FALSE); gkrellm_set_chartdata_draw_style_default(chart_data[i], CHARTDATA_LINE); gkrellm_set_chartdata_flags(chart_data[i], CHARTDATA_ALLOW_HIDE); } gkrellm_alloc_chartdata(chart); if (!gift_cfg.show_chart) gkrellm_chart_hide(chart, FALSE); if (first_create) { g_signal_connect(G_OBJECT(chart->drawing_area), "expose_event", G_CALLBACK(chart_expose), (gpointer) chart); g_signal_connect(G_OBJECT(chart->drawing_area), "button_press_event", G_CALLBACK(chart_click), (gpointer) chart); } else gkrellm_refresh_chart(chart); } static gchar *locale_to_utf8(const gchar *src) { const gchar *charset; if (g_get_charset(&charset)) /* charset is UTF-8 so we don't need to convert src */ return g_strdup(src); else return g_convert_with_fallback(src, strlen(src), "UTF-8", charset, " ", NULL, NULL, NULL); } static void tooltip_update(Transfer *t) { gchar *tooltip, *file, *buf[DIR_NUM] = {"[Down] ", "[Up] "}; file = locale_to_utf8(t->dcl_fname_txt); tooltip = g_strconcat(buf[t->id->dir], file, NULL); g_free(file); gtk_tooltips_set_tip(t->tooltip, t->panel->drawing_area, tooltip, NULL); gtk_tooltips_enable(t->tooltip); g_free(tooltip); } static void panel_refresh(Transfer *t) { g_assert(t); /* update scrolling information */ t->x_scroll = (t->x_scroll > t->x_off) ? t->x_scroll - 2 : chart_width; t->dcl_fname->x_off = t->x_scroll; tooltip_update(t); /* draw the panels */ gkrellm_draw_decal_pixmap(t->panel, t->dcl_dir, 0); gkrellm_draw_decal_text(t->panel, t->dcl_fname, t->dcl_fname_txt, chart_width - t->x_scroll); gkrellm_draw_panel_layers(t->panel); } static void monitor_update() { GkrellmTicks *ticks = gkrellm_ticks(); Transfer *t; GSList *list; gfloat speed[DIR_NUM] = {0.0, 0.0}; /* total speed of all downloads and uploads */ if (ticks->ten_second_tick) gift_try_connect(); /* read from daemon twice a second */ if (!(ticks->timer_ticks % (gkrellm_update_HZ() / 2))) { if (connected) { connected = gift_daemon_read(); if (!connected) gift_monitor_disabled(); /* remove transfers etc */ } /* show chart only when we're connected to the daemon */ gift_chart_set_visibility(connected && gift_cfg.show_chart); } /* scroll the panels' decals */ for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; /* only refresh when the transfer _and_ the panel are visible */ if (t->visible && gkrellm_is_panel_visible(t->panel)) panel_refresh(t); speed[t->id->dir] += t->speed; } /* update the chart * get visible state again for safety */ if (ticks->second_tick && gkrellm_is_chart_visible(chart)) chart_refresh(speed); if (ticks->minute_tick && connected) gift_daemon_send(CMD_STATS); } static void monitor_create(GtkWidget *vbox, gint first_create) { Transfer *t; GSList *list; gkrellm_disable_plugin_connect(&monitor, gift_monitor_disabled); plugin_vbox = vbox; chart_width = gkrellm_chart_width(); if (!first_create) { for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; if (t->visible) panel_create(t, FALSE); } } else gift_try_connect(); chart_create(first_create); } GkrellmMonitor *gkrellm_init_plugin() { libgift_init("GKrellM giFT", GLOG_STDERR, NULL); /* init giFTConfig struct */ memset(&gift_cfg, 0, sizeof(gift_cfg)); gift_cfg.max_transfers[DIR_DOWN] = 5; gift_cfg.max_transfers[DIR_UP] = 5; gift_cfg.show_chart = TRUE; gift_cfg.show_panels = TRUE; gift_cfg.show_chart_lbl = TRUE; gkrellm_dup_string(&gift_cfg.chart_lbl_fmt, DEFAULT_CHART_LBL_FMT); /* try to get the target system settings from ~/.giFT/gift.conf */ gift_read_gift_config(&gift_cfg); memset(&stats, 0, sizeof(stats)); /* fill GkrellmMonitor struct */ memset(&monitor, 0, sizeof(monitor)); monitor.name = PLUGIN_NAME; monitor.create_monitor = monitor_create; monitor.update_monitor = monitor_update; monitor.create_config = gift_config_create; monitor.apply_config = gift_config_apply; monitor.save_user_config = gift_config_save; monitor.load_user_config = gift_config_load; monitor.config_keyword = PLUGIN_NAME; monitor.insert_before_id = MON_CPU; style_id = gkrellm_add_meter_style(&monitor, PLUGIN_NAME); g_atexit(cleanup); return &monitor; } void gift_transfer_add(Transfer *t) { g_assert(t); stats.transfer_count[t->id->dir]++; transfer_list = g_slist_prepend(transfer_list, t); if (t->visible) panel_create(t, TRUE); } Transfer *gift_lookup_transfer(EventID *id) { Transfer *t; GSList *list; g_assert(id); for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; if (t->id->num == id->num) return t; } return NULL; } /* a transfer with TransferDirection @dir has been removed => * look for another transfer of the same direction => * if it's invisible, make it visible */ void gift_transfer_make_visible(TransferDirection dir) { Transfer *t; GSList *list; for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; if (!t->visible && t->id->dir == dir) { t->visible = TRUE; panel_create(t, TRUE); break; } } } /* returns TRUE when the maximum of transfers with * TransferDirection @dir isn't reached yet */ gboolean gift_is_free_transfer_slot(TransferDirection dir) { Transfer *t; GSList *list; gint count = 0; for (list = transfer_list; list; list = list->next) { t = (Transfer *) list->data; if (t->visible && t->id->dir == dir) count++; } return (count < gift_cfg.max_transfers[dir]); }