diff --git a/src/process-tree-view.c b/src/process-tree-view.c index 450681b..5b0bdcc 100644 --- a/src/process-tree-view.c +++ b/src/process-tree-view.c @@ -16,6 +16,7 @@ #include #include "process-tree-view.h" +#include "task-manager.h" #include "settings.h" @@ -34,9 +35,9 @@ struct _XtmProcessTreeView }; G_DEFINE_TYPE (XtmProcessTreeView, xtm_process_tree_view, GTK_TYPE_TREE_VIEW) -static gboolean treeview_clicked (XtmProcessTreeView *treeview, GdkEventButton *event); -static void settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treeview); -static int sort_by_string (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data); +static gboolean treeview_clicked (XtmProcessTreeView *treeview, GdkEventButton *event); +static void settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treeview); +static int sort_by_string (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data); @@ -136,13 +137,144 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview) * Helper functions */ +static void +cb_send_signal (GtkMenuItem *mi, gpointer user_data) +{ + guint pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mi), "pid")); + gint signal = GPOINTER_TO_INT (user_data); + + if (!send_signal_to_pid (pid, signal)) + { + GtkWidget *dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("Error sending signal")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("An error was encountered by sending a signal to the PID %d. " + "It is likely you don't have the required privileges."), pid); + gtk_window_set_title (GTK_WINDOW (dialog), _("Task Manager")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } +} + +static void +cb_set_priority (GtkMenuItem *mi, gpointer user_data) +{ + guint pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mi), "pid")); + gint priority = GPOINTER_TO_INT (user_data); + + if (!set_priority_to_pid (pid, priority)) + { + GtkWidget *dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("Error setting priority")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("An error was encountered by setting a priority to the PID %d. " + "It is likely you don't have the required privileges."), pid); + gtk_window_set_title (GTK_WINDOW (dialog), _("Task Manager")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } +} + +static GtkWidget * +build_context_menu (guint pid) +{ + GtkWidget *menu, *menu_priority, *mi; + + menu = gtk_menu_new (); + + mi = gtk_menu_item_new_with_label (_("Terminate")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_TERMINATE)); + + // TODO look up task for building menu with either Stop or Continue and in an OS-independent way + // if (pid_is_sleeping (pid)) + mi = gtk_menu_item_new_with_label (_("Stop")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_STOP)); + + mi = gtk_menu_item_new_with_label (_("Continue")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_CONTINUE)); + + mi = gtk_menu_item_new_with_label (_("Kill")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_KILL)); + + menu_priority = gtk_menu_new (); + + mi = gtk_menu_item_new_with_label (_("Very low")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu_priority), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_VERY_LOW)); + + mi = gtk_menu_item_new_with_label (_("Low")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu_priority), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_LOW)); + + mi = gtk_menu_item_new_with_label (_("Normal")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu_priority), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_NORMAL)); + + mi = gtk_menu_item_new_with_label (_("High")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu_priority), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_HIGH)); + + mi = gtk_menu_item_new_with_label (_("Very high")); + g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); + gtk_container_add (GTK_CONTAINER (menu_priority), mi); + g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_VERY_HIGH)); + + mi = gtk_menu_item_new_with_label (_("Priority")); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu_priority); + gtk_container_add (GTK_CONTAINER (menu), mi); + + gtk_widget_show_all (menu); + + return menu; +} + static gboolean treeview_clicked (XtmProcessTreeView *treeview, GdkEventButton *event) { + static GtkWidget *menu = NULL; + guint pid; + if (event->button != 3) return FALSE; - g_debug ("popup menu"); + if (menu != NULL) + gtk_widget_destroy (menu); + + { + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreePath *path; + GtkTreeIter iter; + + model = GTK_TREE_MODEL (treeview->model); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL); + + if (path == NULL) + return; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, XTM_PTV_COLUMN_PID, &pid, -1); + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); + +#if DEBUG + g_debug ("Found iter with pid %d", pid); +#endif + } + + menu = build_context_menu (pid); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); return TRUE; } diff --git a/src/task-manager-linux.c b/src/task-manager-linux.c index 6a5e2c0..3d61ab7 100644 --- a/src/task-manager-linux.c +++ b/src/task-manager-linux.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include @@ -305,3 +307,64 @@ get_task_list (GArray *task_list) return TRUE; } +gboolean +pid_is_sleeping (guint pid) +{ + return TRUE; +} + +gboolean +send_signal_to_pid (guint pid, gint signal) +{ + gint sig; + gint res; + switch (signal) + { + case XTM_SIGNAL_TERMINATE: + sig = SIGTERM; + break; + case XTM_SIGNAL_STOP: + sig = SIGSTOP; + break; + case XTM_SIGNAL_CONTINUE: + sig = SIGCONT; + break; + case XTM_SIGNAL_KILL: + sig = SIGKILL; + break; + default: + return TRUE; + } + res = kill (pid, sig); + return (res == 0) ? TRUE : FALSE; +} + +gboolean +set_priority_to_pid (guint pid, gint priority) +{ + gint prio; + gint res; + switch (priority) + { + case XTM_PRIORITY_VERY_LOW: + prio = 15; + break; + case XTM_PRIORITY_LOW: + prio = 5; + break; + case XTM_PRIORITY_NORMAL: + prio = 0; + break; + case XTM_PRIORITY_HIGH: + prio = -5; + break; + case XTM_PRIORITY_VERY_HIGH: + prio = -15; + break; + default: + return TRUE; + } + res = setpriority (PRIO_PROCESS, pid, prio); + return (res == 0) ? TRUE : FALSE; +} + diff --git a/src/task-manager.c b/src/task-manager.c index e849b34..b481c83 100644 --- a/src/task-manager.c +++ b/src/task-manager.c @@ -352,13 +352,3 @@ xtm_task_manager_update_model (XtmTaskManager *manager) return; } -void -xtm_task_manager_send_signal_to_pid (XtmTaskManager *manager) -{ -} - -void -xtm_task_manager_set_priority_to_pid (XtmTaskManager *manager) -{ -} - diff --git a/src/task-manager.h b/src/task-manager.h index 01ca19c..b82eaea 100644 --- a/src/task-manager.h +++ b/src/task-manager.h @@ -15,6 +15,7 @@ #endif #include +#include /** * Task struct used as elements of a task list GArray. @@ -37,6 +38,27 @@ struct _Task gshort prio; }; +/** + * Enumerations of virtual values between the interface and the OS implementation. + */ + +enum +{ + XTM_SIGNAL_TERMINATE = 0, + XTM_SIGNAL_STOP, + XTM_SIGNAL_CONTINUE, + XTM_SIGNAL_KILL, +}; + +enum +{ + XTM_PRIORITY_VERY_LOW = 0, + XTM_PRIORITY_LOW, + XTM_PRIORITY_NORMAL, + XTM_PRIORITY_HIGH, + XTM_PRIORITY_VERY_HIGH, +}; + /** * OS specific implementation. */ @@ -44,8 +66,9 @@ struct _Task gboolean get_memory_usage (guint64 *memory_total, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free); gboolean get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system); gboolean get_task_list (GArray *task_list); -//void send_signal_to_task (gint task_id, gint signal); -//void set_priority_to_task (gint task_id, gint prio); +gboolean pid_is_sleeping (guint pid); +gboolean send_signal_to_pid (guint pid, gint signal); +gboolean set_priority_to_pid (guint pid, gint priority); /** * GObject class used to update the graphical widgets. @@ -61,7 +84,7 @@ gboolean get_task_list (GArray *task_list); typedef struct _XtmTaskManager XtmTaskManager; GType xtm_task_manager_get_type (void); -XtmTaskManager * xtm_task_manager_new (); +XtmTaskManager * xtm_task_manager_new (GtkTreeModel *model); const gchar * xtm_task_manager_get_username (XtmTaskManager *manager); const gchar * xtm_task_manager_get_hostname (XtmTaskManager *manager); void xtm_task_manager_get_system_info (XtmTaskManager *manager, guint *num_processes, gfloat *cpu, gfloat *memory, gfloat *swap);