diff options
Diffstat (limited to 'multiload/src')
-rw-r--r-- | multiload/src/Makefile.am | 59 | ||||
-rw-r--r-- | multiload/src/autoscaler.c | 53 | ||||
-rw-r--r-- | multiload/src/autoscaler.h | 22 | ||||
-rw-r--r-- | multiload/src/global.h | 220 | ||||
-rw-r--r-- | multiload/src/linux-proc.c | 430 | ||||
-rw-r--r-- | multiload/src/linux-proc.h | 13 | ||||
-rw-r--r-- | multiload/src/load-graph.c | 513 | ||||
-rw-r--r-- | multiload/src/load-graph.h | 24 | ||||
-rw-r--r-- | multiload/src/main.c | 584 | ||||
-rw-r--r-- | multiload/src/netspeed.c | 64 | ||||
-rw-r--r-- | multiload/src/netspeed.h | 15 | ||||
-rw-r--r-- | multiload/src/properties.c | 686 |
12 files changed, 2683 insertions, 0 deletions
diff --git a/multiload/src/Makefile.am b/multiload/src/Makefile.am new file mode 100644 index 00000000..6b42871f --- /dev/null +++ b/multiload/src/Makefile.am @@ -0,0 +1,59 @@ +NULL = + +AM_CPPFLAGS = \ + -I$(srcdir) \ + -DMULTILOAD_RESOURCE_PATH=\""/org/mate/mate-applets/multiload/"\" \ + $(MATE_APPLETS4_CFLAGS) \ + $(GTOP_APPLETS_CFLAGS) \ + $(GIO_CFLAGS) \ + ${WARN_CFLAGS} + +BUILT_SOURCES = \ + multiload-resources.c \ + multiload-resources.h \ + $(NULL) +APPLET_SOURCES = \ + global.h \ + linux-proc.h \ + load-graph.h \ + linux-proc.c \ + load-graph.c \ + main.c \ + properties.c \ + netspeed.c netspeed.h \ + autoscaler.c \ + autoscaler.h \ + $(NULL) + +APPLET_LIBS = \ + $(MATE_APPLETS4_LIBS) \ + $(GTOP_APPLETS_LIBS) \ + $(GIO_LIBS) \ + -lm + +if ENABLE_IN_PROCESS +pkglib_LTLIBRARIES = libmate-multiload-applet.la +nodist_libmate_multiload_applet_la_SOURCES = $(BUILT_SOURCES) +libmate_multiload_applet_la_SOURCES = $(APPLET_SOURCES) +libmate_multiload_applet_la_CFLAGS = $(AM_CFLAGS) +libmate_multiload_applet_la_LDFLAGS = -module -avoid-version +libmate_multiload_applet_la_LIBADD = $(APPLET_LIBS) +else !ENABLE_IN_PROCESS +libexec_PROGRAMS = mate-multiload-applet +nodist_mate_multiload_applet_SOURCES = $(BUILT_SOURCES) +mate_multiload_applet_SOURCES = $(APPLET_SOURCES) +mate_multiload_applet_CFLAGS = $(AM_CFLAGS) +mate_multiload_applet_LDADD = $(APPLET_LIBS) +endif !ENABLE_IN_PROCESS + +multiload-resources.c: $(srcdir)/../data/multiload-resources.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/../data --generate-dependencies $(srcdir)/../data/multiload-resources.gresource.xml) + $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)/../data --generate --c-name multiload $< + +multiload-resources.h: $(srcdir)/../data/multiload-resources.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/../data --generate-dependencies $(srcdir)/../data/multiload-resources.gresource.xml) + $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)/../data --generate --c-name multiload $< + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/multiload/src/autoscaler.c b/multiload/src/autoscaler.c new file mode 100644 index 00000000..fb03533f --- /dev/null +++ b/multiload/src/autoscaler.c @@ -0,0 +1,53 @@ +#include <glib.h> + +#include "autoscaler.h" + +/* i wish i could have used C99 initializers instead of writing this function */ +void +autoscaler_init (AutoScaler *that, + gint64 interval, + guint64 floor) +{ + that->update_interval = interval; + that->floor = floor; + that->max = 0; + that->count = 0; + that->last_update = 0; + that->sum = 0; + that->last_average = 0.0f; +} + +guint64 +autoscaler_get_max (AutoScaler *that, + guint64 current) +{ + gint64 now; + + that->sum += current; + that->count++; + now = g_get_monotonic_time (); + + if ((now - that->last_update) > that->update_interval) + { + float new_average = (float) that->sum / (float) that->count; + float average; + + if (new_average < that->last_average) + average = ((that->last_average * 0.5f) + new_average) / 1.5f; + else + average = new_average; + + that->max = (guint64) (average * 1.2f); + that->sum = 0; + that->count = 0; + that->last_update = now; + that->last_average = average; + } + + that->max = MAX(that->max, current); + that->max = MAX(that->max, that->floor); +#if 0 + printf("%p max = %u, current = %u, last_average = %f\n", that, that->max, current, that->last_average); +#endif + return that->max; +} diff --git a/multiload/src/autoscaler.h b/multiload/src/autoscaler.h new file mode 100644 index 00000000..ef4e0946 --- /dev/null +++ b/multiload/src/autoscaler.h @@ -0,0 +1,22 @@ +#ifndef MATE_APPLETS_MULTILOAD_AUTOSCALER_H +#define MATE_APPLETS_MULTILOAD_AUTOSCALER_H + +#include <glib.h> + +typedef struct _AutoScaler AutoScaler; + +struct _AutoScaler +{ + gint64 update_interval; + gint64 last_update; + guint64 floor; + guint64 max; + guint64 count; + guint64 sum; + float last_average; +}; + +G_GNUC_INTERNAL void autoscaler_init (AutoScaler *that, gint64 interval, guint64 floor); +G_GNUC_INTERNAL guint64 autoscaler_get_max (AutoScaler *that, guint64 current); + +#endif /* MATE_APPLETS_MULTILOAD_AUTOSCALER_H */ diff --git a/multiload/src/global.h b/multiload/src/global.h new file mode 100644 index 00000000..f40f194a --- /dev/null +++ b/multiload/src/global.h @@ -0,0 +1,220 @@ +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtk.h> +#include <gio/gio.h> +#include <mate-panel-applet.h> + +G_BEGIN_DECLS + +#define KEY_CPULOAD_USR_COLOR "cpuload-color0" +#define KEY_CPULOAD_SYS_COLOR "cpuload-color1" +#define KEY_CPULOAD_NICE_COLOR "cpuload-color2" +#define KEY_CPULOAD_IOWAIT_COLOR "cpuload-color3" +#define KEY_CPULOAD_IDLE_COLOR "cpuload-color4" +#define KEY_MEMLOAD_USER_COLOR "memload-color0" +#define KEY_MEMLOAD_SHARED_COLOR "memload-color1" +#define KEY_MEMLOAD_BUFFER_COLOR "memload-color2" +#define KEY_MEMLOAD_CACHED_COLOR "memload-color3" +#define KEY_MEMLOAD_FREE_COLOR "memload-color4" +#define KEY_NETLOAD2_IN_COLOR "netload2-color0" +#define KEY_NETLOAD2_OUT_COLOR "netload2-color1" +#define KEY_NETLOAD2_LOOPBACK_COLOR "netload2-color2" +#define KEY_NETLOAD2_BACKGROUND_COLOR "netload2-color3" +#define KEY_NETLOAD2_GRIDLINE_COLOR "netload2-color4" +#define KEY_NETLOAD2_INDICATOR_COLOR "netload2-color5" +#define KEY_SWAPLOAD_USED_COLOR "swapload-color0" +#define KEY_SWAPLOAD_FREE_COLOR "swapload-color1" +#define KEY_LOADAVG_AVERAGE_COLOR "loadavg-color0" +#define KEY_LOADAVG_BACKGROUND_COLOR "loadavg-color1" +#define KEY_LOADAVG_GRIDLINE_COLOR "loadavg-color2" +#define KEY_DISKLOAD_READ_COLOR "diskload-color0" +#define KEY_DISKLOAD_WRITE_COLOR "diskload-color1" +#define KEY_DISKLOAD_FREE_COLOR "diskload-color2" + +#define KEY_NET_THRESHOLD1 "netthreshold1" +#define KEY_NET_THRESHOLD2 "netthreshold2" +#define KEY_NET_THRESHOLD3 "netthreshold3" +#define MIN_NET_THRESHOLD1 10 +#define MIN_NET_THRESHOLD2 11 +#define MIN_NET_THRESHOLD3 12 +#define MAX_NET_THRESHOLD1 999999998 +#define MAX_NET_THRESHOLD2 999999999 +#define MAX_NET_THRESHOLD3 1000000000 + +#define VIEW_CPULOAD_KEY "view-cpuload" +#define VIEW_MEMLOAD_KEY "view-memload" +#define VIEW_NETLOAD_KEY "view-netload" +#define VIEW_SWAPLOAD_KEY "view-swapload" +#define VIEW_LOADAVG_KEY "view-loadavg" +#define VIEW_DISKLOAD_KEY "view-diskload" + +#define DISKLOAD_NVME_KEY "diskload-nvme-diskstats" + +#define REFRESH_RATE_KEY "speed" +#define REFRESH_RATE_MIN 50 +#define REFRESH_RATE_MAX 60000 + +#define GRAPH_SIZE_KEY "size" +#define GRAPH_SIZE_MIN 10 +#define GRAPH_SIZE_MAX 1000 + +typedef struct _MultiloadApplet MultiloadApplet; +typedef struct _LoadGraph LoadGraph; +typedef void (*LoadGraphDataFunc) (guint64, guint64 [], LoadGraph *); + +#include "netspeed.h" + +typedef enum { + graph_cpuload = 0, + graph_memload, + graph_netload2, + graph_swapload, + graph_loadavg, + graph_diskload, + graph_n, +} E_graph; + +typedef enum { + cpuload_usr = 0, + cpuload_sys, + cpuload_nice, + cpuload_iowait, + cpuload_free, + cpuload_n +} E_cpuload; + +typedef enum { + memload_user = 0, + memload_shared, + memload_buffer, + memload_cached, + memload_free, + memload_n +} E_memload; + +typedef enum { + netload2_in = 0, + netload2_out, + netload2_loopback, + netload2_background, + netload2_gridline, + netload2_indicator, + netload2_n +} E_netload2; + +typedef enum { + swapload_used = 0, + swapload_free, + swapload_n +} E_swapload; + +typedef enum { + loadavg_average = 0, + loadavg_background, + loadavg_gridline, + loadavg_n +} E_loadavg; + +typedef enum { + diskload_read = 0, + diskload_write, + diskload_free, + diskload_n +} E_diskload; + +struct _LoadGraph { + MultiloadApplet *multiload; + + guint n; + gint id; + guint speed, size; + guint orient, pixel_size; + gsize draw_width; + guint64 draw_height; + LoadGraphDataFunc get_data; + + guint allocated; + + GdkRGBA *colors; + guint64 **data; + guint64 *pos; + + GtkWidget *main_widget; + GtkWidget *frame, *box, *disp; + cairo_surface_t *surface; + int timer_index; + + gboolean visible; + gboolean tooltip_update; + const gchar *name; +}; + +struct _MultiloadApplet +{ + MatePanelApplet *applet; + + GSettings *settings; + + LoadGraph *graphs [graph_n]; + + GtkWidget *box; + + gboolean view_cpuload; + gboolean view_memload; + gboolean view_netload; + gboolean view_swapload; + gboolean view_loadavg; + gboolean view_diskload; + + GtkWidget *about_dialog; + GtkWidget *check_boxes [graph_n]; + GtkWidget *prop_dialog; + GtkWidget *notebook; + gint last_clicked; + + float cpu_used_ratio; + guint64 cpu_time [cpuload_n]; + guint64 cpu_last [cpuload_n]; + gboolean cpu_initialized; + + double loadavg1; + + guint64 memload_user; + guint64 memload_cache; + guint64 memload_total; + + float swapload_used_ratio; + + float diskload_used_ratio; + gboolean nvme_diskstats; + + NetSpeed *netspeed_in; + NetSpeed *netspeed_out; + guint64 net_threshold1; + guint64 net_threshold2; + guint64 net_threshold3; +}; + +#include "load-graph.h" +#include "linux-proc.h" + +/* show properties dialog */ +G_GNUC_INTERNAL void +multiload_properties_cb (GtkAction *action, + MultiloadApplet *ma); + +/* remove the old graphs and rebuild them */ +G_GNUC_INTERNAL void +multiload_applet_refresh (MultiloadApplet *ma); + +/* update the tooltip to the graph's current "used" percentage */ +G_GNUC_INTERNAL void +multiload_applet_tooltip_update (LoadGraph *g); + +G_END_DECLS + +#endif diff --git a/multiload/src/linux-proc.c b/multiload/src/linux-proc.c new file mode 100644 index 00000000..f5549f67 --- /dev/null +++ b/multiload/src/linux-proc.c @@ -0,0 +1,430 @@ +/* From wmload.c, v0.9.2, licensed under the GPL. */ +#include <config.h> +#include <sys/types.h> +#include <sys/statvfs.h> +#include <fcntl.h> +#include <unistd.h> + +#include <glibtop.h> +#include <glibtop/cpu.h> +#include <glibtop/mem.h> +#include <glibtop/swap.h> +#include <glibtop/loadavg.h> +#include <glibtop/netload.h> +#include <glibtop/netlist.h> +#include <glibtop/mountlist.h> +#include <glibtop/fsusage.h> + +#include "linux-proc.h" +#include "autoscaler.h" + +static const unsigned needed_cpu_flags = +(1 << GLIBTOP_CPU_USER) + +(1 << GLIBTOP_CPU_IDLE) + +(1 << GLIBTOP_CPU_SYS) + +(1 << GLIBTOP_CPU_NICE); + +#if 0 +static const unsigned needed_page_flags = +(1 << GLIBTOP_SWAP_PAGEIN) + +(1 << GLIBTOP_SWAP_PAGEOUT); +#endif + +static const unsigned needed_mem_flags = +(1 << GLIBTOP_MEM_USED) + +(1 << GLIBTOP_MEM_FREE); + +static const unsigned needed_swap_flags = +(1 << GLIBTOP_SWAP_USED) + +(1 << GLIBTOP_SWAP_FREE); + +static const unsigned needed_loadavg_flags = +(1 << GLIBTOP_LOADAVG_LOADAVG); + +static const unsigned needed_netload_flags = +(1 << GLIBTOP_NETLOAD_IF_FLAGS) + +(1 << GLIBTOP_NETLOAD_BYTES_TOTAL); + +void +GetLoad (guint64 Maximum, + guint64 data [cpuload_n], + LoadGraph *g) +{ + MultiloadApplet *multiload; + glibtop_cpu cpu; + guint64 cpu_aux [cpuload_n], used = 0, total = 0; + guint64 current_scaled, used_scaled = 0; + unsigned i; + + glibtop_get_cpu (&cpu); + + g_return_if_fail ((cpu.flags & needed_cpu_flags) == needed_cpu_flags); + + multiload = g->multiload; + + multiload->cpu_time [cpuload_usr] = cpu.user; + multiload->cpu_time [cpuload_nice] = cpu.nice; + multiload->cpu_time [cpuload_sys] = cpu.sys; + multiload->cpu_time [cpuload_iowait] = cpu.iowait + cpu.irq + cpu.softirq; + multiload->cpu_time [cpuload_free] = cpu.idle; + + if (!multiload->cpu_initialized) { + memcpy (multiload->cpu_last, multiload->cpu_time, sizeof (multiload->cpu_last)); + multiload->cpu_initialized = TRUE; + } + + for (i = 0; i < cpuload_n; i++) { + cpu_aux [i] = multiload->cpu_time [i] - multiload->cpu_last [i]; + total += cpu_aux [i]; + } + + for (i = 0; i < cpuload_free; i++) { + used += cpu_aux [i]; + current_scaled = (guint64) ((float)(cpu_aux [i] * Maximum) / (float)total); + used_scaled += current_scaled; + data [i] = current_scaled; + } + data [cpuload_free] = Maximum - used_scaled; + + multiload->cpu_used_ratio = (float)(used) / (float)total; + + memcpy (multiload->cpu_last, multiload->cpu_time, sizeof multiload->cpu_last); +} + +void +GetDiskLoad (guint64 Maximum, + guint64 data [diskload_n], + LoadGraph *g) +{ + static gboolean first_call = TRUE; + static guint64 lastread = 0, lastwrite = 0; + static AutoScaler scaler; + + guint64 max; + guint64 read, write; + guint64 readdiff, writediff; + guint i; + + MultiloadApplet *multiload; + + multiload = g->multiload; + + if(first_call) + { + autoscaler_init (&scaler, g->speed, 500); + } + + read = write = 0; + + if (multiload->nvme_diskstats) + { + FILE *fdr; + char line[255]; + guint64 s_read, s_write; + + fdr = fopen("/proc/diskstats", "r"); + if (!fdr) + { + multiload->nvme_diskstats = FALSE; + g_settings_set_boolean (multiload->settings, "diskload-nvme-diskstats", FALSE); + return; + } + + while (fgets(line, 255, fdr)) + { + /* Match main device, rather than individual partitions (e.g. nvme0n1) */ + if (!g_regex_match_simple("\\snvme\\d+\\w+\\d+\\s", line, 0, 0)) + { + continue; + } + + /* + 6 - sectors read + 10 - sectors written + */ + if (sscanf(line, "%*d %*d %*s %*d %*d %ld %*d %*d %*d %ld", &s_read, &s_write) == 2) + { + read += 512 * s_read; + write += 512 * s_write; + } + } + fclose(fdr); + } + else + { + glibtop_mountlist mountlist; + glibtop_mountentry *mountentries; + + mountentries = glibtop_get_mountlist (&mountlist, FALSE); + + for (i = 0; i < mountlist.number; i++) + { + struct statvfs statresult; + glibtop_fsusage fsusage; + + if (strstr (mountentries[i].devname, "/dev/") == NULL) + continue; + + if (strstr (mountentries[i].mountdir, "/media/") != NULL) + continue; + + if (statvfs (mountentries[i].mountdir, &statresult) < 0) + { + g_debug ("Failed to get statistics for mount entry: %s. Reason: %s. Skipping entry.", + mountentries[i].mountdir, strerror(errno)); + continue; + } + + glibtop_get_fsusage(&fsusage, mountentries[i].mountdir); + read += fsusage.read; + write += fsusage.write; + } + + g_free(mountentries); + } + + readdiff = read - lastread; + writediff = write - lastwrite; + + lastread = read; + lastwrite = write; + + if(first_call) + { + first_call = FALSE; + memset(data, 0, 3 * sizeof data[0]); + return; + } + + max = autoscaler_get_max(&scaler, readdiff + writediff); + + multiload->diskload_used_ratio = (float)(readdiff + writediff) / (float)max; + + data [diskload_read] = (guint64) ((float)Maximum * (float)readdiff / (float)max); + data [diskload_write] = (guint64) ((float)Maximum * (float)writediff / (float)max); + data [diskload_free] = Maximum - (data [0] + data[1]); +} + +/* GNU/Linux: + * aux [memload_user] = (mem.total - mem.free) - (mem.cached + mem.buffer) + * aux [memload_shared] = mem.shared; + * aux [memload_cached] = mem.cached - mem.shared; + * aux [memload_buffer] = mem.buffer; + * + * Other operating systems: + * aux [memload_user] = mem.user; + * aux [memload_shared] = mem.shared; + * aux [memload_cached] = mem.cached; + * aux [memload_buffer] = mem.buffer; + */ +void +GetMemory (guint64 Maximum, + guint64 data [memload_n], + LoadGraph *g) +{ + MultiloadApplet *multiload; + glibtop_mem mem; + guint64 aux [memload_n], cache = 0; + guint64 current_scaled, used_scaled = 0; + int i; + + glibtop_get_mem (&mem); + + g_return_if_fail ((mem.flags & needed_mem_flags) == needed_mem_flags); + +#ifndef __linux__ + aux [memload_user] = mem.user; + aux [memload_cached] = mem.cached; +#else + aux [memload_user] = mem.total - mem.free - mem.buffer - mem.cached;; + aux [memload_cached] = mem.cached - mem.shared; +#endif /* __linux__ */ + aux [memload_shared] = mem.shared; + aux [memload_buffer] = mem.buffer; + + for (i = 0; i < memload_free; i++) { + current_scaled = (guint64) ((float)(aux [i] * Maximum) / (float)mem.total); + if (i != memload_user) { + cache += aux [i]; + } + used_scaled += current_scaled; + data [i] = current_scaled; + } + data [memload_free] = MAX (Maximum - used_scaled, 0); + + multiload = g->multiload; + multiload->memload_user = aux [memload_user]; + multiload->memload_cache = cache; + multiload->memload_total = mem.total; +} + +void +GetSwap (guint64 Maximum, + guint64 data [swapload_n], + LoadGraph *g) +{ + guint64 used; + MultiloadApplet *multiload; + glibtop_swap swap; + + glibtop_get_swap (&swap); + g_return_if_fail ((swap.flags & needed_swap_flags) == needed_swap_flags); + + multiload = g->multiload; + + if (swap.total == 0) { + used = 0; + multiload->swapload_used_ratio = 0.0f; + } + else { + float ratio; + + ratio = (float)swap.used / (float)swap.total; + used = (guint64) ((float) Maximum * ratio); + multiload->swapload_used_ratio = ratio; + } + + data [0] = used; + data [1] = Maximum - used; +} + +void +GetLoadAvg (guint64 Maximum, + guint64 data [2], + LoadGraph *g) +{ + glibtop_loadavg loadavg; + MultiloadApplet *multiload; + + glibtop_get_loadavg (&loadavg); + + g_return_if_fail ((loadavg.flags & needed_loadavg_flags) == needed_loadavg_flags); + + multiload = g->multiload; + multiload->loadavg1 = loadavg.loadavg[0]; + + data [0] = (guint64) ((float) Maximum * loadavg.loadavg[0]); + data [1] = Maximum - data[0]; +} + +/* + * Return true if a network device (identified by its name) is virtual + * (ie: not corresponding to a physical device). In case it is a physical + * device or unknown, returns false. + */ +static gboolean +is_net_device_virtual(char *device) +{ + /* + * There is not definitive way to find out. On some systems (Linux + * kernels ≳ 2.19 without option SYSFS_DEPRECATED), there exist a + * directory /sys/devices/virtual/net which only contains virtual + * devices. It's also possible to detect by the fact that virtual + * devices do not have a symlink "device" in + * /sys/class/net/name-of-dev/ . This second method is more complex + * but more reliable. + */ + gboolean ret = FALSE; + char *path = malloc (strlen (device) + strlen ("/sys/class/net//device") + 1); + + if (path == NULL) + return FALSE; + + /* Check if /sys/class/net/name-of-dev/ exists (may be old linux kernel + * or not linux at all). */ + do { + if (sprintf(path, "/sys/class/net/%s", device) < 0) + break; + if (access(path, F_OK) != 0) + break; /* unknown */ + + if (sprintf(path, "/sys/class/net/%s/device", device) < 0) + break; + if (access(path, F_OK) != 0) + ret = TRUE; + } while (0); + + free (path); + return ret; +} + +void +GetNet (guint64 Maximum, + guint64 data [4], + LoadGraph *g) +{ + enum Types { + IN_COUNT = 0, + OUT_COUNT = 1, + LOCAL_COUNT = 2, + COUNT_TYPES = 3 + }; + + static int ticks = 0; + static guint64 past[COUNT_TYPES] = {0}; + + guint64 present[COUNT_TYPES] = {0}; + + guint i; + gchar **devices; + glibtop_netlist netlist; + + MultiloadApplet *multiload; + + multiload = g->multiload; + devices = glibtop_get_netlist(&netlist); + + for(i = 0; i < netlist.number; ++i) + { + glibtop_netload netload; + + glibtop_get_netload(&netload, devices[i]); + + g_return_if_fail((netload.flags & needed_netload_flags) == needed_netload_flags); + + if (!(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP))) + continue; + + if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) { + /* for loopback in and out are identical, so only count in */ + present[LOCAL_COUNT] += netload.bytes_in; + continue; + } + + /* + * Do not include virtual devices (VPN, PPPOE...) to avoid + * counting the same throughput several times. + */ + if (is_net_device_virtual(devices[i])) + continue; + + present[IN_COUNT] += netload.bytes_in; + present[OUT_COUNT] += netload.bytes_out; + } + + g_strfreev(devices); + netspeed_add (multiload->netspeed_in, present[IN_COUNT]); + netspeed_add (multiload->netspeed_out, present[OUT_COUNT]); + + if(ticks < 2) /* avoid initial spike */ + { + ticks++; + memset(data, 0, (COUNT_TYPES + 1) * sizeof data[0]); + } + else + { + data[COUNT_TYPES] = 0; + float seconds = (float) g->speed / 1000.0f; + for (i = 0; i < COUNT_TYPES; i++) + { + /* protect against weirdness */ + if (present[i] >= past[i]) + data[i] = (guint) ((float) (present[i] - past[i]) / seconds); + else + data[i] = 0; + data[COUNT_TYPES] += data[i]; + } + } + + memcpy(past, present, sizeof past); +} diff --git a/multiload/src/linux-proc.h b/multiload/src/linux-proc.h new file mode 100644 index 00000000..fb18ef59 --- /dev/null +++ b/multiload/src/linux-proc.h @@ -0,0 +1,13 @@ +#ifndef LINUX_PROC_H__ +#define LINUX_PROC_H__ + +#include <load-graph.h> + +G_GNUC_INTERNAL void GetLoad (guint64 Maximum, guint64 data [cpuload_n], LoadGraph *g); +G_GNUC_INTERNAL void GetDiskLoad (guint64 Maximum, guint64 data [diskload_n], LoadGraph *g); +G_GNUC_INTERNAL void GetMemory (guint64 Maximum, guint64 data [memload_n], LoadGraph *g); +G_GNUC_INTERNAL void GetSwap (guint64 Maximum, guint64 data [swapload_n], LoadGraph *g); +G_GNUC_INTERNAL void GetLoadAvg (guint64 Maximum, guint64 data [2], LoadGraph *g); +G_GNUC_INTERNAL void GetNet (guint64 Maximum, guint64 data [4], LoadGraph *g); + +#endif diff --git a/multiload/src/load-graph.c b/multiload/src/load-graph.c new file mode 100644 index 00000000..e8ca8bd3 --- /dev/null +++ b/multiload/src/load-graph.c @@ -0,0 +1,513 @@ +#include <config.h> +#include <stdio.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <dirent.h> +#include <string.h> +#include <time.h> +#include <glib.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include <gio/gio.h> +#include <mate-panel-applet.h> +#include <mate-panel-applet-gsettings.h> +#include <math.h> + +#include "global.h" + +/* + Shifts data right + + data[i+1] = data[i] + + data[i] are int*, so we just move the pointer, not the data. + But moving data loses data[n-1], so we save data[n-1] and reuse + it as new data[0]. In fact, we rotate data[]. + +*/ + +static void +shift_right(LoadGraph *g) +{ + guint64 *last_data; + gsize i; + + /* data[g->draw_width - 1] becomes data[0] */ + last_data = g->data[g->draw_width - 1]; + + /* data[i+1] = data[i] */ + for (i = g->draw_width - 1; i != 0; --i) + g->data[i] = g->data[i - 1]; + + g->data[0] = last_data; +} + +/* Redraws the backing pixmap for the load graph and updates the window */ +static void +load_graph_draw (LoadGraph *g) +{ + guint i, j, k; + cairo_t *cr; + MultiloadApplet *multiload; + + multiload = g->multiload; + + /* we might get called before the configure event so that + * g->disp->allocation may not have the correct size + * (after the user resized the applet in the prop dialog). */ + + if (!g->surface) + g->surface = gdk_window_create_similar_surface (gtk_widget_get_window (g->disp), + CAIRO_CONTENT_COLOR, + (int) g->draw_width, + (int) g->draw_height); + + cr = cairo_create (g->surface); + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + /* all graphs except Load and Net go this path */ + switch (g->id) { + + /* This is for network graph */ + case graph_netload2: { + guint64 maxnet = 1; + guint64 segments = 1; + guint64 net_threshold; + guint level = 0; + double ratio; + double spacing; + + for (i = 0; i < g->draw_width; i++) + { + g->pos [i] = g->draw_height - 1; + if (g->data[i][3] > maxnet) + maxnet = g->data[i][3]; + } + //printf("max = %d ", maxnet); + if (maxnet > multiload->net_threshold3) { + net_threshold = multiload->net_threshold3; + level = 3; + } + else + if (maxnet > multiload->net_threshold2) { + net_threshold = multiload->net_threshold2; + level = 2; + } + else { + net_threshold = multiload->net_threshold1; + if (maxnet >= multiload->net_threshold1) + level = 1; + } + + //printf("level %d maxnet = %d ", level, maxnet); + maxnet = maxnet/net_threshold; + segments = MAX (maxnet+1,1); + ratio = (double) g->draw_height / (double) (net_threshold*segments); + //printf("segments %d ratio = %f t1=%ld t2=%ld t3=%ld t=%ld\n", segments, ratio, multiload->net_threshold1, multiload->net_threshold2, multiload->net_threshold3, multiload->net_threshold); + + for (j = 0; j < g->n-1; j++) + { + gdk_cairo_set_source_rgba (cr, &(g->colors [j])); + + for (i = 0; i < g->draw_width; i++) + { + double x = (double) (g->draw_width - i) - 0.5; + cairo_move_to (cr, x, (double) g->pos[i] + 0.5); + cairo_line_to (cr, x, (double) g->pos[i] - 0.5 - (((double) g->data [i][j] * ratio))); + g->pos [i] -= (guint64) ((double) g->data [i][j] * ratio); + } + cairo_stroke (cr); + } + + for (j = g->n-1; j < g->n; j++) + { + gdk_cairo_set_source_rgba (cr, &(g->colors [j])); + for (i = 0; i < g->draw_width; i++) + { + double x = (double) (g->draw_width - i) - 0.5; + cairo_move_to (cr, x, (double) g->pos[i] + 0.5); + cairo_line_to (cr, x, 0.5); + } + cairo_stroke (cr); + } + + /* draw grid lines if needed */ + gdk_cairo_set_source_rgba (cr, &(g->colors [4])); + for (k = 0; k < segments -1; k++) + { + spacing = ((double) g->draw_height / (double) segments) * (k+1); + cairo_move_to (cr, 0.5, spacing); + cairo_line_to (cr, (double) g->draw_width - 0.5, spacing); + } + cairo_stroke (cr); + /* draw indicator if needed */ + if (level > 0) + { + gdk_cairo_set_source_rgba (cr, &(g->colors [5])); + for (k = 0; k< level; k++ ) + cairo_rectangle (cr, + 0.5, (double) k * 2.0 * (double) g->draw_height / 5.0, + 5.0, (double) g->draw_height / 5.0); + cairo_fill(cr); + } + cairo_stroke (cr); + break; + } + + /* this is Load graph */ + case graph_loadavg: { + double load; + guint64 maxload = 1; + for (i = 0; i < g->draw_width; i++) + { + g->pos [i] = g->draw_height - 1; + /* find maximum value */ + if (g->data[i][0] > maxload) + maxload = g->data[i][0]; + } + load = ceil ((double) maxload / (double) g->draw_height) + 1.0; + + for (j = 0; j < g->n; j++) + { + gdk_cairo_set_source_rgba (cr, &(g->colors [j])); + + for (i = 0; i < g->draw_width; i++) + { + double x = (double) (g->draw_width - i) - 0.5; + cairo_move_to (cr, x, (double) g->pos[i] + 0.5); + if (j == 0) + { + cairo_line_to (cr, x, (double) g->pos[i] - (((double) g->data [i][j] - 0.5)/load)); + } + else + { + cairo_line_to (cr, x, 0.5); + } + g->pos [i] -= (guint64) ((double) g->data [i][j] / load); + } + cairo_stroke (cr); + } + + /* draw grid lines in Load graph if needed */ + gdk_cairo_set_source_rgba (cr, &(g->colors [2])); + + double spacing; + for (k = 0; k < load - 1; k++) + { + spacing = ((double) g->draw_height/load) * (k+1); + cairo_move_to (cr, 0.5, spacing); + cairo_line_to (cr, (double) g->draw_width - 0.5, spacing); + } + + cairo_stroke (cr); + break; + } + + default: + for (i = 0; i < g->draw_width; i++) + g->pos [i] = g->draw_height - 1; + + for (j = 0; j < g->n; j++) + { + gdk_cairo_set_source_rgba (cr, &(g->colors [j])); + + for (i = 0; i < g->draw_width; i++) + { + if (g->data [i][j] != 0) + { + double x = (double) (g->draw_width - i) - 0.5; + cairo_move_to (cr, x, (double) g->pos[i] + 0.5); + cairo_line_to (cr, x, (double) g->pos[i] - (double) g->data [i][j] - 0.5); + } + g->pos [i] -= g->data [i][j]; + } + cairo_stroke (cr); + } + } + + gtk_widget_queue_draw (g->disp); + + cairo_destroy (cr); +} + +/* Updates the load graph when the timeout expires */ +static gboolean +load_graph_update (LoadGraph *g) +{ + if (g->data == NULL) + return TRUE; + + shift_right(g); + + if (g->tooltip_update) + multiload_applet_tooltip_update (g); + + g->get_data (g->draw_height, g->data [0], g); + + load_graph_draw (g); + return TRUE; +} + +void +load_graph_unalloc (LoadGraph *g) +{ + gsize i; + + if (!g->allocated) + return; + + for (i = 0; i < g->draw_width; i++) + { + g_free (g->data [i]); + } + + g_free (g->data); + g_free (g->pos); + + g->pos = NULL; + g->data = NULL; + + g->size = CLAMP (g_settings_get_uint (g->multiload->settings, GRAPH_SIZE_KEY), + GRAPH_SIZE_MIN, + GRAPH_SIZE_MAX); + + if (g->surface) { + cairo_surface_destroy (g->surface); + g->surface = NULL; + } + + g->allocated = FALSE; +} + +static void +load_graph_alloc (LoadGraph *g) +{ + gsize i; + gsize data_size; + + if (g->allocated) + return; + + g->data = g_new0 (guint64 *, g->draw_width); + g->pos = g_new0 (guint64, g->draw_width); + + data_size = sizeof (guint64) * g->n; + + for (i = 0; i < g->draw_width; i++) { + g->data [i] = g_malloc0 (data_size); + } + + g->allocated = TRUE; +} + +static gint +load_graph_configure (GtkWidget *widget, GdkEventConfigure *event, + gpointer data_ptr) +{ + GtkAllocation allocation; + LoadGraph *c = (LoadGraph *) data_ptr; + + load_graph_unalloc (c); + + gtk_widget_get_allocation (c->disp, &allocation); + + c->draw_width = (gsize) allocation.width; + c->draw_height = (guint64) allocation.height; + c->draw_width = MAX (c->draw_width, 1); + c->draw_height = MAX (c->draw_height, 1); + + load_graph_alloc (c); + + if (!c->surface) + c->surface = gdk_window_create_similar_surface (gtk_widget_get_window (c->disp), + CAIRO_CONTENT_COLOR, + (int) c->draw_width, + (int) c->draw_height); + gtk_widget_queue_draw (widget); + + return TRUE; +} + +static gint +load_graph_expose (GtkWidget *widget, + cairo_t *cr, + gpointer data_ptr) +{ + LoadGraph *g = (LoadGraph *) data_ptr; + + cairo_set_source_surface (cr, g->surface, 0, 0); + cairo_paint (cr); + + return FALSE; +} + +static void +load_graph_destroy (GtkWidget *widget, gpointer data_ptr) +{ + LoadGraph *g = (LoadGraph *) data_ptr; + + load_graph_stop (g); + + gtk_widget_destroy(widget); +} + +static gboolean +load_graph_clicked (GtkWidget *widget, GdkEventButton *event, LoadGraph *load) +{ + load->multiload->last_clicked = load->id; + + return FALSE; +} + +static gboolean +load_graph_enter_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + LoadGraph *graph; + graph = (LoadGraph *)data; + + graph->tooltip_update = TRUE; + multiload_applet_tooltip_update(graph); + + return TRUE; +} + +static gboolean +load_graph_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + LoadGraph *graph; + graph = (LoadGraph *)data; + + graph->tooltip_update = FALSE; + + return TRUE; +} + +static void +load_graph_load_config (LoadGraph *g) +{ + gchar *name, *temp; + guint i; + + if (!g->colors) + g->colors = g_new0(GdkRGBA, g->n); + + for (i = 0; i < g->n; i++) + { + name = g_strdup_printf ("%s-color%u", g->name, i); + temp = g_settings_get_string(g->multiload->settings, name); + if (!temp) + temp = g_strdup ("#000000"); + gdk_rgba_parse(&(g->colors[i]), temp); + g_free(temp); + g_free(name); + } +} + +LoadGraph * +load_graph_new (MultiloadApplet *ma, guint n, const gchar *label, + gint id, guint speed, guint size, gboolean visible, + const gchar *name, LoadGraphDataFunc get_data) +{ + LoadGraph *g; + MatePanelAppletOrient orient; + + g = g_new0 (LoadGraph, 1); + g->visible = visible; + g->name = name; + g->n = n; + g->id = id; + g->speed = speed; + g->size = size; + g->pixel_size = mate_panel_applet_get_size (ma->applet); + g->tooltip_update = FALSE; + g->multiload = ma; + + g->main_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + g->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + orient = mate_panel_applet_get_orient (g->multiload->applet); + switch (orient) + { + case MATE_PANEL_APPLET_ORIENT_UP: + case MATE_PANEL_APPLET_ORIENT_DOWN: + { + g->orient = FALSE; + break; + } + case MATE_PANEL_APPLET_ORIENT_LEFT: + case MATE_PANEL_APPLET_ORIENT_RIGHT: + { + g->orient = TRUE; + break; + } + default: + g_assert_not_reached (); + } + + g->frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (g->frame), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (g->frame), g->box); + gtk_box_pack_start (GTK_BOX (g->main_widget), g->frame, TRUE, TRUE, 0); + + load_graph_load_config (g); + + g->get_data = get_data; + + g->timer_index = -1; + + if (g->orient) + gtk_widget_set_size_request (g->main_widget, -1, (gint) g->size); + else + gtk_widget_set_size_request (g->main_widget, (gint) g->size, -1); + + g->disp = gtk_drawing_area_new (); + gtk_widget_set_events (g->disp, GDK_EXPOSURE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK); + + g_signal_connect (g->disp, "draw", + G_CALLBACK (load_graph_expose), g); + g_signal_connect (g->disp, "configure-event", + G_CALLBACK (load_graph_configure), g); + g_signal_connect (g->disp, "destroy", + G_CALLBACK (load_graph_destroy), g); + g_signal_connect (g->disp, "button-press-event", + G_CALLBACK (load_graph_clicked), g); + g_signal_connect (g->disp, "enter-notify-event", + G_CALLBACK(load_graph_enter_cb), g); + g_signal_connect (g->disp, "leave-notify-event", + G_CALLBACK(load_graph_leave_cb), g); + + gtk_box_pack_start (GTK_BOX (g->box), g->disp, TRUE, TRUE, 0); + gtk_widget_show_all(g->box); + + return g; +} + +void +load_graph_start (LoadGraph *g) +{ + guint event_source_id; + + if (g->timer_index != -1) + g_source_remove ((guint) g->timer_index); + + event_source_id = g_timeout_add (g->speed, + (GSourceFunc) load_graph_update, g); + + g->timer_index = (gint) event_source_id; +} + +void +load_graph_stop (LoadGraph *g) +{ + if (g->timer_index != -1) + g_source_remove ((guint) g->timer_index); + + g->timer_index = -1; +} diff --git a/multiload/src/load-graph.h b/multiload/src/load-graph.h new file mode 100644 index 00000000..1f13a8d0 --- /dev/null +++ b/multiload/src/load-graph.h @@ -0,0 +1,24 @@ +#ifndef LOAD_GRAPH_H__ +#define LOAD_GRAPH_H__ + +#include "global.h" + +/* Create new load graph. */ +G_GNUC_INTERNAL LoadGraph * +load_graph_new (MultiloadApplet *multiload, guint n, const gchar *label, + gint id, guint speed, guint size, gboolean visible, + const gchar *name, LoadGraphDataFunc get_data); + +/* Start load graph. */ +G_GNUC_INTERNAL void +load_graph_start (LoadGraph *g); + +/* Stop load graph. */ +G_GNUC_INTERNAL void +load_graph_stop (LoadGraph *g); + +/* free load graph */ +G_GNUC_INTERNAL void +load_graph_unalloc (LoadGraph *g); + +#endif diff --git a/multiload/src/main.c b/multiload/src/main.c new file mode 100644 index 00000000..9439b320 --- /dev/null +++ b/multiload/src/main.c @@ -0,0 +1,584 @@ +/* MATE multiload panel applet + * (C) 1997 The Free Software Foundation + * + * Authors: Tim P. Gerla + * Martin Baulig + * Todd Kulesza + * + * With code from wmload.c, v0.9.2, apparently by Ryan Land, [email protected]. + * + */ + +#include <config.h> +#include <stdio.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <dirent.h> +#include <string.h> +#include <time.h> + +#include <glibtop.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> +#include <mate-panel-applet.h> +#include <mate-panel-applet-gsettings.h> + +#include "global.h" + +static void +about_cb (GtkAction *action, + MultiloadApplet *ma) +{ + const gchar * const authors[] = + { + "Martin Baulig <[email protected]>", + "Todd Kulesza <[email protected]>", + "Benoît Dejean <[email protected]>", + "Davyd Madeley <[email protected]>", + NULL + }; + + const gchar* documenters[] = + { + "Chee Bin HOH <[email protected]>", + N_("Sun GNOME Documentation Team <[email protected]>"), + N_("MATE Documentation Team"), + NULL + }; + +#ifdef ENABLE_NLS + const char **p; + for (p = documenters; *p; ++p) + *p = _(*p); +#endif + + gtk_show_about_dialog (NULL, + "title", _("About System Monitor"), + "version", VERSION, + "copyright", _("Copyright \xc2\xa9 1999-2005 Free Software Foundation and others\n" + "Copyright \xc2\xa9 2012-2021 MATE developers"), + "comments", _("A system load monitor capable of displaying graphs " + "for CPU, ram, and swap space use, plus network " + "traffic."), + "authors", authors, + "documenters", documenters, + "translator-credits", _("translator-credits"), + "logo-icon-name", "utilities-system-monitor", + NULL); +} + +static void +help_cb (GtkAction *action, + MultiloadApplet *ma) +{ + GError *error = NULL; + + gtk_show_uri_on_window (NULL, + "help:mate-multiload", + gtk_get_current_event_time (), + &error); + + if (error) { /* FIXME: the user needs to see this */ + g_warning ("help error: %s\n", error->message); + g_error_free (error); + error = NULL; + } +} + +/* run the full-scale system process monitor */ + +static void +start_procman (MultiloadApplet *ma) +{ + GError *error = NULL; + GDesktopAppInfo *appinfo; + gchar *monitor; + GdkAppLaunchContext *launch_context; + GdkDisplay *display; + GAppInfo *app_info; + GdkScreen *screen; + + g_return_if_fail (ma != NULL); + + monitor = g_settings_get_string (ma->settings, "system-monitor"); + if (monitor == NULL) + monitor = g_strdup ("mate-system-monitor.desktop"); + + screen = gtk_widget_get_screen (GTK_WIDGET (ma->applet)); + appinfo = g_desktop_app_info_new (monitor); + if (appinfo) { + GdkAppLaunchContext *context; + display = gdk_screen_get_display (screen); + context = gdk_display_get_app_launch_context (display); + gdk_app_launch_context_set_screen (context, screen); + g_app_info_launch (G_APP_INFO (appinfo), NULL, G_APP_LAUNCH_CONTEXT (context), &error); + g_object_unref (context); + g_object_unref (appinfo); + } + else { + app_info = g_app_info_create_from_commandline ("mate-system-monitor", + _("Start system-monitor"), + G_APP_INFO_CREATE_NONE, + &error); + + if (!error) { + display = gdk_screen_get_display (screen); + launch_context = gdk_display_get_app_launch_context (display); + gdk_app_launch_context_set_screen (launch_context, screen); + g_app_info_launch (app_info, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error); + + g_object_unref (launch_context); + } + } + g_free (monitor); + + if (error) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("There was an error executing '%s': %s"), + "mate-system-monitor", + error->message); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_window_set_screen (GTK_WINDOW (dialog), screen); + + gtk_widget_show (dialog); + + g_error_free (error); + } +} + +static void +start_procman_cb (GtkAction *action, + MultiloadApplet *ma) +{ + start_procman (ma); +} + +static void +multiload_change_size_cb(MatePanelApplet *applet, gint size, gpointer data) +{ + MultiloadApplet *ma = (MultiloadApplet *)data; + + multiload_applet_refresh(ma); + + return; +} + +static void +multiload_change_orient_cb(MatePanelApplet *applet, gint arg1, gpointer data) +{ + MultiloadApplet *ma = data; + multiload_applet_refresh((MultiloadApplet *)data); + gtk_widget_show (GTK_WIDGET (ma->applet)); + return; +} + +static void +multiload_destroy_cb(GtkWidget *widget, gpointer data) +{ + guint i; + MultiloadApplet *ma = data; + + for (i = 0; i < graph_n; i++) + { + load_graph_stop(ma->graphs[i]); + if (ma->graphs[i]->colors) + { + g_free (ma->graphs[i]->colors); + ma->graphs[i]->colors = NULL; + } + gtk_widget_destroy(ma->graphs[i]->main_widget); + + load_graph_unalloc(ma->graphs[i]); + g_free(ma->graphs[i]); + } + + netspeed_delete (ma->netspeed_in); + netspeed_delete (ma->netspeed_out); + + if (ma->about_dialog) + gtk_widget_destroy (ma->about_dialog); + + if (ma->prop_dialog) + gtk_widget_destroy (ma->prop_dialog); + + gtk_widget_destroy(GTK_WIDGET(ma->applet)); + + g_free (ma); + + return; +} + +static gboolean +multiload_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, MultiloadApplet *ma) +{ + g_return_val_if_fail (event != NULL, FALSE); + g_return_val_if_fail (ma != NULL, FALSE); + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) { + start_procman (ma); + return TRUE; + } + return FALSE; +} + +static gboolean +multiload_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, MultiloadApplet *ma) +{ + g_return_val_if_fail (event != NULL, FALSE); + g_return_val_if_fail (ma != NULL, FALSE); + + switch (event->keyval) { + /* this list of keyvals taken from mixer applet, which seemed to have + a good list of keys to use */ + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + case GDK_KEY_3270_Enter: + case GDK_KEY_Return: + case GDK_KEY_space: + case GDK_KEY_KP_Space: + /* activate */ + start_procman (ma); + return TRUE; + + default: + break; + } + + return FALSE; +} + +/* update the tooltip to the graph's current "used" percentage */ +void +multiload_applet_tooltip_update(LoadGraph *g) +{ + gchar *tooltip_text; + MultiloadApplet *multiload; + const char *tooltip_label [graph_n] = { + [graph_cpuload] = N_("Processor"), + [graph_memload] = N_("Memory"), + [graph_netload2] = N_("Network"), + [graph_swapload] = N_("Swap Space"), + [graph_loadavg] = N_("Load Average"), + [graph_diskload] = N_("Disk") + }; + const char *name; + + g_assert(g); + + multiload = g->multiload; + + /* label the tooltip intuitively */ + name = gettext (tooltip_label [g->id]); + + switch (g->id) { + case graph_memload: { + float user_percent, cache_percent; + + user_percent = MIN ((float)(100 * multiload->memload_user) / (float)(multiload->memload_total), 100.0f); + cache_percent = MIN ((float)(100 * multiload->memload_cache) / (float)(multiload->memload_total), 100.0f); + tooltip_text = g_strdup_printf (_("%s:\n" + "%.01f%% in use by programs\n" + "%.01f%% in use as cache"), + name, + user_percent, + cache_percent); + break; + } + case graph_loadavg: { + tooltip_text = g_strdup_printf (_("The system load average is %0.02f"), + multiload->loadavg1); + break; + } + case graph_netload2: { + char *tx_in, *tx_out; + + tx_in = netspeed_get(multiload->netspeed_in); + tx_out = netspeed_get(multiload->netspeed_out); + /* xgettext: same as in graphic tab of g-s-m */ + tooltip_text = g_strdup_printf(_("%s:\n" + "Receiving %s\n" + "Sending %s"), + name, tx_in, tx_out); + g_free(tx_in); + g_free(tx_out); + break; + } + default: { + float ratio; + float percent; + + if (g->id == graph_cpuload) + ratio = multiload->cpu_used_ratio; + else if (g->id == graph_swapload) + ratio = multiload->swapload_used_ratio; + else if (g->id == graph_diskload) + ratio = multiload->diskload_used_ratio; + else + g_assert_not_reached (); + + percent = CLAMP (ratio * 100.0f, 0.0f, 100.0f); + tooltip_text = g_strdup_printf(_("%s:\n" + "%.01f%% in use"), + name, + percent); + } + } + + gtk_widget_set_tooltip_text(g->disp, tooltip_text); + + g_free(tooltip_text); +} + +static void +multiload_create_graphs(MultiloadApplet *ma) +{ + struct { const char *label; + const char *visibility_key; + const char *name; + guint num_colours; + LoadGraphDataFunc callback; + } graph_types [graph_n] = { + [graph_cpuload] = { _("CPU Load"), VIEW_CPULOAD_KEY, "cpuload", cpuload_n, GetLoad }, + [graph_memload] = { _("Memory Load"), VIEW_MEMLOAD_KEY, "memload", memload_n, GetMemory }, + [graph_netload2] = { _("Net Load"), VIEW_NETLOAD_KEY, "netload2", 6, GetNet }, + [graph_swapload] = { _("Swap Load"), VIEW_SWAPLOAD_KEY, "swapload", swapload_n, GetSwap }, + [graph_loadavg] = { _("Load Average"), VIEW_LOADAVG_KEY, "loadavg", 3, GetLoadAvg }, + [graph_diskload] = { _("Disk Load"), VIEW_DISKLOAD_KEY, "diskload", diskload_n, GetDiskLoad } + }; + + guint size; + guint speed; + guint64 net_threshold1; + guint64 net_threshold2; + guint64 net_threshold3; + gint i; + + speed = CLAMP (g_settings_get_uint (ma->settings, REFRESH_RATE_KEY), REFRESH_RATE_MIN, REFRESH_RATE_MAX); + size = CLAMP (g_settings_get_uint (ma->settings, GRAPH_SIZE_KEY), GRAPH_SIZE_MIN, GRAPH_SIZE_MAX); + net_threshold1 = CLAMP (g_settings_get_uint64 (ma->settings, KEY_NET_THRESHOLD1), MIN_NET_THRESHOLD1, MAX_NET_THRESHOLD1); + net_threshold2 = CLAMP (g_settings_get_uint64 (ma->settings, KEY_NET_THRESHOLD2), MIN_NET_THRESHOLD2, MAX_NET_THRESHOLD2); + net_threshold3 = CLAMP (g_settings_get_uint64 (ma->settings, KEY_NET_THRESHOLD3), MIN_NET_THRESHOLD3, MAX_NET_THRESHOLD3); + if (net_threshold1 >= net_threshold2) + { + net_threshold1 = net_threshold2 - 1; + } + if (net_threshold2 >= net_threshold3) + { + net_threshold3 = net_threshold2 + 1; + } + + for (i = 0; i < graph_n; i++) + { + ma->graphs[i] = load_graph_new (ma, + graph_types[i].num_colours, + graph_types[i].label, + i, + speed, + size, + g_settings_get_boolean (ma->settings, graph_types[i].visibility_key), + graph_types[i].name, + graph_types[i].callback); + } + ma->nvme_diskstats = g_settings_get_boolean (ma->settings, "diskload-nvme-diskstats"); + /* for Network graph, colors[4] is grid line color, it should not be used in loop in load-graph.c */ + /* for Network graph, colors[5] is indicator color, it should not be used in loop in load-graph.c */ + ma->graphs[graph_netload2]->n = 4; + ma->net_threshold1 = net_threshold1; + ma->net_threshold2 = net_threshold2; + ma->net_threshold3 = net_threshold3; + ma->netspeed_in = netspeed_new (ma->graphs [graph_netload2]); + ma->netspeed_out = netspeed_new (ma->graphs [graph_netload2]); + /* for Load graph, colors[2] is grid line color, it should not be used in loop in load-graph.c */ + ma->graphs[graph_loadavg]->n = 2; +} + +/* remove the old graphs and rebuild them */ +void +multiload_applet_refresh(MultiloadApplet *ma) +{ + guint i; + MatePanelAppletOrient orientation; + + /* stop and free the old graphs */ + for (i = 0; i < graph_n; i++) + { + if (!ma->graphs[i]) + continue; + + load_graph_stop(ma->graphs[i]); + gtk_widget_destroy(ma->graphs[i]->main_widget); + + load_graph_unalloc(ma->graphs[i]); + g_free(ma->graphs[i]); + } + + if (ma->box) + gtk_widget_destroy(ma->box); + + orientation = mate_panel_applet_get_orient(ma->applet); + + if ( (orientation == MATE_PANEL_APPLET_ORIENT_UP) || + (orientation == MATE_PANEL_APPLET_ORIENT_DOWN) ) { + ma->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + } + else + ma->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + gtk_container_add(GTK_CONTAINER(ma->applet), ma->box); + + /* create the N graphs, passing in their user-configurable properties with gsettings. */ + multiload_create_graphs (ma); + + /* only start and display the graphs the user has turned on */ + + for (i = 0; i < graph_n; i++) { + gtk_box_pack_start(GTK_BOX(ma->box), + ma->graphs[i]->main_widget, + TRUE, TRUE, 1); + if (ma->graphs[i]->visible) { + gtk_widget_show_all (ma->graphs[i]->main_widget); + load_graph_start(ma->graphs[i]); + } + } + gtk_widget_show (ma->box); + + return; +} + +static const GtkActionEntry multiload_menu_actions [] = { + { "MultiLoadProperties", "document-properties", N_("_Preferences"), + NULL, NULL, + G_CALLBACK (multiload_properties_cb) }, + { "MultiLoadRunProcman", "system-run", N_("_Open System Monitor"), + NULL, NULL, + G_CALLBACK (start_procman_cb) }, + { "MultiLoadHelp", "help-browser", N_("_Help"), + NULL, NULL, + G_CALLBACK (help_cb) }, + { "MultiLoadAbout", "help-about", N_("_About"), + NULL, NULL, + G_CALLBACK (about_cb) } +}; + +/* create a box and stuff the load graphs inside of it */ +static gboolean +multiload_applet_new(MatePanelApplet *applet, const gchar *iid, gpointer data) +{ + GtkStyleContext *context; + MultiloadApplet *ma; + GSettings *lockdown_settings; + GtkActionGroup *action_group; + AtkObject *atk_obj; + + context = gtk_widget_get_style_context (GTK_WIDGET (applet)); + gtk_style_context_add_class (context, "multiload-applet"); + + ma = g_new0(MultiloadApplet, 1); + + ma->applet = applet; + + ma->about_dialog = NULL; + ma->prop_dialog = NULL; + ma->last_clicked = 0; + +#ifndef ENABLE_IN_PROCESS + g_set_application_name (_("System Monitor")); +#endif + + gtk_window_set_default_icon_name ("utilities-system-monitor"); + + ma->settings = mate_panel_applet_settings_new (applet, "org.mate.panel.applet.multiload"); + mate_panel_applet_set_flags (applet, MATE_PANEL_APPLET_EXPAND_MINOR); + + action_group = gtk_action_group_new ("Multiload Applet Actions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions (action_group, + multiload_menu_actions, + G_N_ELEMENTS (multiload_menu_actions), + ma); + mate_panel_applet_setup_menu_from_resource (applet, + MULTILOAD_RESOURCE_PATH "multiload-applet-menu.xml", + action_group); + + if (mate_panel_applet_get_locked_down (applet)) { + GtkAction *action; + + action = gtk_action_group_get_action (action_group, "MultiLoadProperties"); + gtk_action_set_visible (action, FALSE); + } + + lockdown_settings = g_settings_new ("org.mate.lockdown"); + if (g_settings_get_boolean (lockdown_settings, "disable-command-line") || + mate_panel_applet_get_locked_down (applet)) { + GtkAction *action; + + /* When the panel is locked down or when the command line is inhibited, + it seems very likely that running the procman would be at least harmful */ + action = gtk_action_group_get_action (action_group, "MultiLoadRunProcman"); + gtk_action_set_visible (action, FALSE); + } + g_object_unref (lockdown_settings); + + g_object_unref (action_group); + + g_signal_connect (applet, "change-size", + G_CALLBACK (multiload_change_size_cb), ma); + g_signal_connect (applet, "change-orient", + G_CALLBACK (multiload_change_orient_cb), ma); + g_signal_connect (applet, "destroy", + G_CALLBACK (multiload_destroy_cb), ma); + g_signal_connect (applet, "button-press-event", + G_CALLBACK (multiload_button_press_event_cb), ma); + g_signal_connect (applet, "key-press-event", + G_CALLBACK (multiload_key_press_event_cb), ma); + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (applet)); + + if (GTK_IS_ACCESSIBLE (atk_obj)) { + atk_object_set_name (atk_obj, _("System Monitor")); + atk_object_set_description (atk_obj, + _("A system load monitor capable of displaying graphs " + "for CPU, ram, and swap space use, plus network " + "traffic.")); + } + + multiload_applet_refresh (ma); + + gtk_widget_show(GTK_WIDGET(applet)); + + return TRUE; +} + +static gboolean +multiload_factory (MatePanelApplet *applet, + const gchar *iid, + gpointer data) +{ + gboolean retval = FALSE; + + glibtop_init(); + + retval = multiload_applet_new(applet, iid, data); + + return retval; +} + +PANEL_APPLET_FACTORY ("MultiLoadAppletFactory", + PANEL_TYPE_APPLET, + "multiload", + multiload_factory, + NULL) diff --git a/multiload/src/netspeed.c b/multiload/src/netspeed.c new file mode 100644 index 00000000..a61ce04b --- /dev/null +++ b/multiload/src/netspeed.c @@ -0,0 +1,64 @@ +#include <config.h> +#include <glib.h> + +#include "netspeed.h" + +enum { N_STATES = 4 }; + +struct _NetSpeed +{ + LoadGraph *graph; + guint64 states[N_STATES]; + size_t cur; +}; + +NetSpeed* +netspeed_new (LoadGraph *g) +{ + NetSpeed *ns = g_new0 (NetSpeed, 1); + ns->graph = g; + return ns; +} + +void netspeed_delete(NetSpeed *ns) +{ + g_free(ns); +} + +void +netspeed_add (NetSpeed *ns, + guint64 tx) +{ + ns->cur = (ns->cur + 1) % N_STATES; + ns->states[ns->cur] = tx; +} + +char* +netspeed_get (NetSpeed *ns) +{ + guint64 older; + guint64 newer; + guint64 rate; + char *bytes; + char *text; + + newer = ns->states[ns->cur]; + older = ns->states[(ns->cur + 1) % N_STATES]; + + if ((older != 0) && (newer > older)) + rate = (newer - older) * 1000 / ((N_STATES - 1) * ns->graph->speed); + else + /* We end up here if we haven't got enough data yet or the + network interface has jumped back (or there has never + been any activity on any interface). A value of 0 is + likely to be accurate, but if it is wrong it will be + clearly wrong. In any event, it should fix itself in a + few seconds. */ + rate = 0; + + bytes = g_format_size (rate); + text = g_strdup_printf (_("%s/s"), bytes); + g_free (bytes); + + return text; +} diff --git a/multiload/src/netspeed.h b/multiload/src/netspeed.h new file mode 100644 index 00000000..55a2ff75 --- /dev/null +++ b/multiload/src/netspeed.h @@ -0,0 +1,15 @@ +#ifndef H_MULTILOAD_NETSPEED_ +#define H_MULTILOAD_NETSPEED_ + +#include <glib.h> + +typedef struct _NetSpeed NetSpeed; + +#include "global.h" + +NetSpeed* netspeed_new(LoadGraph *graph); +void netspeed_delete(NetSpeed *ns); +void netspeed_add(NetSpeed *ns, guint64 tx); +char* netspeed_get(NetSpeed *ns); + +#endif /* H_MULTILOAD_NETSPEED_ */ diff --git a/multiload/src/properties.c b/multiload/src/properties.c new file mode 100644 index 00000000..e776b7b8 --- /dev/null +++ b/multiload/src/properties.c @@ -0,0 +1,686 @@ +/* MATE cpuload/memload panel applet + * (C) 2002 The Free Software Foundation + * + * Authors: + * Todd Kulesza + * + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <gtk/gtk.h> + +#include <gio/gio.h> +#include <mate-panel-applet.h> +#include <mate-panel-applet-gsettings.h> + +#include "global.h" + +#define NEVER_SENSITIVE "never_sensitive" + +/* set sensitive and setup NEVER_SENSITIVE appropriately */ +static void +hard_set_sensitive (GtkWidget *w, gboolean sensitivity) +{ + gtk_widget_set_sensitive (w, sensitivity); + g_object_set_data (G_OBJECT (w), NEVER_SENSITIVE, + GINT_TO_POINTER ( ! sensitivity)); +} + +/* set sensitive, but always insensitive if NEVER_SENSITIVE is set */ +static void +soft_set_sensitive (GtkWidget *w, gboolean sensitivity) +{ + if (g_object_get_data (G_OBJECT (w), NEVER_SENSITIVE)) + gtk_widget_set_sensitive (w, FALSE); + else + gtk_widget_set_sensitive (w, sensitivity); +} + +static void +properties_set_insensitive(MultiloadApplet *ma) +{ + guint total_graphs = 0; + guint last_graph = 0; + guint i; + + for (i = 0; i < graph_n; i++) + if (ma->graphs[i]->visible) + { + last_graph = i; + total_graphs++; + } + + if (total_graphs < 2) + soft_set_sensitive (ma->check_boxes[last_graph], FALSE); +} + +static void +on_properties_dialog_response (GtkWidget *widget, + gint arg, + MultiloadApplet *ma) +{ + GError *error = NULL; + + switch (arg) { + case GTK_RESPONSE_HELP: + + gtk_show_uri_on_window (NULL, + "help:mate-multiload/multiload-prefs", + gtk_get_current_event_time (), + &error); + + if (error) { /* FIXME: the user needs to see this */ + g_warning ("help error: %s\n", error->message); + g_error_free (error); + } + break; + + case GTK_RESPONSE_CLOSE: + default: + gtk_widget_destroy (widget); + ma->prop_dialog = NULL; + } +} + +static void +on_speed_spin_button_value_changed (GtkSpinButton *spin_button, + gpointer user_data) +{ + MultiloadApplet *ma = user_data; + gint value; + guint i; + + value = gtk_spin_button_get_value_as_int (spin_button); + g_settings_set_uint (ma->settings, REFRESH_RATE_KEY, (guint) value); + for (i = 0; i < graph_n; i++) { + load_graph_stop (ma->graphs[i]); + ma->graphs[i]->speed = (guint) value; + if (ma->graphs[i]->visible) + load_graph_start (ma->graphs[i]); + } +} + +static void +on_graph_size_spin_button_value_changed (GtkSpinButton *spin_button, + gpointer user_data) +{ + MultiloadApplet *ma = user_data; + gint value; + guint i; + + value = gtk_spin_button_get_value_as_int (spin_button); + g_settings_set_uint (ma->settings, GRAPH_SIZE_KEY, (guint) value); + for (i = 0; i < graph_n; i++) { + ma->graphs[i]->size = (guint) value; + if (ma->graphs[i]->orient) { + gtk_widget_set_size_request (ma->graphs[i]->main_widget, + (gint) ma->graphs[i]->pixel_size, + (gint) ma->graphs[i]->size); + } else { + gtk_widget_set_size_request (ma->graphs[i]->main_widget, + (gint) ma->graphs[i]->size, + (gint) ma->graphs[i]->pixel_size); + } + } +} + +static void +on_net_threshold1_spin_button_value_changed (GtkSpinButton *spin_button, + gpointer user_data) +{ + MultiloadApplet *ma = user_data; + gdouble temp; + + temp = gtk_spin_button_get_value (spin_button); + ma->net_threshold1 = (guint64) temp; + if (ma->net_threshold1 >= ma->net_threshold2) { + ma->net_threshold1 = ma->net_threshold2 - 1; + gtk_spin_button_set_value (spin_button, (gdouble) ma->net_threshold1); + } + g_settings_set_uint64 (ma->settings, KEY_NET_THRESHOLD1, ma->net_threshold1); +} + +static void +on_net_threshold2_spin_button_value_changed (GtkSpinButton *spin_button, + gpointer user_data) +{ + MultiloadApplet *ma = user_data; + gdouble temp; + + temp = gtk_spin_button_get_value (spin_button); + ma->net_threshold2 = (guint64) temp; + if (ma->net_threshold2 >= ma->net_threshold3) { + ma->net_threshold2 = ma->net_threshold3 - 1; + gtk_spin_button_set_value (spin_button, (gdouble) ma->net_threshold2); + } else if (ma->net_threshold2 <= ma->net_threshold1) { + ma->net_threshold2 = ma->net_threshold1 + 1; + gtk_spin_button_set_value (spin_button, (gdouble) ma->net_threshold2); + } + g_settings_set_uint64 (ma->settings, KEY_NET_THRESHOLD2, ma->net_threshold2); +} + +static void +on_net_threshold3_spin_button_value_changed (GtkSpinButton *spin_button, + gpointer user_data) +{ + MultiloadApplet *ma = user_data; + gdouble temp; + + temp = gtk_spin_button_get_value (spin_button); + ma->net_threshold3 = (guint64) temp; + if (ma->net_threshold3 <= ma->net_threshold2) { + ma->net_threshold3 = ma->net_threshold2 + 1; + gtk_spin_button_set_value (spin_button, (gdouble) ma->net_threshold3); + } + g_settings_set_uint64 (ma->settings, KEY_NET_THRESHOLD3, ma->net_threshold3); +} + +static void +color_button_set (GtkColorChooser *button, + GSettings *settings, + const char *key, + GdkRGBA *color) +{ + gchar *color_string; + + gtk_color_chooser_get_rgba (button, color); + color_string = gdk_rgba_to_string (color); + g_settings_set_string (settings, key, color_string); + g_free (color_string); +} + +static void +on_cpuload_usr_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_CPULOAD_USR_COLOR, + &(ma->graphs[graph_cpuload]->colors[cpuload_usr])); +} + +static void +on_cpuload_sys_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_CPULOAD_SYS_COLOR, + &(ma->graphs[graph_cpuload]->colors[cpuload_sys])); +} + +static void +on_cpuload_nice_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_CPULOAD_NICE_COLOR, + &(ma->graphs[graph_cpuload]->colors[cpuload_nice])); +} + +static void +on_cpuload_iowait_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_CPULOAD_IOWAIT_COLOR, + &(ma->graphs[graph_cpuload]->colors[cpuload_iowait])); +} + +static void +on_cpuload_free_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_CPULOAD_IDLE_COLOR, + &(ma->graphs[graph_cpuload]->colors[cpuload_free])); +} + +static void +on_memload_user_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_MEMLOAD_USER_COLOR, + &(ma->graphs[graph_memload]->colors[memload_user])); +} + +static void +on_memload_shared_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_MEMLOAD_SHARED_COLOR, + &(ma->graphs[graph_memload]->colors[memload_shared])); +} + +static void +on_memload_buffer_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_MEMLOAD_BUFFER_COLOR, + &(ma->graphs[graph_memload]->colors[memload_buffer])); +} + +static void +on_memload_cached_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_MEMLOAD_CACHED_COLOR, + &(ma->graphs[graph_memload]->colors[memload_cached])); +} + +static void +on_memload_free_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_MEMLOAD_FREE_COLOR, + &(ma->graphs[graph_memload]->colors[memload_free])); +} + +static void +on_netload2_in_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_IN_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_in])); +} + +static void +on_netload2_out_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_OUT_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_out])); +} + +static void +on_netload2_loopback_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_LOOPBACK_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_loopback])); +} + +static void +on_netload2_background_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_BACKGROUND_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_background])); +} + +static void +on_netload2_gridline_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_GRIDLINE_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_gridline])); +} + +static void +on_netload2_indicator_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_NETLOAD2_INDICATOR_COLOR, + &(ma->graphs[graph_netload2]->colors[netload2_indicator])); +} + +static void +on_swapload_used_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_SWAPLOAD_USED_COLOR, + &(ma->graphs[graph_swapload]->colors[swapload_used])); +} + +static void +on_swapload_free_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_SWAPLOAD_FREE_COLOR, + &(ma->graphs[graph_swapload]->colors[swapload_free])); +} + +static void +on_loadavg_average_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_LOADAVG_AVERAGE_COLOR, + &(ma->graphs[graph_loadavg]->colors[loadavg_average])); +} + +static void +on_loadavg_background_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_LOADAVG_BACKGROUND_COLOR, + &(ma->graphs[graph_loadavg]->colors[loadavg_background])); +} + +static void +on_loadavg_gridline_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_LOADAVG_GRIDLINE_COLOR, + &(ma->graphs[graph_loadavg]->colors[loadavg_gridline])); +} + +static void +on_diskload_read_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_DISKLOAD_READ_COLOR, + &(ma->graphs[graph_diskload]->colors[diskload_read])); +} + +static void +on_diskload_write_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_DISKLOAD_WRITE_COLOR, + &(ma->graphs[graph_diskload]->colors[diskload_write])); +} + +static void +on_diskload_free_color_button_color_set (GtkColorButton *button, + MultiloadApplet *ma) +{ + color_button_set (GTK_COLOR_CHOOSER (button), + ma->settings, KEY_DISKLOAD_FREE_COLOR, + &(ma->graphs[graph_diskload]->colors[diskload_free])); +} + +static void +graph_set_active (MultiloadApplet *ma, + LoadGraph *graph, + gboolean active) +{ + graph->visible = active; + if (active) { + guint i; + + for (i = 0; i < graph_n; i++) + soft_set_sensitive(ma->check_boxes[i], TRUE); + gtk_widget_show_all (graph->main_widget); + load_graph_start (graph); + } else { + load_graph_stop (graph); + gtk_widget_hide (graph->main_widget); + properties_set_insensitive (ma); + } +} + +#define GRAPH_ACTIVE_SET(x) graph_set_active (ma, ma->graphs[(x)], \ + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox))) + +static void +on_graph_cpuload_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_cpuload); +} + +static void +on_graph_memload_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_memload); +} + +static void +on_graph_netload2_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_netload2); +} + +static void +on_graph_swapload_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_swapload); +} + +static void +on_graph_loadavg_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_loadavg); +} + +static void +on_graph_diskload_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + GRAPH_ACTIVE_SET (graph_diskload); +} + +/* save the checkbox option to gsettings and apply it on the applet */ +static void +on_nvme_checkbox_toggled (GtkCheckButton *checkbox, + MultiloadApplet *ma) +{ + ma->nvme_diskstats = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox)); +} + +static void +read_spin_uint_button (GtkWidget *widget, + GSettings *settings, + const char *key, + guint min, + guint max) +{ + guint value; + + value = CLAMP (g_settings_get_uint (settings, key), min, max); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), (gdouble) value); + if (!g_settings_is_writable (settings, key)) + hard_set_sensitive (widget, FALSE); +} + +static void +read_spin_uint64_button (GtkWidget *widget, + GSettings *settings, + const char *key, + guint min, + guint max) +{ + guint64 value; + + value = CLAMP (g_settings_get_uint64 (settings, key), min, max); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), (gdouble) value); + + if (!g_settings_is_writable (settings, key)) + hard_set_sensitive (widget, FALSE); +} + +static void +read_color_button (GtkWidget *widget, + GSettings *settings, + const char *key) +{ + GdkRGBA color; + gchar *color_string; + + color_string = g_settings_get_string (settings, key); + if (*color_string != '\0') { + gdk_rgba_parse (&color, color_string); + } else { + gdk_rgba_parse (&color, "#000000"); + } + g_free (color_string); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (widget), &color); + + if (!g_settings_is_writable (settings, key)) + hard_set_sensitive (widget, FALSE); +} + +/* show properties dialog */ +void +multiload_properties_cb (GtkAction *action, + MultiloadApplet *ma) +{ + GtkBuilder *builder; + GtkWidget *dialog = NULL; + GtkWidget *graph_size_spin_button_label; + const char *graph_size_spin_button_label_txt; + MatePanelAppletOrient orient; + + if (ma->prop_dialog) { + dialog = ma->prop_dialog; + + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (GTK_WIDGET (ma->applet))); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (ma->notebook), + ma->last_clicked); + gtk_window_present (GTK_WINDOW (dialog)); + return; + } + + builder = gtk_builder_new_from_resource (MULTILOAD_RESOURCE_PATH "properties.ui"); + gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); + + #define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (builder, (x)))) + + ma->prop_dialog = GET_WIDGET ("properties_dialog"); + + read_color_button (GET_WIDGET ("cpuload_free_color_button"), ma->settings, KEY_CPULOAD_IDLE_COLOR); + read_color_button (GET_WIDGET ("cpuload_iowait_color_button"), ma->settings, KEY_CPULOAD_IOWAIT_COLOR); + read_color_button (GET_WIDGET ("cpuload_nice_color_button"), ma->settings, KEY_CPULOAD_NICE_COLOR); + read_color_button (GET_WIDGET ("cpuload_sys_color_button"), ma->settings, KEY_CPULOAD_SYS_COLOR); + read_color_button (GET_WIDGET ("cpuload_usr_color_button"), ma->settings, KEY_CPULOAD_USR_COLOR); + read_color_button (GET_WIDGET ("diskload_free_color_button"), ma->settings, KEY_DISKLOAD_FREE_COLOR); + read_color_button (GET_WIDGET ("diskload_read_color_button"), ma->settings, KEY_DISKLOAD_READ_COLOR); + read_color_button (GET_WIDGET ("diskload_write_color_button"), ma->settings, KEY_DISKLOAD_WRITE_COLOR); + read_color_button (GET_WIDGET ("loadavg_average_color_button"), ma->settings, KEY_LOADAVG_AVERAGE_COLOR); + read_color_button (GET_WIDGET ("loadavg_background_color_button"), ma->settings, KEY_LOADAVG_BACKGROUND_COLOR); + read_color_button (GET_WIDGET ("loadavg_gridline_color_button"), ma->settings, KEY_LOADAVG_GRIDLINE_COLOR); + read_color_button (GET_WIDGET ("memload_buffer_color_button"), ma->settings, KEY_MEMLOAD_BUFFER_COLOR); + read_color_button (GET_WIDGET ("memload_cached_color_button"), ma->settings, KEY_MEMLOAD_CACHED_COLOR); + read_color_button (GET_WIDGET ("memload_free_color_button"), ma->settings, KEY_MEMLOAD_FREE_COLOR); + read_color_button (GET_WIDGET ("memload_shared_color_button"), ma->settings, KEY_MEMLOAD_SHARED_COLOR); + read_color_button (GET_WIDGET ("memload_user_color_button"), ma->settings, KEY_MEMLOAD_USER_COLOR); + read_color_button (GET_WIDGET ("netload2_background_color_button"), ma->settings, KEY_NETLOAD2_BACKGROUND_COLOR); + read_color_button (GET_WIDGET ("netload2_gridline_color_button"), ma->settings, KEY_NETLOAD2_GRIDLINE_COLOR); + read_color_button (GET_WIDGET ("netload2_in_color_button"), ma->settings, KEY_NETLOAD2_IN_COLOR); + read_color_button (GET_WIDGET ("netload2_indicator_color_button"), ma->settings, KEY_NETLOAD2_INDICATOR_COLOR); + read_color_button (GET_WIDGET ("netload2_loopback_color_button"), ma->settings, KEY_NETLOAD2_LOOPBACK_COLOR); + read_color_button (GET_WIDGET ("netload2_out_color_button"), ma->settings, KEY_NETLOAD2_OUT_COLOR); + read_color_button (GET_WIDGET ("swapload_free_color_button"), ma->settings, KEY_SWAPLOAD_FREE_COLOR); + read_color_button (GET_WIDGET ("swapload_used_color_button"), ma->settings, KEY_SWAPLOAD_USED_COLOR); + + graph_size_spin_button_label = GET_WIDGET ("graph_size_spin_button_label"); + orient = mate_panel_applet_get_orient(ma->applet); + switch (orient) { + case MATE_PANEL_APPLET_ORIENT_UP: + case MATE_PANEL_APPLET_ORIENT_DOWN: + graph_size_spin_button_label_txt = _("System m_onitor width:"); + break; + default: + graph_size_spin_button_label_txt = _("System m_onitor height:"); + } + gtk_label_set_text_with_mnemonic (GTK_LABEL (graph_size_spin_button_label), graph_size_spin_button_label_txt); + + read_spin_uint_button (GET_WIDGET ("graph_size_spin_button"), ma->settings, GRAPH_SIZE_KEY, GRAPH_SIZE_MIN, GRAPH_SIZE_MAX); + read_spin_uint_button (GET_WIDGET ("speed_spin_button"), ma->settings, REFRESH_RATE_KEY, REFRESH_RATE_MIN, REFRESH_RATE_MAX); + + read_spin_uint64_button (GET_WIDGET ("net_threshold1_spin_button"), ma->settings, KEY_NET_THRESHOLD1, MIN_NET_THRESHOLD1, MAX_NET_THRESHOLD1); + read_spin_uint64_button (GET_WIDGET ("net_threshold2_spin_button"), ma->settings, KEY_NET_THRESHOLD2, MIN_NET_THRESHOLD2, MAX_NET_THRESHOLD2); + read_spin_uint64_button (GET_WIDGET ("net_threshold3_spin_button"), ma->settings, KEY_NET_THRESHOLD3, MIN_NET_THRESHOLD3, MAX_NET_THRESHOLD3); + + ma->notebook = GET_WIDGET ("notebook"); + + ma->check_boxes[graph_cpuload] = GET_WIDGET ("graph_cpuload_checkbox"); + ma->check_boxes[graph_memload] = GET_WIDGET ("graph_memload_checkbox"); + ma->check_boxes[graph_netload2] = GET_WIDGET ("graph_netload2_checkbox"); + ma->check_boxes[graph_swapload] = GET_WIDGET ("graph_swapload_checkbox"); + ma->check_boxes[graph_loadavg] = GET_WIDGET ("graph_loadavg_checkbox"); + ma->check_boxes[graph_diskload] = GET_WIDGET ("graph_diskload_checkbox"); + + g_settings_bind (ma->settings, VIEW_CPULOAD_KEY, ma->check_boxes[graph_cpuload], "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (ma->settings, VIEW_MEMLOAD_KEY, ma->check_boxes[graph_memload], "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (ma->settings, VIEW_NETLOAD_KEY, ma->check_boxes[graph_netload2], "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (ma->settings, VIEW_SWAPLOAD_KEY, ma->check_boxes[graph_swapload], "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (ma->settings, VIEW_LOADAVG_KEY, ma->check_boxes[graph_loadavg], "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (ma->settings, VIEW_DISKLOAD_KEY, ma->check_boxes[graph_diskload], "active", G_SETTINGS_BIND_DEFAULT); + + g_settings_bind (ma->settings, DISKLOAD_NVME_KEY, GET_WIDGET ("nvme_checkbox"), "active", G_SETTINGS_BIND_DEFAULT); + + #undef GET_WIDGET + + properties_set_insensitive (ma); + + gtk_builder_add_callback_symbols (builder, + "on_cpuload_usr_color_button_color_set", G_CALLBACK (on_cpuload_usr_color_button_color_set), + "on_cpuload_sys_color_button_color_set", G_CALLBACK (on_cpuload_sys_color_button_color_set), + "on_cpuload_nice_color_button_color_set", G_CALLBACK (on_cpuload_nice_color_button_color_set), + "on_cpuload_iowait_color_button_color_set", G_CALLBACK (on_cpuload_iowait_color_button_color_set), + "on_cpuload_free_color_button_color_set", G_CALLBACK (on_cpuload_free_color_button_color_set), + "on_memload_user_color_button_color_set", G_CALLBACK (on_memload_user_color_button_color_set), + "on_memload_shared_color_button_color_set", G_CALLBACK (on_memload_shared_color_button_color_set), + "on_memload_buffer_color_button_color_set", G_CALLBACK (on_memload_buffer_color_button_color_set), + "on_memload_cached_color_button_color_set", G_CALLBACK (on_memload_cached_color_button_color_set), + "on_memload_free_color_button_color_set", G_CALLBACK (on_memload_free_color_button_color_set), + "on_netload2_in_color_button_color_set", G_CALLBACK (on_netload2_in_color_button_color_set), + "on_netload2_out_color_button_color_set", G_CALLBACK (on_netload2_out_color_button_color_set), + "on_netload2_loopback_color_button_color_set", G_CALLBACK (on_netload2_loopback_color_button_color_set), + "on_netload2_background_color_button_color_set", G_CALLBACK (on_netload2_background_color_button_color_set), + "on_netload2_gridline_color_button_color_set", G_CALLBACK (on_netload2_gridline_color_button_color_set), + "on_netload2_indicator_color_button_color_set", G_CALLBACK (on_netload2_indicator_color_button_color_set), + "on_swapload_used_color_button_color_set", G_CALLBACK (on_swapload_used_color_button_color_set), + "on_swapload_free_color_button_color_set", G_CALLBACK (on_swapload_free_color_button_color_set), + "on_loadavg_average_color_button_color_set", G_CALLBACK (on_loadavg_average_color_button_color_set), + "on_loadavg_background_color_button_color_set", G_CALLBACK (on_loadavg_background_color_button_color_set), + "on_loadavg_gridline_color_button_color_set", G_CALLBACK (on_loadavg_gridline_color_button_color_set), + "on_diskload_read_color_button_color_set", G_CALLBACK (on_diskload_read_color_button_color_set), + "on_diskload_write_color_button_color_set", G_CALLBACK (on_diskload_write_color_button_color_set), + "on_diskload_free_color_button_color_set", G_CALLBACK (on_diskload_free_color_button_color_set), + "on_properties_dialog_response", G_CALLBACK (on_properties_dialog_response), + "on_graph_cpuload_checkbox_toggled", G_CALLBACK (on_graph_cpuload_checkbox_toggled), + "on_graph_memload_checkbox_toggled", G_CALLBACK (on_graph_memload_checkbox_toggled), + "on_graph_netload2_checkbox_toggled", G_CALLBACK (on_graph_netload2_checkbox_toggled), + "on_graph_swapload_checkbox_toggled", G_CALLBACK (on_graph_swapload_checkbox_toggled), + "on_graph_loadavg_checkbox_toggled", G_CALLBACK (on_graph_loadavg_checkbox_toggled), + "on_graph_diskload_checkbox_toggled", G_CALLBACK (on_graph_diskload_checkbox_toggled), + "on_nvme_checkbox_toggled", G_CALLBACK (on_nvme_checkbox_toggled), + "on_graph_size_spin_button_value_changed", G_CALLBACK (on_graph_size_spin_button_value_changed), + "on_speed_spin_button_value_changed", G_CALLBACK (on_speed_spin_button_value_changed), + "on_net_threshold1_spin_button_value_changed", G_CALLBACK (on_net_threshold1_spin_button_value_changed), + "on_net_threshold2_spin_button_value_changed", G_CALLBACK (on_net_threshold2_spin_button_value_changed), + "on_net_threshold3_spin_button_value_changed", G_CALLBACK (on_net_threshold3_spin_button_value_changed), + NULL); + + gtk_builder_connect_signals (builder, ma); + + g_object_unref (builder); + + gtk_window_set_screen (GTK_WINDOW (ma->prop_dialog), + gtk_widget_get_screen (GTK_WIDGET (ma->applet))); + + gtk_widget_show_all (ma->prop_dialog); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (ma->notebook), + ma->last_clicked); +} |