/* Procman * Copyright (C) 2001 Kevin Vandersloot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include <config.h> #include <stdlib.h> #include <locale.h> #include <gtkmm/main.h> #include <giomm/volumemonitor.h> #include <giomm/init.h> #include <glib.h> #include <glib/gi18n.h> #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <bacon-message-connection.h> #include <glibtop.h> #include <glibtop/close.h> #include <glibtop/loadavg.h> #include "load-graph.h" #include "procman.h" #include "interface.h" #include "proctable.h" #include "prettytable.h" #include "callbacks.h" #include "smooth_refresh.h" #include "util.h" #include "settings-keys.h" #include "argv.h" ProcData::ProcData() : tree(NULL), cpu_graph(NULL), mem_graph(NULL), net_graph(NULL), selected_process(NULL), timeout(0), disk_timeout(0), cpu_total_time(1), cpu_total_time_last(1) { } ProcData* ProcData::get_instance() { static ProcData instance; return &instance; } static gboolean has_key (gchar **keys, const gchar *key) { gchar **loop = keys; while (*loop) { if (!strcmp (*loop++, key)) return TRUE; } return FALSE; } static void tree_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); procdata->config.show_tree = g_settings_get_boolean(settings, key); g_object_set(G_OBJECT(procdata->tree), "show-expanders", procdata->config.show_tree, NULL); proctable_clear_tree (procdata); proctable_update_all (procdata); } static void solaris_mode_changed_cb(GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); procdata->config.solaris_mode = g_settings_get_boolean(settings, key); proctable_update_all (procdata); } static void network_in_bits_changed_cb(GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); procdata->config.network_in_bits = g_settings_get_boolean(settings, key); procdata->net_graph->clear_background(); } static void view_as_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); procdata->config.whose_process = g_settings_get_int (settings, key); procdata->config.whose_process = CLAMP (procdata->config.whose_process, 0, 2); proctable_clear_tree (procdata); proctable_update_all (procdata); } static void warning_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); if (g_str_equal (key, "kill-dialog")) { procdata->config.show_kill_warning = g_settings_get_boolean (settings, key); } } static void timeouts_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); if (g_str_equal (key, "update-interval")) { procdata->config.update_interval = g_settings_get_int (settings, key); procdata->config.update_interval = MAX (procdata->config.update_interval, 1000); procdata->smooth_refresh->reset(); if(procdata->timeout) { g_source_remove (procdata->timeout); procdata->timeout = g_timeout_add (procdata->config.update_interval, cb_timeout, procdata); } } else if (g_str_equal (key, "graph-update-interval")){ procdata->config.graph_update_interval = g_settings_get_int (settings, key); procdata->config.graph_update_interval = MAX (procdata->config.graph_update_interval, 250); load_graph_change_speed(procdata->cpu_graph, procdata->config.graph_update_interval); load_graph_change_speed(procdata->mem_graph, procdata->config.graph_update_interval); load_graph_change_speed(procdata->net_graph, procdata->config.graph_update_interval); } else if (g_str_equal(key, "disks-interval")) { procdata->config.disks_update_interval = g_settings_get_int (settings, key); procdata->config.disks_update_interval = MAX (procdata->config.disks_update_interval, 1000); if(procdata->disk_timeout) { g_source_remove (procdata->disk_timeout); procdata->disk_timeout = \ g_timeout_add (procdata->config.disks_update_interval, cb_update_disks, procdata); } } else { g_assert_not_reached(); } } static void color_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData * const procdata = static_cast<ProcData*>(data); gchar *color = g_settings_get_string (settings, key); if (g_str_has_prefix (key, "cpu-color")) { for (int i = 0; i < procdata->config.num_cpus; i++) { string cpu_key = make_string(g_strdup_printf("cpu-color%d", i)); if (cpu_key == key) { gdk_color_parse (color, &procdata->config.cpu_color[i]); procdata->cpu_graph->colors.at(i) = procdata->config.cpu_color[i]; break; } } } else if (g_str_equal (key, "mem-color")) { gdk_color_parse (color, &procdata->config.mem_color); procdata->mem_graph->colors.at(0) = procdata->config.mem_color; } else if (g_str_equal (key, "swap-color")) { gdk_color_parse (color, &procdata->config.swap_color); procdata->mem_graph->colors.at(1) = procdata->config.swap_color; } else if (g_str_equal (key, "net-in-color")) { gdk_color_parse (color, &procdata->config.net_in_color); procdata->net_graph->colors.at(0) = procdata->config.net_in_color; } else if (g_str_equal (key, "net-out-color")) { gdk_color_parse (color, &procdata->config.net_out_color); procdata->net_graph->colors.at(1) = procdata->config.net_out_color; } else { g_assert_not_reached(); } g_free (color); } static void show_all_fs_changed_cb (GSettings *settings, const gchar *key, gpointer data) { ProcData * const procdata = static_cast<ProcData*>(data); procdata->config.show_all_fs = g_settings_get_boolean (settings, key); cb_update_disks (data); } static ProcData * procman_data_new (GSettings *settings) { ProcData *pd; gchar *color; gchar **keys; gint swidth, sheight; gint i; glibtop_cpu cpu; pd = ProcData::get_instance(); g_settings_get (settings, "window-state", "(iiii)", &pd->config.width, &pd->config.height, &pd->config.xpos, &pd->config.ypos); pd->config.show_tree = g_settings_get_boolean (settings, "show-tree"); g_signal_connect (G_OBJECT(settings), "changed::show-tree", G_CALLBACK(tree_changed_cb), pd); pd->config.solaris_mode = g_settings_get_boolean(settings, procman::settings::solaris_mode.c_str()); std::string detail_string("changed::" + procman::settings::solaris_mode); g_signal_connect(G_OBJECT(settings), detail_string.c_str(), G_CALLBACK(solaris_mode_changed_cb), pd); pd->config.network_in_bits = g_settings_get_boolean(settings, procman::settings::network_in_bits.c_str()); detail_string = "changed::" + procman::settings::network_in_bits; g_signal_connect(G_OBJECT(settings), detail_string.c_str(), G_CALLBACK(network_in_bits_changed_cb), pd); pd->config.show_kill_warning = g_settings_get_boolean (settings, "kill-dialog"); g_signal_connect (G_OBJECT(settings), "changed::kill-dialog", G_CALLBACK(warning_changed_cb), pd); pd->config.update_interval = g_settings_get_int (settings, "update-interval"); g_signal_connect (G_OBJECT(settings), "changed::update-interval", G_CALLBACK(timeouts_changed_cb), pd); pd->config.graph_update_interval = g_settings_get_int (settings, "graph-update-interval"); g_signal_connect (G_OBJECT(settings), "changed::graph-update-interval", G_CALLBACK(timeouts_changed_cb), pd); pd->config.disks_update_interval = g_settings_get_int (settings, "disks-interval"); g_signal_connect (G_OBJECT(settings), "changed::disks-interval", G_CALLBACK(timeouts_changed_cb), pd); /* show_all_fs */ pd->config.show_all_fs = g_settings_get_boolean (settings, "show-all-fs"); g_signal_connect (settings, "changed::show-all-fs", G_CALLBACK(show_all_fs_changed_cb), pd); pd->config.whose_process = g_settings_get_int (settings, "view-as"); g_signal_connect (G_OBJECT(settings), "changed::view-as", G_CALLBACK(view_as_changed_cb),pd); pd->config.current_tab = g_settings_get_int (settings, "current-tab"); /* Determinie number of cpus since libgtop doesn't really tell you*/ pd->config.num_cpus = 0; glibtop_get_cpu (&cpu); pd->frequency = cpu.frequency; i=0; while (i < GLIBTOP_NCPU && cpu.xcpu_total[i] != 0) { pd->config.num_cpus ++; i++; } if (pd->config.num_cpus == 0) pd->config.num_cpus = 1; keys = g_settings_list_keys (settings); for (int i = 0; i < pd->config.num_cpus; i++) { gchar *key; key = g_strdup_printf ("cpu-color%d", i); if (has_key (keys, key)) color = g_settings_get_string (settings, key); else color = g_strdup ("#f25915e815e8"); detail_string = std::string("changed::") + std::string(key); g_signal_connect (G_OBJECT(settings), detail_string.c_str(), G_CALLBACK(color_changed_cb), pd); gdk_color_parse(color, &pd->config.cpu_color[i]); g_free (color); g_free (key); } g_strfreev (keys); color = g_settings_get_string (settings, "mem-color"); if (!color) color = g_strdup ("#000000ff0082"); g_signal_connect (G_OBJECT(settings), "changed::mem-color", G_CALLBACK(color_changed_cb), pd); gdk_color_parse(color, &pd->config.mem_color); g_free (color); color = g_settings_get_string (settings, "swap-color"); if (!color) color = g_strdup ("#00b6000000ff"); g_signal_connect (G_OBJECT(settings), "changed::swap-color", G_CALLBACK(color_changed_cb), pd); gdk_color_parse(color, &pd->config.swap_color); g_free (color); color = g_settings_get_string (settings, "net-in-color"); if (!color) color = g_strdup ("#000000f200f2"); g_signal_connect (G_OBJECT(settings), "changed::net-in-color", G_CALLBACK(color_changed_cb), pd); gdk_color_parse(color, &pd->config.net_in_color); g_free (color); color = g_settings_get_string (settings, "net-out-color"); if (!color) color = g_strdup ("#00f2000000c1"); g_signal_connect (G_OBJECT(settings), "changed::net-out-color", G_CALLBACK(color_changed_cb), pd); gdk_color_parse(color, &pd->config.net_out_color); g_free (color); /* Sanity checks */ swidth = gdk_screen_width (); sheight = gdk_screen_height (); pd->config.width = CLAMP (pd->config.width, 50, swidth); pd->config.height = CLAMP (pd->config.height, 50, sheight); pd->config.update_interval = MAX (pd->config.update_interval, 1000); pd->config.graph_update_interval = MAX (pd->config.graph_update_interval, 250); pd->config.disks_update_interval = MAX (pd->config.disks_update_interval, 1000); pd->config.whose_process = CLAMP (pd->config.whose_process, 0, 2); pd->config.current_tab = CLAMP(pd->config.current_tab, PROCMAN_TAB_SYSINFO, PROCMAN_TAB_DISKS); // delayed initialization as SmoothRefresh() needs ProcData // i.e. we can't call ProcData::get_instance pd->smooth_refresh = new SmoothRefresh(settings); return pd; } static void procman_free_data (ProcData *procdata) { proctable_free_table (procdata); delete procdata->smooth_refresh; } gboolean procman_get_tree_state (GSettings *settings, GtkWidget *tree, const gchar *child_schema) { GtkTreeModel *model; GList *columns, *it; gint sort_col; GtkSortType order; g_assert(tree); g_assert(child_schema); GSettings *pt_settings = g_settings_get_child (settings, child_schema); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree)); sort_col = g_settings_get_int (pt_settings, "sort-col"); order = static_cast<GtkSortType>(g_settings_get_int (pt_settings, "sort-order")); if (sort_col != -1) gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), sort_col, order); columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree)); if(!g_strcmp0(child_schema, "proctree")) { for(it = columns; it; it = it->next) { GtkTreeViewColumn *column; gint width; gboolean visible; int id; gchar *key; column = static_cast<GtkTreeViewColumn*>(it->data); id = gtk_tree_view_column_get_sort_column_id (column); key = g_strdup_printf ("col-%d-width", id); g_settings_get (pt_settings, key, "i", &width); g_free (key); key = g_strdup_printf ("col-%d-visible", id); visible = g_settings_get_boolean (pt_settings, key); g_free (key); column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), id); if(!column) continue; gtk_tree_view_column_set_visible (column, visible); if (visible) { /* ensure column is really visible */ width = MAX(width, 10); gtk_tree_view_column_set_fixed_width(column, width); } } } if(!g_strcmp0(child_schema, "proctree") || !g_strcmp0(child_schema, "disktreenew")) { GVariant *value; GVariantIter iter; int sortIndex; GSList *order = NULL; value = g_settings_get_value(pt_settings, "columns-order"); g_variant_iter_init(&iter, value); while (g_variant_iter_loop (&iter, "i", &sortIndex)) order = g_slist_append(order, GINT_TO_POINTER(sortIndex)); proctable_set_columns_order(GTK_TREE_VIEW(tree), order); g_variant_unref(value); g_slist_free(order); } g_object_unref(pt_settings); pt_settings = NULL; g_list_free(columns); return TRUE; } void procman_save_tree_state (GSettings *settings, GtkWidget *tree, const gchar *child_schema) { GtkTreeModel *model; GList *columns; gint sort_col; GtkSortType order; g_assert(tree); g_assert(child_schema); GSettings *pt_settings = g_settings_get_child (settings, child_schema); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree)); if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), &sort_col, &order)) { g_settings_set_int (pt_settings, "sort-col", sort_col); g_settings_set_int (pt_settings, "sort-order", order); } columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree)); if(!g_strcmp0(child_schema, "proctree") || !g_strcmp0(child_schema, "disktreenew")) { GSList *order; GSList *order_node; GVariantBuilder *builder; GVariant *order_variant; order = proctable_get_columns_order(GTK_TREE_VIEW(tree)); builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); for(order_node = order; order_node; order_node = order_node->next) g_variant_builder_add(builder, "i", GPOINTER_TO_INT(order_node->data)); order_variant = g_variant_new("ai", builder); g_settings_set_value(pt_settings, "columns-order", order_variant); g_slist_free(order); } g_list_free(columns); } void procman_save_config (ProcData *data) { GSettings *settings = data->settings; g_assert(data); data->config.width = gdk_window_get_width(gtk_widget_get_window(data->app)); data->config.height = gdk_window_get_height(gtk_widget_get_window(data->app)); gtk_window_get_position(GTK_WINDOW(data->app), &data->config.xpos, &data->config.ypos); g_settings_set (settings, "window-state", "(iiii)", data->config.width, data->config.height, data->config.xpos, data->config.ypos); g_settings_set_int (settings, "current-tab", data->config.current_tab); } static guint32 get_startup_timestamp () { const gchar *startup_id_env; gchar *startup_id = NULL; gchar *time_str; gulong retval = 0; /* we don't unset the env, since startup-notification * may still need it */ startup_id_env = g_getenv ("DESKTOP-STARTUP-ID"); if (startup_id_env == NULL) goto out; startup_id = g_strdup (startup_id_env); time_str = g_strrstr (startup_id, "_TIME"); if (time_str == NULL) goto out; /* Skip past the "_TIME" part */ time_str += 5; retval = strtoul (time_str, NULL, 0); out: g_free (startup_id); return retval; } static void set_tab(GtkNotebook* notebook, gint tab, ProcData* procdata) { gtk_notebook_set_current_page(notebook, tab); cb_change_current_page(notebook, tab, procdata); } static void cb_server (const gchar *msg, gpointer user_data) { GdkWindow *window; ProcData *procdata; guint32 timestamp = 0; window = gdk_get_default_root_window (); procdata = *(ProcData**)user_data; g_assert (procdata != NULL); procman_debug("cb_server(%s)", msg); if (msg != NULL) { if (procman::SHOW_SYSTEM_TAB_CMD == msg) { procman_debug("Changing to PROCMAN_TAB_SYSINFO via bacon message"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO, procdata); } else if (procman::SHOW_PROCESSES_TAB_CMD == msg) { procman_debug("Changing to PROCMAN_TAB_PROCESSES via bacon message"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_PROCESSES, procdata); } else if (procman::SHOW_RESOURCES_TAB_CMD == msg) { procman_debug("Changing to PROCMAN_TAB_RESOURCES via bacon message"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_RESOURCES, procdata); } else if (procman::SHOW_FILE_SYSTEMS_TAB_CMD == msg) { procman_debug("Changing to PROCMAN_TAB_DISKS via bacon message"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_DISKS, procdata); } } else timestamp = strtoul(msg, NULL, 0); if (timestamp == 0) { /* fall back to rountripping to X */ timestamp = gdk_x11_get_server_time (window); } gdk_x11_window_set_user_time (window, timestamp); gtk_window_present (GTK_WINDOW(procdata->app)); } static void mount_changed(const Glib::RefPtr<Gio::Mount>&) { cb_update_disks(ProcData::get_instance()); } static void init_volume_monitor(ProcData *procdata) { using namespace Gio; using namespace Glib; RefPtr<VolumeMonitor> monitor = VolumeMonitor::get(); monitor->signal_mount_added().connect(sigc::ptr_fun(&mount_changed)); monitor->signal_mount_changed().connect(sigc::ptr_fun(&mount_changed)); monitor->signal_mount_removed().connect(sigc::ptr_fun(&mount_changed)); } namespace procman { const std::string SHOW_SYSTEM_TAB_CMD("SHOWSYSTAB"); const std::string SHOW_PROCESSES_TAB_CMD("SHOWPROCTAB"); const std::string SHOW_RESOURCES_TAB_CMD("SHOWRESTAB"); const std::string SHOW_FILE_SYSTEMS_TAB_CMD("SHOWFSTAB"); } int main (int argc, char *argv[]) { guint32 startup_timestamp; GSettings *settings; ProcData *procdata; BaconMessageConnection *conn; bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); setlocale (LC_ALL, ""); startup_timestamp = get_startup_timestamp(); Glib::OptionContext context; context.set_summary(_("A simple process and system monitor.")); context.set_ignore_unknown_options(true); procman::OptionGroup option_group; context.set_main_group(option_group); try { context.parse(argc, argv); } catch (const Glib::Error& ex) { g_error("Arguments parse error : %s", ex.what().c_str()); } Gio::init(); Gtk::Main kit(&argc, &argv); procman_debug("post gtk_init"); conn = bacon_message_connection_new ("mate-system-monitor"); if (!conn) g_error("Couldn't connect to mate-system-monitor"); if (bacon_message_connection_get_is_server (conn)) { bacon_message_connection_set_callback (conn, cb_server, &procdata); } else /* client */ { char *timestamp; timestamp = g_strdup_printf ("%" G_GUINT32_FORMAT, startup_timestamp); if (option_group.show_system_tab) bacon_message_connection_send(conn, procman::SHOW_SYSTEM_TAB_CMD.c_str()); if (option_group.show_processes_tab) bacon_message_connection_send(conn, procman::SHOW_PROCESSES_TAB_CMD.c_str()); if (option_group.show_resources_tab) bacon_message_connection_send(conn, procman::SHOW_RESOURCES_TAB_CMD.c_str()); if (option_group.show_file_systems_tab) bacon_message_connection_send(conn, procman::SHOW_FILE_SYSTEMS_TAB_CMD.c_str()); bacon_message_connection_send (conn, timestamp); gdk_notify_startup_complete (); g_free (timestamp); bacon_message_connection_free (conn); exit (0); } gtk_window_set_default_icon_name ("utilities-system-monitor"); g_set_application_name(_("System Monitor")); settings = g_settings_new (GSM_GSETTINGS_SCHEMA); glibtop_init (); procman_debug("end init"); procdata = procman_data_new (settings); procdata->settings = g_settings_new(GSM_GSETTINGS_SCHEMA); procman_debug("begin create_main_window"); create_main_window (procdata); procman_debug("end create_main_window"); // proctable_update_all (procdata); init_volume_monitor (procdata); g_assert(procdata->app); if (option_group.show_system_tab) { procman_debug("Starting with PROCMAN_TAB_SYSINFO by commandline request"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_SYSINFO, procdata); } else if (option_group.show_processes_tab) { procman_debug("Starting with PROCMAN_TAB_PROCESSES by commandline request"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_PROCESSES, procdata); } else if (option_group.show_resources_tab) { procman_debug("Starting with PROCMAN_TAB_RESOURCES by commandline request"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_RESOURCES, procdata); } else if (option_group.show_file_systems_tab) { procman_debug("Starting with PROCMAN_TAB_DISKS by commandline request"); set_tab(GTK_NOTEBOOK(procdata->notebook), PROCMAN_TAB_DISKS, procdata); } gtk_widget_set_name(procdata->app, "mate-system-monitor"); gtk_widget_show(procdata->app); procman_debug("begin gtk_main"); kit.run(); procman_free_data (procdata); glibtop_close (); return 0; }