/* * This file is part of Xfce (https://gitlab.xfce.org). * * Copyright (c) 2003 Riccardo Persichetti * Copyright (c) 2010 Florian Rivoal * Copyright (c) 2012 David Schneider * Copyright (c) 2014-2017 Landry Breuil * Copyright (c) 2021 Simon Steinbeiss * Copyright (c) 2022 Jan Ziak <0xe2.0x9a.0x9b@xfce.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_UPOWER_GLIB #include #endif #include "cpu.h" #include "memswap.h" #include "network.h" #include "plugin.h" #include "settings.h" #include "uptime.h" #include "zram.h" struct t_command { bool enabled; gchar *command_text; }; struct t_monitor { GtkWidget *box; GtkWidget *label; GtkWidget *status; GtkWidget *ebox; gulong value_read; /* Range: 0% ... 100% */ }; struct t_uptime_monitor { GtkWidget *label; GtkWidget *ebox; gulong value_read; }; struct t_global_monitor { XfcePanelPlugin *plugin; SystemloadConfig *config; GtkWidget *ebox; GtkWidget *box; guint timeout, timeout_seconds; bool use_timeout_seconds; guint timeout_id; t_command command; t_monitor *monitor[N_MONITORS]; t_uptime_monitor uptime; #ifdef HAVE_UPOWER_GLIB UpClient *upower; #endif }; /* zram sits between memory and swap: that is where it sits physically too - * data already pushed out of RAM, but not yet spilled onto the disk. */ static const SystemloadMonitor VISUAL_ORDER[] = { CPU_MONITOR, MEM_MONITOR, ZRAM_MONITOR, SWAP_MONITOR, NET_MONITOR, }; static gboolean setup_monitor_cb(gpointer user_data); static bool spawn_system_monitor(GtkWidget *w, t_global_monitor *global) { // Spawn defined command; In-terminal: false, Startup-notify: false return xfce_spawn_command_line_on_screen(gdk_screen_get_default(), global->command.command_text, FALSE, FALSE, NULL); } static gboolean click_event(GtkWidget *w, GdkEventButton *event, t_global_monitor *global) { if(event->button == 1 && global->command.enabled && *(global->command.command_text)) return spawn_system_monitor(w, global); return FALSE; } static void set_fraction(GtkProgressBar *bar, gdouble fraction) { /* * Try to avoid a call to GTK's bar_set_fraction() if the new fraction * isn't changing the number of pixels (height or width of the bar) already * displayed on the screen. * * max_alloc is independent from horizontal/vertical orientation of the bar. */ GtkAllocation alloc; gtk_widget_get_allocation(GTK_WIDGET(bar), &alloc); gint max_alloc = MAX(alloc.width, alloc.height); if (max_alloc > 1) fraction = round(fraction * max_alloc) / max_alloc; if (gtk_progress_bar_get_fraction(bar) != fraction) gtk_progress_bar_set_fraction(bar, fraction); } static void set_label_text(GtkLabel *label, const gchar *text) { const gchar *displayed_text = gtk_label_get_text(label); if (g_strcmp0(displayed_text, text) != 0) gtk_label_set_text(label, text); } static void set_tooltip(GtkWidget *w, const gchar *caption) { gchar *displayed_caption = gtk_widget_get_tooltip_text(w); if (g_strcmp0(displayed_caption, caption) != 0) gtk_widget_set_tooltip_text(w, caption); g_free(displayed_caption); } static void update_monitors(t_global_monitor *global) { const SystemloadConfig *config = global->config; gulong MTotal = 0, MUsed = 0, NTotal = 0, STotal = 0, SUsed = 0, MZram = 0; gulong ZPhys = 0, ZStored = 0; gdouble ZRatio = 0.0; bool ZValid = false; for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) global->monitor[i]->value_read = 0; if (systemload_config_get_enabled (config, CPU_MONITOR)) global->monitor[CPU_MONITOR]->value_read = read_cpuload(); if (systemload_config_get_enabled (config, MEM_MONITOR) || systemload_config_get_enabled (config, SWAP_MONITOR)) { gulong mem, swap; if (read_memswap(&mem, &swap, &MTotal, &MUsed, &STotal, &SUsed) == 0) { /* * MUsed as returned includes the RAM held by zram - that memory is * genuinely occupied. Whether the memory bar should show it is a * question of what the bar is meant to answer, so it is left to * the user; see systemload_config_get_memory_exclude_zram(). */ MZram = read_zram_phys(); if (systemload_config_get_memory_exclude_zram (config)) { MUsed = (MUsed > MZram) ? (MUsed - MZram) : 0; mem = (MTotal > 0) ? (MUsed * 100 / MTotal) : 0; } global->monitor[MEM_MONITOR]->value_read = mem; global->monitor[SWAP_MONITOR]->value_read = swap; } } if (systemload_config_get_enabled (config, ZRAM_MONITOR)) { gulong zram; if (read_zram (&zram, &ZPhys, &ZStored, &ZRatio) == 0) { global->monitor[ZRAM_MONITOR]->value_read = zram; ZValid = true; } } if (systemload_config_get_enabled (config, NET_MONITOR)) { gulong net; if (read_netload (&net, &NTotal) == 0) global->monitor[NET_MONITOR]->value_read = net; } if (systemload_config_get_uptime_enabled (config)) global->uptime.value_read = read_uptime(); for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { const auto monitor = (SystemloadMonitor) i; t_monitor *m = global->monitor[monitor]; if (systemload_config_get_enabled (config, monitor)) { gulong value = MIN(m->value_read, 100); set_fraction(GTK_PROGRESS_BAR(global->monitor[i]->status), value / 100.0); } } if (systemload_config_get_enabled (config, CPU_MONITOR)) { gchar tooltip[128]; g_snprintf(tooltip, sizeof(tooltip), _("System Load: %ld%%"), global->monitor[CPU_MONITOR]->value_read); set_tooltip(global->monitor[CPU_MONITOR]->ebox, tooltip); } if (systemload_config_get_enabled (config, MEM_MONITOR)) { gchar tooltip[192]; /* Always spell out the zram share, whichever way the bar is counted: * that way the number on screen is never ambiguous. */ if (MZram > 0) { if (systemload_config_get_memory_exclude_zram (config)) g_snprintf(tooltip, sizeof(tooltip), _("Memory: %ldMB of %ldMB used, excluding %ldMB held by zram"), MUsed >> 10, MTotal >> 10, MZram >> 10); else g_snprintf(tooltip, sizeof(tooltip), _("Memory: %ldMB of %ldMB used, including %ldMB held by zram"), MUsed >> 10, MTotal >> 10, MZram >> 10); } else { g_snprintf(tooltip, sizeof(tooltip), _("Memory: %ldMB of %ldMB used"), MUsed >> 10 , MTotal >> 10); } set_tooltip(global->monitor[MEM_MONITOR]->ebox, tooltip); } if (systemload_config_get_enabled (config, NET_MONITOR)) { gchar tooltip[128]; g_snprintf(tooltip, sizeof(tooltip), _("Network: %ld Mbit/s"), (glong) round (NTotal / 1e6)); set_tooltip(global->monitor[NET_MONITOR]->ebox, tooltip); } if (systemload_config_get_enabled (config, ZRAM_MONITOR)) { gchar tooltip[192]; if (ZValid && ZStored > 0) g_snprintf(tooltip, sizeof(tooltip), _("ZRAM: %ldMB of RAM holds %ldMB of data (%.2f:1)"), ZPhys >> 10, ZStored >> 10, ZRatio); else if (ZValid) g_snprintf(tooltip, sizeof(tooltip), _("ZRAM: empty")); else g_snprintf(tooltip, sizeof(tooltip), _("No zram device")); set_tooltip(global->monitor[ZRAM_MONITOR]->ebox, tooltip); } if (systemload_config_get_enabled (config, SWAP_MONITOR)) { gchar tooltip[128]; /* Disk-backed swap only; zram is reported by its own monitor */ if (STotal) g_snprintf(tooltip, sizeof(tooltip), _("Disk swap: %ldMB of %ldMB used"), SUsed >> 10, STotal >> 10); else g_snprintf(tooltip, sizeof(tooltip), _("No disk swap")); set_tooltip(global->monitor[SWAP_MONITOR]->ebox, tooltip); } if (systemload_config_get_uptime_enabled (config)) { gchar days_str[2][32], hours_str[2][32], mins_str[2][32]; gchar text[128], tooltip[128]; gint days = global->uptime.value_read / 86400; gint hours = (global->uptime.value_read / 3600) % 24; gint mins = (global->uptime.value_read / 60) % 60; /* sizeof of one element: the whole [2][32] array is 64 bytes and * would let g_snprintf write past the 32-byte buffer */ g_snprintf(days_str[0], sizeof(days_str[0]), _("%dd"), days); g_snprintf(hours_str[0], sizeof(hours_str[0]), _("%dh"), hours); g_snprintf(mins_str[0], sizeof(mins_str[0]), _("%dm"), mins); g_snprintf(days_str[1], sizeof(days_str[1]), ngettext("%d day", "%d days", days), days); g_snprintf(hours_str[1], sizeof(hours_str[1]), ngettext("%d hour", "%d hours", hours), hours); g_snprintf(mins_str[1], sizeof(mins_str[1]), ngettext("%d minute", "%d minutes", mins), mins); g_snprintf(tooltip, sizeof(tooltip), _("Uptime: %s, %s, %s"), days_str[1], hours_str[1], mins_str[1]); const gchar *format = systemload_config_get_uptime_format (config); if (format != NULL && format[0] != '\0') { gchar *custom = format_uptime (format, global->uptime.value_read); set_label_text(GTK_LABEL(global->uptime.label), custom); g_free (custom); } else { if (days > 0) g_snprintf(text, sizeof(text), "%s %s %s", days_str[0], hours_str[0], mins_str[0]); else g_snprintf(text, sizeof(text), "%s %s", hours_str[0], mins_str[0]); set_label_text(GTK_LABEL(global->uptime.label), text); } set_tooltip(global->uptime.ebox, tooltip); } } static void monitor_update_orientation (XfcePanelPlugin *plugin, GtkOrientation panel_orientation, GtkOrientation orientation, t_global_monitor *global) { gtk_orientable_set_orientation(GTK_ORIENTABLE(global->box), panel_orientation); for(gsize count = 0; count < G_N_ELEMENTS (global->monitor); count++) { gtk_orientable_set_orientation(GTK_ORIENTABLE(global->monitor[count]->box), panel_orientation); gtk_label_set_angle(GTK_LABEL(global->monitor[count]->label), (orientation == GTK_ORIENTATION_HORIZONTAL) ? 0 : -90); gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR(global->monitor[count]->status), (panel_orientation == GTK_ORIENTATION_HORIZONTAL)); gtk_orientable_set_orientation (GTK_ORIENTABLE(global->monitor[count]->status), (panel_orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); } gtk_label_set_angle(GTK_LABEL(global->uptime.label), (orientation == GTK_ORIENTATION_HORIZONTAL) ? 0 : -90); } static void create_monitor (t_global_monitor *global) { const SystemloadConfig *config = global->config; global->box = gtk_box_new(xfce_panel_plugin_get_orientation(global->plugin), 0); gtk_widget_show(global->box); for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { SystemloadMonitor monitor = VISUAL_ORDER[i]; t_monitor *m = global->monitor[monitor]; m->label = gtk_label_new (systemload_config_get_label (config, monitor)); m->status = GTK_WIDGET(gtk_progress_bar_new()); #if GTK_CHECK_VERSION (3, 16, 0) GtkCssProvider *css_provider = gtk_css_provider_new (); gtk_style_context_add_provider ( GTK_STYLE_CONTEXT (gtk_widget_get_style_context (GTK_WIDGET (m->status))), GTK_STYLE_PROVIDER (css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); gtk_css_provider_load_from_data (css_provider, "\ progressbar.horizontal trough { min-height: 4px; }\ progressbar.horizontal progress { min-height: 4px; }\ progressbar.vertical trough { min-width: 4px; }\ progressbar.vertical progress { min-width: 4px; }", -1, NULL); g_object_set_data(G_OBJECT(m->status), "css_provider", css_provider); #endif m->box = gtk_box_new(xfce_panel_plugin_get_orientation(global->plugin), 0); gtk_box_pack_start(GTK_BOX(m->box), GTK_WIDGET(m->label), FALSE, FALSE, 0); m->ebox = gtk_event_box_new(); gtk_widget_show(m->ebox); gtk_container_add(GTK_CONTAINER(m->ebox), GTK_WIDGET(m->box)); gtk_event_box_set_visible_window(GTK_EVENT_BOX(m->ebox), FALSE); gtk_event_box_set_above_child(GTK_EVENT_BOX(m->ebox), TRUE); gtk_widget_show(GTK_WIDGET(m->status)); gtk_box_pack_start(GTK_BOX(m->box), GTK_WIDGET(m->status), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(global->box), GTK_WIDGET(m->ebox), FALSE, FALSE, 0); gtk_widget_show_all(GTK_WIDGET(m->ebox)); } global->uptime.ebox = gtk_event_box_new(); if (systemload_config_get_uptime_enabled (config)) gtk_widget_show(global->uptime.ebox); gtk_event_box_set_visible_window(GTK_EVENT_BOX(global->uptime.ebox), FALSE); global->uptime.label = gtk_label_new(""); /* A format with \n gives several lines; keep them centred as a block */ gtk_label_set_justify(GTK_LABEL(global->uptime.label), GTK_JUSTIFY_CENTER); gtk_widget_show(global->uptime.label); gtk_container_add(GTK_CONTAINER(global->uptime.ebox), GTK_WIDGET(global->uptime.label)); gtk_box_pack_start(GTK_BOX(global->box), GTK_WIDGET(global->uptime.ebox), FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(global->ebox), GTK_WIDGET(global->box)); gtk_event_box_set_visible_window(GTK_EVENT_BOX(global->ebox), FALSE); gtk_widget_show(GTK_WIDGET(global->ebox)); update_monitors (global); } static t_global_monitor * monitor_control_new(XfcePanelPlugin *plugin) { t_global_monitor *global = g_new0 (t_global_monitor, 1); #ifdef HAVE_UPOWER_GLIB global->upower = up_client_new(); #endif global->plugin = plugin; /* initialize xfconf */ global->config = systemload_config_new (xfce_panel_plugin_get_property_base (plugin)); global->timeout = systemload_config_get_timeout (global->config); if (global->timeout < MIN_TIMEOUT) global->timeout = MIN_TIMEOUT; global->timeout_seconds = systemload_config_get_timeout_seconds (global->config); global->use_timeout_seconds = (global->timeout_seconds > 0); global->ebox = gtk_event_box_new(); gtk_widget_show(global->ebox); global->command.command_text = g_strdup (systemload_config_get_system_monitor_command (global->config)); if (strlen(global->command.command_text) > 0) global->command.enabled = true; xfce_panel_plugin_add_action_widget (plugin, global->ebox); for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) global->monitor[i] = g_new0 (t_monitor, 1); systemload_config_on_change (global->config, setup_monitor_cb, global); return global; } static void monitor_free(XfcePanelPlugin *plugin, t_global_monitor *global) { #ifdef HAVE_UPOWER_GLIB if (global->upower) { g_object_unref(global->upower); global->upower = NULL; } #endif if (global->timeout_id) g_source_remove(global->timeout_id); g_free(global->command.command_text); for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) g_free (global->monitor[i]); g_free(global); } static gboolean update_monitors_cb(gpointer user_data) { auto global = (t_global_monitor*) user_data; update_monitors (global); return TRUE; } static void setup_timer(t_global_monitor *global) { GtkSettings *settings; if (global->timeout_id) g_source_remove(global->timeout_id); #ifdef HAVE_UPOWER_GLIB if (global->upower && global->use_timeout_seconds) { if (up_client_get_on_battery(global->upower)) { if (!up_client_get_lid_is_closed(global->upower)) { global->timeout_id = g_timeout_add_seconds( global->timeout_seconds, update_monitors_cb, global); } else { /* Don't do any timeout if the lid is closed on battery */ global->timeout_id = 0; } return; } } #endif global->timeout_id = g_timeout_add(global->timeout, update_monitors_cb, global); /* reduce the default tooltip timeout to be smaller than the update interval otherwise * we won't see tooltips on GTK 2.16 or newer */ settings = gtk_settings_get_default(); if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings), "gtk-tooltip-timeout")) g_object_set(settings, "gtk-tooltip-timeout", global->timeout - 10, NULL); } static void set_margin (const t_global_monitor *global, GtkWidget *w, gint margin) { if (xfce_panel_plugin_get_orientation (global->plugin) == GTK_ORIENTATION_HORIZONTAL) { gtk_widget_set_margin_start (w, margin); gtk_widget_set_margin_top (w, 0); } else { gtk_widget_set_margin_start (w, 0); gtk_widget_set_margin_top (w, margin); } } static void setup_monitors(t_global_monitor *global) { const SystemloadConfig *config = global->config; gtk_widget_hide(GTK_WIDGET(global->uptime.ebox)); /* determine the number of enabled monitors and the number of enabled labels */ guint n_enabled = 0, n_enabled_labels = 0; for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { auto monitor = (SystemloadMonitor) i; if (systemload_config_get_enabled (config, monitor)) { bool label_visible = systemload_config_get_use_label (config, monitor) && strlen (systemload_config_get_label (config, monitor)) != 0; n_enabled++; n_enabled_labels += (label_visible ? 1 : 0); } } for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { const auto monitor = (SystemloadMonitor) i; const t_monitor *m = global->monitor[monitor]; const GdkRGBA *color = NULL; gtk_widget_hide(m->ebox); gtk_widget_hide(m->label); gtk_label_set_text(GTK_LABEL(m->label), systemload_config_get_label (config, monitor)); color = systemload_config_get_color (config, monitor); if (G_LIKELY (color != NULL)) { #if GTK_CHECK_VERSION (3, 16, 0) gchar *color_str = gdk_rgba_to_string(color); gchar *css; #if GTK_CHECK_VERSION (3, 20, 0) css = g_strdup_printf("progressbar progress { background-color: %s; background-image: none; border-color: %s; }", color_str, color_str); #else css = g_strdup_printf(".progressbar progress { background-color: %s; background-image: none; }", color); #endif gtk_css_provider_load_from_data ( (GtkCssProvider*) g_object_get_data(G_OBJECT(m->status), "css_provider"), css, strlen(css), NULL); g_free(color_str); g_free(css); #else gtk_widget_override_background_color(m->status, GTK_STATE_FLAG_PRELIGHT, color); gtk_widget_override_background_color(m->status, GTK_STATE_FLAG_SELECTED, color); gtk_widget_override_color(m->status, GTK_STATE_FLAG_SELECTED, color); #endif } if (systemload_config_get_enabled (config, monitor)) { bool label_visible = systemload_config_get_use_label (config, monitor) && strlen (systemload_config_get_label (config, monitor)) != 0; gtk_widget_show_all(GTK_WIDGET(m->ebox)); gtk_widget_set_visible (m->label, label_visible); set_margin (global, m->ebox, (n_enabled_labels == 0) ? 0 : 6); } } if (systemload_config_get_uptime_enabled (config)) { gtk_widget_show_all (global->uptime.ebox); set_margin (global, global->uptime.ebox, (n_enabled == 0) ? 0 : 6); } setup_timer (global); } static gboolean setup_monitor_cb(gpointer user_data) { auto global = (t_global_monitor*) user_data; setup_monitors (global); update_monitors (global); return TRUE; } static gboolean monitor_set_size(XfcePanelPlugin *plugin, int size, t_global_monitor *global) { gtk_container_set_border_width (GTK_CONTAINER (global->ebox), (size > 26 ? 2 : 1)); for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { if (xfce_panel_plugin_get_orientation (plugin) == GTK_ORIENTATION_HORIZONTAL) { gtk_widget_set_size_request(GTK_WIDGET(global->monitor[i]->status), 8, -1); } else { gtk_widget_set_size_request(GTK_WIDGET(global->monitor[i]->status), -1, 8); } } setup_monitors (global); return TRUE; } static void monitor_set_mode (XfcePanelPlugin *plugin, XfcePanelPluginMode mode, t_global_monitor *global) { GtkOrientation panel_orientation = xfce_panel_plugin_get_orientation (plugin); GtkOrientation orientation = (mode == XFCE_PANEL_PLUGIN_MODE_VERTICAL) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL; /* Set small in all modes except deskbar mode */ if (mode == XFCE_PANEL_PLUGIN_MODE_DESKBAR) xfce_panel_plugin_set_small(XFCE_PANEL_PLUGIN(plugin), FALSE); else xfce_panel_plugin_set_small(XFCE_PANEL_PLUGIN(plugin), TRUE); monitor_update_orientation (plugin, panel_orientation, orientation, global); monitor_set_size (plugin, xfce_panel_plugin_get_size (plugin), global); } #ifdef HAVE_UPOWER_GLIB static void #if UP_CHECK_VERSION(0, 99, 0) upower_changed_cb(UpClient *client, GParamSpec *pspec, t_global_monitor *global) #else /* UP_CHECK_VERSION < 0.99 */ upower_changed_cb(UpClient *client, t_global_monitor *global) #endif /* UP_CHECK_VERSION */ { setup_timer(global); } #endif /* HAVE_UPOWER_GLIB */ static void command_entry_changed_cb(GtkEntry *entry, t_global_monitor *global) { g_free (global->command.command_text); global->command.command_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); global->command.enabled = (strlen (global->command.command_text) != 0); } static void switch_cb(GtkSwitch *check_button, gboolean state, t_global_monitor *global) { gpointer sensitive_widget = g_object_get_data (G_OBJECT (check_button), "sensitive_widget"); gtk_switch_set_state (check_button, state); if (sensitive_widget) gtk_revealer_set_reveal_child (GTK_REVEALER (sensitive_widget), state); } static void monitor_dialog_response (GtkWidget *dlg, int response, t_global_monitor *global) { if (response == GTK_RESPONSE_HELP) { xfce_dialog_show_help (GTK_WINDOW (dlg), PACKAGE_NAME, NULL, NULL); } else { gtk_widget_destroy (dlg); xfce_panel_plugin_unblock_menu (global->plugin); } } static void change_timeout_cb(GtkSpinButton *spin, t_global_monitor *global) { global->timeout = gtk_spin_button_get_value(spin); setup_timer(global); } #ifdef HAVE_UPOWER_GLIB static void change_timeout_seconds_cb(GtkSpinButton *spin, t_global_monitor *global) { global->timeout_seconds = gtk_spin_button_get_value (spin); global->use_timeout_seconds = (global->timeout_seconds != 0); setup_timer (global); } #endif /* Creates a label, its mnemonic will point to target. * Returns the widget. */ static GtkWidget *new_label (GtkGrid *grid, guint row, const gchar *labeltext, GtkWidget *target) { GtkWidget *label = gtk_label_new_with_mnemonic (labeltext); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_widget_set_valign(label, GTK_ALIGN_CENTER); gtk_widget_set_margin_start (label, 12); gtk_label_set_mnemonic_widget(GTK_LABEL(label), target); gtk_grid_attach(grid, label, 0, row, 1, 1); return label; } /* Create a new monitor setting with gtkswitch, and eventually a color button and a checkbox + entry * * extra_toggle_setting: optional boolean property of the config object to * expose as a checkbox under the label row; NULL for none. Used by the memory * monitor for "exclude zram". */ static void new_monitor_setting (t_global_monitor *global, GtkGrid *grid, int position, const gchar *title, bool color, const gchar *setting, const gchar *extra_toggle_setting = NULL, const gchar *extra_toggle_text = NULL) { GtkWidget *sw, *label; gchar *markup, *setting_name; gboolean enabled = TRUE; sw = gtk_switch_new(); gtk_widget_set_halign (sw, GTK_ALIGN_END); gtk_widget_set_valign (sw, GTK_ALIGN_CENTER); gtk_widget_set_margin_top (sw, 12); setting_name = g_strconcat (setting, "-enabled", NULL); g_object_get (G_OBJECT (global->config), setting_name, &enabled, NULL); g_object_bind_property (G_OBJECT (global->config), setting_name, G_OBJECT (sw), "active", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_signal_connect (GTK_WIDGET(sw), "state-set", G_CALLBACK(switch_cb), global); g_free (setting_name); markup = g_markup_printf_escaped ("%s", title); label = gtk_label_new (markup); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_widget_set_valign(label, GTK_ALIGN_CENTER); gtk_widget_set_margin_top (label, 12); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); g_free (markup); gtk_grid_attach(GTK_GRID(grid), label, 0, position, 1, 1); gtk_grid_attach(GTK_GRID(grid), sw, 1, position, 1, 1); if (g_strcmp0 (setting, "uptime") != 0) { GtkWidget *revealer = gtk_revealer_new (); GtkWidget *subgrid = gtk_grid_new (); gtk_container_add (GTK_CONTAINER (revealer), subgrid); gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE); g_object_set_data (G_OBJECT(sw), "sensitive_widget", revealer); gtk_grid_attach(GTK_GRID(grid), revealer, 0, position + 1, 2, 1); gtk_grid_set_column_spacing (GTK_GRID(subgrid), 12); gtk_grid_set_row_spacing (GTK_GRID(subgrid), 6); label = gtk_label_new_with_mnemonic (_("Label:")); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_widget_set_valign (label, GTK_ALIGN_CENTER); gtk_widget_set_margin_start (label, 12); gtk_grid_attach (GTK_GRID(subgrid), label, 0, 0, 1, 1); /* Entry for the optional monitor label */ GtkWidget *entry = gtk_entry_new (); gtk_widget_set_hexpand (entry, TRUE); gtk_widget_set_tooltip_text (entry, _("Leave empty to disable the label")); setting_name = g_strconcat (setting, "-label", NULL); g_object_bind_property (G_OBJECT (global->config), setting_name, G_OBJECT (entry), "text", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_free (setting_name); gtk_grid_attach(GTK_GRID(subgrid), entry, 1, 0, 1, 1); if (color) { /* Colorbutton to set the progressbar color */ GtkWidget *button = gtk_color_button_new (); gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (button), TRUE); gtk_label_set_mnemonic_widget (GTK_LABEL (label), button); gtk_widget_set_halign(button, GTK_ALIGN_START); gtk_widget_set_margin_start (button, 12); setting_name = g_strconcat (setting, "-color", NULL); g_object_bind_property (G_OBJECT (global->config), setting_name, G_OBJECT (button), "rgba", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_free (setting_name); gtk_grid_attach(GTK_GRID(subgrid), button, 2, 0, 1, 1); } if (extra_toggle_setting != NULL) { GtkWidget *check = gtk_check_button_new_with_mnemonic (extra_toggle_text); gtk_widget_set_margin_start (check, 12); g_object_bind_property (G_OBJECT (global->config), extra_toggle_setting, G_OBJECT (check), "active", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); gtk_grid_attach (GTK_GRID(subgrid), check, 0, 1, 3, 1); } } else { GtkWidget *revealer = gtk_revealer_new (); GtkWidget *subgrid = gtk_grid_new (); gtk_container_add (GTK_CONTAINER (revealer), subgrid); gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE); g_object_set_data (G_OBJECT(sw), "sensitive_widget", revealer); gtk_grid_attach(GTK_GRID(grid), revealer, 0, position + 1, 2, 1); gtk_grid_set_column_spacing (GTK_GRID(subgrid), 12); gtk_grid_set_row_spacing (GTK_GRID(subgrid), 6); label = gtk_label_new_with_mnemonic (_("_Format:")); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_widget_set_valign (label, GTK_ALIGN_CENTER); gtk_widget_set_margin_start (label, 12); gtk_grid_attach (GTK_GRID(subgrid), label, 0, 0, 1, 1); GtkWidget *entry = gtk_entry_new (); gtk_widget_set_hexpand (entry, TRUE); gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry); gtk_entry_set_placeholder_text (GTK_ENTRY (entry), _("built-in format")); gtk_widget_set_tooltip_text (entry, _("%d days, %h hours, %m minutes, %s seconds\n" "%H %M %S - the same with two digits (07)\n" "%t - total hours, %% - percent sign\n" "\\n - new line\n" "{...} - hidden when every number inside is 0\n" "Example: {%dd }%H:%M\n" "Leave empty for the built-in format")); g_object_bind_property (G_OBJECT (global->config), "uptime-format", G_OBJECT (entry), "text", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); gtk_grid_attach(GTK_GRID(subgrid), entry, 1, 0, 1, 1); } switch_cb (GTK_SWITCH (sw), enabled, global); } static void monitor_create_options(XfcePanelPlugin *plugin, t_global_monitor *global) { SystemloadConfig *config = global->config; GtkWidget *label, *entry, *button, *box; /* Indices must match enum SystemloadMonitor; uptime has no enum entry and * therefore sits at N_MONITORS, right after the last real monitor. */ static const gchar *FRAME_TEXT[] = { N_ ("CPU monitor"), N_ ("Memory monitor"), N_ ("Network monitor"), N_ ("Swap monitor"), N_ ("ZRAM monitor"), N_ ("Uptime monitor") }; static const gchar *SETTING_TEXT[] = { "cpu", "memory", "network", "swap", "zram" }; xfce_panel_plugin_block_menu (plugin); GtkWidget *dlg; dlg = xfce_titled_dialog_new_with_mixed_buttons (_("System Load Monitor"), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (plugin))), GTK_DIALOG_DESTROY_WITH_PARENT, "window-close-symbolic", _("_Close"), GTK_RESPONSE_OK, "help-browser", _("_Help"), GTK_RESPONSE_HELP, NULL); g_signal_connect (G_OBJECT (dlg), "response", G_CALLBACK (monitor_dialog_response), global); gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER); gtk_window_set_icon_name (GTK_WINDOW (dlg), "org.xfce.panel.systemload"); GtkBox *content = GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(dlg))); GtkWidget *grid = gtk_grid_new(); gtk_grid_set_column_spacing (GTK_GRID(grid), 12); gtk_grid_set_row_spacing (GTK_GRID(grid), 6); gtk_container_set_border_width(GTK_CONTAINER(grid), 12); gtk_box_pack_start (content, grid, TRUE, TRUE, 0); label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (label), _("General")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_widget_set_valign(label, GTK_ALIGN_CENTER); gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1); /* Update interval */ button = gtk_spin_button_new_with_range (MIN_TIMEOUT, MAX_TIMEOUT, 50); gtk_label_set_mnemonic_widget (GTK_LABEL(label), button); gtk_widget_set_halign (button, GTK_ALIGN_START); gtk_spin_button_set_value (GTK_SPIN_BUTTON (button), (gfloat)global->timeout); g_object_bind_property (G_OBJECT (config), "timeout", G_OBJECT (button), "value", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_signal_connect (G_OBJECT (button), "value-changed", G_CALLBACK(change_timeout_cb), global); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new ("ms"); gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); gtk_grid_attach (GTK_GRID (grid), box, 1, 1, 1, 1); new_label (GTK_GRID (grid), 1, _("Update interval:"), button); #ifdef HAVE_UPOWER_GLIB /* Power-saving interval */ button = gtk_spin_button_new_with_range (0, 10, 1); gtk_widget_set_halign (button, GTK_ALIGN_START); gtk_widget_set_tooltip_text(GTK_WIDGET(button), _("Update interval when running on battery (uses regular update interval if set to zero)")); gtk_spin_button_set_value (GTK_SPIN_BUTTON (button), (gfloat)global->timeout_seconds); g_object_bind_property (G_OBJECT (config), "timeout-seconds", G_OBJECT (button), "value", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_signal_connect (G_OBJECT (button), "value-changed", G_CALLBACK(change_timeout_seconds_cb), global); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new ("s"); gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); gtk_grid_attach (GTK_GRID (grid), box, 1, 2, 1, 1); new_label (GTK_GRID (grid), 2, _("Power-saving interval:"), button); #endif /* System Monitor */ entry = gtk_entry_new (); gtk_widget_set_hexpand (entry, TRUE); gtk_entry_set_text (GTK_ENTRY(entry), global->command.command_text); gtk_widget_set_tooltip_text(GTK_WIDGET(entry), _("Launched when clicking on the plugin")); g_object_bind_property (G_OBJECT (config), "system-monitor-command", G_OBJECT (entry), "text", GBindingFlags (G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL)); g_signal_connect (G_OBJECT(entry), "changed", G_CALLBACK(command_entry_changed_cb), global); gtk_grid_attach (GTK_GRID (grid), entry, 1, 3, 1, 1); label = new_label (GTK_GRID (grid), 3, _("System monitor:"), entry); /* Add options for the monitors */ for(gsize i = 0; i < G_N_ELEMENTS (global->monitor); i++) { const SystemloadMonitor monitor = VISUAL_ORDER[i]; new_monitor_setting (global, GTK_GRID(grid), 4 + 2 * i, _(FRAME_TEXT[monitor]), true, SETTING_TEXT[monitor], (monitor == MEM_MONITOR) ? "memory-exclude-zram" : NULL, (monitor == MEM_MONITOR) ? _("Do not count the RAM held by _zram") : NULL); } /* Uptime monitor options */ new_monitor_setting (global, GTK_GRID(grid), 4 + 2*G_N_ELEMENTS (global->monitor), _(FRAME_TEXT[N_MONITORS]), FALSE, "uptime"); gtk_widget_show_all (dlg); } static void monitor_show_about(XfcePanelPlugin *plugin, t_global_monitor *global) { /* List of authors (in alphabetical order) */ const gchar *auth[] = { "David Schneider ", "Florian Rivoal ", "Jan Ziak <0xe2.0x9a.0x9b@xfce.org>", "Landry Breuil ", "Riccardo Persichetti ", "Simon Steinbeiß ", NULL }; gtk_show_about_dialog (NULL, "logo-icon-name", "org.xfce.panel.systemload", "license", xfce_get_license_text (XFCE_LICENSE_TEXT_BSD), "version", PACKAGE_VERSION, "program-name", PACKAGE_NAME, "comments", _("Monitor CPU load, swap usage and memory footprint"), "website", "https://docs.xfce.org/panel-plugins/xfce4-systemload-plugin/start", "copyright", _("Copyright (c) 2003-2022\n"), "authors", auth, NULL); } void systemload_construct (XfcePanelPlugin *plugin) { xfce_textdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8"); t_global_monitor *global = monitor_control_new (plugin); create_monitor (global); monitor_set_mode (plugin, xfce_panel_plugin_get_mode (plugin), global); setup_monitors (global); gtk_container_add (GTK_CONTAINER (plugin), global->ebox); update_monitors (global); #ifdef HAVE_UPOWER_GLIB if (global->upower) { #if UP_CHECK_VERSION(0, 99, 0) g_signal_connect (global->upower, "notify", G_CALLBACK(upower_changed_cb), global); #else /* UP_CHECK_VERSION < 0.99 */ g_signal_connect (global->upower, "changed", G_CALLBACK(upower_changed_cb), global); #endif /* UP_CHECK_VERSION */ } #endif /* HAVE_UPOWER_GLIB */ g_signal_connect (plugin, "free-data", G_CALLBACK (monitor_free), global); g_signal_connect (plugin, "size-changed", G_CALLBACK (monitor_set_size), global); g_signal_connect (plugin, "mode-changed", G_CALLBACK (monitor_set_mode), global); g_signal_connect (plugin, "button-press-event", G_CALLBACK (click_event), global); xfce_panel_plugin_menu_show_configure (plugin); g_signal_connect (plugin, "configure-plugin", G_CALLBACK (monitor_create_options), global); xfce_panel_plugin_menu_show_about(plugin); g_signal_connect (plugin, "about", G_CALLBACK (monitor_show_about), global); }