diff --git a/configure.ac.in b/configure.ac.in index 666d6bc..0f1e0cf 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -64,6 +64,7 @@ dnl *********************************** dnl *** Check for required packages *** dnl *********************************** XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.12.0]) +XDT_CHECK_PACKAGE([CAIRO], [cairo], [1.5.0]) dnl ****************************************** dnl *** Check for optional package libwnck *** diff --git a/src/Makefile.am b/src/Makefile.am index 9fc671a..b9fdf3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,11 +10,13 @@ bin_PROGRAMS = \ xfce4_taskmanager_CFLAGS = \ $(GTK_CFLAGS) \ + $(CAIRO_CFLAGS) \ $(WNCK_CFLAGS) \ $(NULL) xfce4_taskmanager_LDADD = \ $(GTK_LIBS) \ + $(CAIRO_LIBS) \ $(WNCK_LIBS) \ $(NULL) @@ -22,6 +24,7 @@ xfce4_taskmanager_SOURCES = \ main.c \ process-window_ui.h \ process-window.c process-window.h \ + process-monitor.c process-monitor.h \ process-tree-view.c process-tree-view.h \ process-statusbar.c process-statusbar.h \ task-manager.c task-manager.h \ diff --git a/src/process-monitor.c b/src/process-monitor.c new file mode 100644 index 0000000..16d145c --- /dev/null +++ b/src/process-monitor.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2010 Mike Massonnet, + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "process-monitor.h" + + + +enum +{ + PROP_STEP_SIZE = 1, + PROP_COLOR_RED, + PROP_COLOR_GREEN, + PROP_COLOR_BLUE, +}; +typedef struct _XtmProcessMonitorClass XtmProcessMonitorClass; +struct _XtmProcessMonitorClass +{ + GtkDrawingAreaClass parent_class; +}; +struct _XtmProcessMonitor +{ + GtkDrawingArea parent; + /**/ + gfloat step_size; + GArray * history; + gfloat color_red; + gfloat color_green; + gfloat color_blue; +}; +G_DEFINE_TYPE (XtmProcessMonitor, xtm_process_monitor, GTK_TYPE_DRAWING_AREA) + +static void xtm_process_monitor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); +static void xtm_process_monitor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); +static gboolean xtm_process_monitor_expose (GtkWidget *widget, GdkEventExpose *event); +static void xtm_process_monitor_paint (XtmProcessMonitor *monitor); + + + +static void +xtm_process_monitor_class_init (XtmProcessMonitorClass *klass) +{ + GObjectClass *class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + xtm_process_monitor_parent_class = g_type_class_peek_parent (klass); + class->get_property = xtm_process_monitor_get_property; + class->set_property = xtm_process_monitor_set_property; + widget_class->expose_event = xtm_process_monitor_expose; + g_object_class_install_property (class, PROP_STEP_SIZE, + g_param_spec_float ("step-size", "StepSize", "Step size", 0.1, G_MAXFLOAT, 1, G_PARAM_CONSTRUCT|G_PARAM_READWRITE)); + g_object_class_install_property (class, PROP_COLOR_RED, + g_param_spec_float ("color-red", "ColorRed", "Color red", 0, 1, 0, G_PARAM_READWRITE)); + g_object_class_install_property (class, PROP_COLOR_GREEN, + g_param_spec_float ("color-green", "ColorGreen", "Color green", 0, 1, 0, G_PARAM_READWRITE)); + g_object_class_install_property (class, PROP_COLOR_BLUE, + g_param_spec_float ("color-blue", "ColorBlue", "Color blue", 0, 1, 0, G_PARAM_READWRITE)); +} + +static void +init_source_color (GtkWidget *widget, GtkStyle *prev_style, gpointer user_data) +{ + GdkColor *color = &widget->style->base[GTK_STATE_SELECTED]; + XTM_PROCESS_MONITOR (widget)->color_red = color->red / 65535.0; + XTM_PROCESS_MONITOR (widget)->color_green = color->green / 65535.0; + XTM_PROCESS_MONITOR (widget)->color_blue = color->blue / 65535.0; +} + +static void +xtm_process_monitor_init (XtmProcessMonitor *monitor) +{ + monitor->history = g_array_new (FALSE, TRUE, sizeof (gfloat)); + g_signal_connect (monitor, "style-set", G_CALLBACK (init_source_color), NULL); +} + +static void +xtm_process_monitor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (object); + switch (property_id) + { + case PROP_STEP_SIZE: + g_value_set_float (value, monitor->step_size); + break; + + case PROP_COLOR_RED: + g_value_set_float (value, monitor->color_red); + break; + + case PROP_COLOR_GREEN: + g_value_set_float (value, monitor->color_green); + break; + + case PROP_COLOR_BLUE: + g_value_set_float (value, monitor->color_blue); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +xtm_process_monitor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (object); + switch (property_id) + { + case PROP_STEP_SIZE: + monitor->step_size = g_value_get_float (value); + break; + + case PROP_COLOR_RED: + monitor->color_red = g_value_get_float (value); + break; + + case PROP_COLOR_GREEN: + monitor->color_green = g_value_get_float (value); + break; + + case PROP_COLOR_BLUE: + monitor->color_blue = g_value_get_float (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +xtm_process_monitor_expose (GtkWidget *widget, GdkEventExpose *event) +{ + XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (widget); + guint minimum_history_length; + + minimum_history_length = widget->allocation.width / monitor->step_size; + if (monitor->history->len < minimum_history_length) + g_array_set_size (monitor->history, minimum_history_length); + + xtm_process_monitor_paint (monitor); + return FALSE; +} + +static cairo_surface_t * +xtm_process_monitor_graph_surface_create (XtmProcessMonitor *monitor) +{ + cairo_t *cr; + cairo_surface_t *graph_surface; + cairo_pattern_t *linpat; + gfloat *peak; + gfloat step_size; + gint width, height; + gint i; + + if (monitor->history->len <= 1) + { + g_warning ("Cannot paint graph with n_peak <= 1"); + return NULL; + } + + width = GTK_WIDGET (monitor)->allocation.width; + height = GTK_WIDGET (monitor)->allocation.height; + step_size = monitor->step_size; + + graph_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create (graph_surface); + + /* Draw area */ + linpat = cairo_pattern_create_linear (0, 0, 0, height); + cairo_pattern_add_color_stop_rgba (linpat, 0.4, monitor->color_red, monitor->color_green, monitor->color_blue, 0.3); + cairo_pattern_add_color_stop_rgba (linpat, 0.9, monitor->color_red, monitor->color_green, monitor->color_blue, 0.5); + cairo_pattern_add_color_stop_rgba (linpat, 1, monitor->color_red, monitor->color_green, monitor->color_blue, 0.6); + cairo_set_source (cr, linpat); + cairo_set_line_width (cr, 0.0); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT); + + cairo_move_to (cr, width, height); + cairo_translate (cr, step_size, 0); + for (i = 0; step_size * i < width; i++) + { + peak = &g_array_index (monitor->history, gfloat, i); + cairo_translate (cr, -step_size, 0); + cairo_line_to (cr, width, (1.0 - *peak) * height); + } + cairo_line_to (cr, width, height); + cairo_fill (cr); + + cairo_pattern_destroy (linpat); + + /* Draw line */ + cairo_translate (cr, step_size * i, 0); + + cairo_set_source_rgb (cr, monitor->color_red, monitor->color_green, monitor->color_blue); + cairo_set_line_width (cr, 0.85); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT); + cairo_move_to (cr, width, height); + for (i = 0; step_size * i < width; i++) + { + peak = &g_array_index (monitor->history, gfloat, i); + cairo_translate (cr, -step_size, 0); + cairo_line_to (cr, width, (1.0 - *peak) * height); + } + cairo_stroke (cr); + + cairo_destroy (cr); + + return graph_surface; +} + +static void +xtm_process_monitor_paint (XtmProcessMonitor *monitor) +{ + cairo_t *cr; + cairo_surface_t *graph_surface; + gint width, height; + gint i; + + cr = gdk_cairo_create (GTK_WIDGET (monitor)->window); + width = GTK_WIDGET (monitor)->allocation.width; + height = GTK_WIDGET (monitor)->allocation.height; + + /* Paint a box */ + gtk_paint_box (GTK_WIDGET (monitor)->style, GTK_WIDGET (monitor)->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, + NULL, GTK_WIDGET (monitor), "trough", 0, 0, width, height); + + /* Paint the graph */ + graph_surface = xtm_process_monitor_graph_surface_create (monitor); + if (graph_surface != NULL) + { + cairo_set_source_surface (cr, graph_surface, 0.0, 0.0); + cairo_paint (cr); + cairo_surface_destroy (graph_surface); + } + + /* Trace some marks */ + cairo_set_line_width (cr, 0.75); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT); + for (i = 10; i < 100; i += 10) + { + cairo_move_to (cr, 0.0, i * height / 100); + cairo_line_to (cr, width, i * height / 100); + } + cairo_stroke (cr); + + gdk_cairo_set_source_color (cr, >K_WIDGET (monitor)->style->fg[GTK_STATE_NORMAL]); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (cr, 1.0); + for (i = 25; i <= 75; i += 25) + { + cairo_move_to (cr, 0.0, i * height / 100); + cairo_line_to (cr, 4.0, i * height / 100); + cairo_move_to (cr, width, i * height / 100); + cairo_line_to (cr, width - 4.0, i * height / 100); + } + cairo_stroke (cr); + + /* Repaint a shadow on top of everything to clear corners */ + gtk_paint_shadow (GTK_WIDGET (monitor)->style, GTK_WIDGET (monitor)->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, + NULL, GTK_WIDGET (monitor), "trough", 0, 0, width, height); + + cairo_destroy (cr); +} + +GtkWidget * +xtm_process_monitor_new (void) +{ + return g_object_new (XTM_TYPE_PROCESS_MONITOR, NULL); +} + +void +xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak) +{ + g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor)); + g_return_if_fail (peak >= 0.0 && peak <= 1.0); + + g_array_prepend_val (monitor->history, peak); + if (monitor->history->len > 1) + g_array_remove_index (monitor->history, monitor->history->len - 1); + + if (GDK_IS_WINDOW (GTK_WIDGET (monitor)->window)) + gdk_window_invalidate_rect (GTK_WIDGET (monitor)->window, NULL, FALSE); +} + +void +xtm_process_monitor_set_step_size (XtmProcessMonitor *monitor, gfloat step_size) +{ + g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor)); + g_object_set (monitor, "step_size", step_size, NULL); + if (GDK_IS_WINDOW (GTK_WIDGET (monitor)->window)) + gdk_window_invalidate_rect (GTK_WIDGET (monitor)->window, NULL, FALSE); +} + +void +xtm_process_monitor_clear (XtmProcessMonitor *monitor) +{ + g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor)); + g_array_set_size (monitor->history, 0); + if (GDK_IS_WINDOW (GTK_WIDGET (monitor)->window)) + gdk_window_invalidate_rect (GTK_WIDGET (monitor)->window, NULL, FALSE); +} + +void +xtm_process_monitor_set_source_color (XtmProcessMonitor *monitor, gdouble red, gdouble green, gdouble blue) +{ + g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor)); + g_signal_handlers_disconnect_by_func (GTK_WIDGET (monitor), init_source_color, NULL); + g_object_set (monitor, "color-red", red, "color-green", green, "color-blue", blue, NULL); + if (GDK_IS_WINDOW (GTK_WIDGET (monitor)->window)) + gdk_window_invalidate_rect (GTK_WIDGET (monitor)->window, NULL, FALSE); +} + diff --git a/src/process-monitor.h b/src/process-monitor.h new file mode 100644 index 0000000..e4ead80 --- /dev/null +++ b/src/process-monitor.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 Mike Massonnet, + * + * 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. + */ + +#ifndef PROCESS_MONITOR_H +#define PROCESS_MONITOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#define XTM_TYPE_PROCESS_MONITOR (xtm_process_monitor_get_type ()) +#define XTM_PROCESS_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XTM_TYPE_PROCESS_MONITOR, XtmProcessMonitor)) +#define XTM_PROCESS_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XTM_TYPE_PROCESS_MONITOR, XtmProcessMonitorClass)) +#define XTM_IS_PROCESS_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XTM_TYPE_PROCESS_MONITOR)) +#define XTM_IS_PROCESS_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XTM_TYPE_PROCESS_MONITOR)) +#define XTM_PROCESS_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XTM_TYPE_PROCESS_MONITOR, XtmProcessMonitorClass)) + +typedef struct _XtmProcessMonitor XtmProcessMonitor; + +GType xtm_process_monitor_get_type (void); +GtkWidget * xtm_process_monitor_new (void); +void xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak); +void xtm_process_monitor_set_step_size (XtmProcessMonitor *monitor, gfloat step_size); +void xtm_process_monitor_clear (XtmProcessMonitor *monitor); +void xtm_process_monitor_set_source_color (XtmProcessMonitor *monitor, gdouble red, gdouble green, gdouble blue); + +#endif /* !PROCESS_MONITOR_H */ diff --git a/src/process-window.c b/src/process-window.c index 9cf5d09..0ffdbab 100644 --- a/src/process-window.c +++ b/src/process-window.c @@ -22,6 +22,7 @@ #include "settings.h" #include "process-window.h" #include "process-window_ui.h" +#include "process-monitor.h" #include "process-tree-view.h" #include "process-statusbar.h" @@ -44,7 +45,7 @@ struct _XtmProcessWindowPriv GtkBuilder * builder; GtkWidget * window; GtkWidget * cpu_monitor; - GtkWidget * memory_monitor; + GtkWidget * mem_monitor; GtkWidget * treeview; GtkWidget * statusbar; XtmSettings * settings; @@ -58,6 +59,7 @@ static void xtm_process_window_hide (GtkWidget *widget); static void emit_destroy_signal (XtmProcessWindow *window); static gboolean emit_delete_event_signal (XtmProcessWindow *window, GdkEvent *event); +static void monitor_update_step_size (XtmProcessWindow *window); static void show_menu_execute_task (XtmProcessWindow *window); static void show_menu_preferences (XtmProcessWindow *window); static void show_about_dialog (XtmProcessWindow *window); @@ -99,8 +101,26 @@ xtm_process_window_init (XtmProcessWindow *window) g_signal_connect_swapped (window->priv->window, "destroy", G_CALLBACK (emit_destroy_signal), window); g_signal_connect_swapped (window->priv->window, "delete-event", G_CALLBACK (emit_delete_event_signal), window); - window->priv->cpu_monitor = GTK_WIDGET (gtk_builder_get_object (window->priv->builder, "cpu-monitor")); - window->priv->memory_monitor = GTK_WIDGET (gtk_builder_get_object (window->priv->builder, "mem-monitor")); + { + GtkWidget *toolitem; + guint refresh_rate; + + g_object_get (window->priv->settings, "refresh-rate", &refresh_rate, NULL); + + toolitem = GTK_WIDGET (gtk_builder_get_object (window->priv->builder, "cpu-toolitem")); + window->priv->cpu_monitor = xtm_process_monitor_new (); + xtm_process_monitor_set_step_size (XTM_PROCESS_MONITOR (window->priv->cpu_monitor), refresh_rate / 1000.0); + gtk_widget_show (window->priv->cpu_monitor); + gtk_container_add (GTK_CONTAINER (toolitem), window->priv->cpu_monitor); + + toolitem = GTK_WIDGET (gtk_builder_get_object (window->priv->builder, "mem-toolitem")); + window->priv->mem_monitor = xtm_process_monitor_new (); + xtm_process_monitor_set_step_size (XTM_PROCESS_MONITOR (window->priv->mem_monitor), refresh_rate / 1000.0); + gtk_widget_show (window->priv->mem_monitor); + gtk_container_add (GTK_CONTAINER (toolitem), window->priv->mem_monitor); + + g_signal_connect_swapped (window->priv->settings, "notify::refresh-rate", G_CALLBACK (monitor_update_step_size), window); + } if (geteuid () == 0) { @@ -181,6 +201,15 @@ emit_delete_event_signal (XtmProcessWindow *window, GdkEvent *event) return ret; } +static void +monitor_update_step_size (XtmProcessWindow *window) +{ + guint refresh_rate; + g_object_get (window->priv->settings, "refresh-rate", &refresh_rate, NULL); + g_object_set (window->priv->cpu_monitor, "step-size", refresh_rate / 1000.0, NULL); + g_object_set (window->priv->mem_monitor, "step-size", refresh_rate / 1000.0, NULL); +} + static void execute_command (const gchar *command) { @@ -465,12 +494,20 @@ xtm_process_window_get_model (XtmProcessWindow *window) void xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gfloat swap) { + gchar text[100]; + g_return_if_fail (XTM_IS_PROCESS_WINDOW (window)); g_return_if_fail (GTK_IS_STATUSBAR (window->priv->statusbar)); + g_object_set (window->priv->statusbar, "num-processes", num_processes, "cpu", cpu, "memory", memory, "swap", swap, NULL); - // TODO update cpu/memory monitors - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->memory_monitor), memory / 100.0); - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->cpu_monitor), cpu / 100.0); + + xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->priv->cpu_monitor), cpu / 100.0); + g_snprintf (text, 100, _("CPU: %.0f%%"), cpu); + gtk_widget_set_tooltip_text (window->priv->cpu_monitor, text); + + xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->priv->mem_monitor), memory / 100.0); + g_snprintf (text, 100, _("Memory: %.0f%%"), memory); + gtk_widget_set_tooltip_text (window->priv->mem_monitor, text); } void diff --git a/src/process-window.ui b/src/process-window.ui index 90e857e..39f80e8 100644 --- a/src/process-window.ui +++ b/src/process-window.ui @@ -46,13 +46,7 @@ True 2 - - 60 - True - True - CPU - start - + @@ -65,13 +59,7 @@ True 2 - - 60 - True - True - Memory - start - +