1 /*
2  * Copyright (c) 2010 Mike Massonnet, <mmassonnet@xfce.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at
7  * your option) any later version.
8  */
9 
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13 
14 #include <gtk/gtk.h>
15 #include <cairo.h>
16 
17 #include "task-manager.h"
18 #include "process-monitor.h"
19 
20 
21 
22 enum
23 {
24 	PROP_STEP_SIZE = 1,
25 	PROP_TYPE,
26 };
27 typedef struct _XtmProcessMonitorClass XtmProcessMonitorClass;
28 struct _XtmProcessMonitorClass
29 {
30 	GtkDrawingAreaClass	parent_class;
31 };
32 struct _XtmProcessMonitor
33 {
34 	GtkDrawingArea		parent;
35 	/*<private>*/
36 	gfloat			step_size;
37 	gint			type;
38 	GArray *		history;
39 };
40 G_DEFINE_TYPE (XtmProcessMonitor, xtm_process_monitor, GTK_TYPE_DRAWING_AREA)
41 
42 static void	xtm_process_monitor_get_property	(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
43 static void	xtm_process_monitor_set_property	(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
44 static gboolean	xtm_process_monitor_draw		(GtkWidget *widget, cairo_t *cr);
45 static void	xtm_process_monitor_paint			(XtmProcessMonitor *monitor, cairo_t *cr);
46 
47 
48 
49 static void
xtm_process_monitor_class_init(XtmProcessMonitorClass * klass)50 xtm_process_monitor_class_init (XtmProcessMonitorClass *klass)
51 {
52 	GObjectClass *class = G_OBJECT_CLASS (klass);
53 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
54 	xtm_process_monitor_parent_class = g_type_class_peek_parent (klass);
55 	class->get_property = xtm_process_monitor_get_property;
56 	class->set_property = xtm_process_monitor_set_property;
57 	widget_class->draw = xtm_process_monitor_draw;
58 
59 	g_object_class_install_property (class, PROP_STEP_SIZE,
60 		g_param_spec_float ("step-size", "StepSize", "Step size", 0.1f, G_MAXFLOAT, 1, G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
61 	g_object_class_install_property (class, PROP_TYPE,
62 		g_param_spec_int ("type", "Type", "Type of graph to render", 0, G_MAXINT, 0, G_PARAM_READWRITE));
63 }
64 
65 static void
xtm_process_monitor_init(XtmProcessMonitor * monitor)66 xtm_process_monitor_init (XtmProcessMonitor *monitor)
67 {
68 	monitor->history = g_array_new (FALSE, TRUE, sizeof (gfloat));
69 }
70 
71 static void
xtm_process_monitor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)72 xtm_process_monitor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
73 {
74 	XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (object);
75 	switch (property_id)
76 	{
77 		case PROP_STEP_SIZE:
78 		g_value_set_float (value, monitor->step_size);
79 		break;
80 
81 		case PROP_TYPE:
82 		g_value_set_int (value, monitor->type);
83 		break;
84 
85 		default:
86 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
87 		break;
88 	}
89 }
90 
91 static void
xtm_process_monitor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)92 xtm_process_monitor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
93 {
94 	XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (object);
95 	switch (property_id)
96 	{
97 		case PROP_STEP_SIZE:
98 		monitor->step_size = g_value_get_float (value);
99 		break;
100 
101 		case PROP_TYPE:
102 		monitor->type = g_value_get_int (value);
103 		break;
104 
105 		default:
106 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
107 		break;
108 	}
109 }
110 
111 static gboolean
xtm_process_monitor_draw(GtkWidget * widget,cairo_t * cr)112 xtm_process_monitor_draw (GtkWidget *widget, cairo_t *cr)
113 {
114 	XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (widget);
115 	guint minimum_history_length;
116 
117 	minimum_history_length = (guint)(gtk_widget_get_allocated_width(widget) / monitor->step_size);
118 	if (monitor->history->len < minimum_history_length)
119 		g_array_set_size (monitor->history, minimum_history_length + 1);
120 
121 	xtm_process_monitor_paint (monitor, cr);
122 	return FALSE;
123 }
124 
125 static cairo_surface_t *
xtm_process_monitor_graph_surface_create(XtmProcessMonitor * monitor,gint width,gint height)126 xtm_process_monitor_graph_surface_create (XtmProcessMonitor *monitor, gint width, gint height)
127 {
128 	cairo_t *cr;
129 	cairo_surface_t *graph_surface;
130 	gfloat *peak;
131 	gdouble step_size;
132 	gint i;
133 
134 	if (monitor->history->len <= 1)
135 	{
136 		g_warning ("Cannot paint graph with n_peak <= 1");
137 		return NULL;
138 	}
139 	step_size = (gdouble)monitor->step_size;
140 
141 	graph_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
142 	cr = cairo_create (graph_surface);
143 
144 	/* Draw area below the line, distinguish between CPU (0) and Mem (1) color-wise */
145 	if (monitor->type == 0)
146 		cairo_set_source_rgba (cr, 1.0, 0.43, 0.0, 0.3);
147 	else
148 		cairo_set_source_rgba (cr, 0.67, 0.09, 0.32, 0.3);
149 	cairo_set_line_width (cr, 0.0);
150 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
151 
152 	cairo_move_to (cr, width, height);
153 	cairo_translate (cr, step_size, 0);
154 	for (i = 0; (step_size * (i - 1)) <= width; i++)
155 	{
156 		peak = &g_array_index (monitor->history, gfloat, i);
157 		cairo_translate (cr, -step_size, 0);
158 		cairo_line_to (cr, width, (1.0 - ((gdouble)(*peak))) * height);
159 	}
160 	cairo_line_to (cr, width, height);
161 	cairo_fill (cr);
162 
163 	/* Draw line */
164 	cairo_translate (cr, step_size * i, 0);
165 
166 	if (monitor->type == 0)
167 		cairo_set_source_rgba (cr, 1.0, 0.43, 0.0, 1.0);
168 	else
169 		cairo_set_source_rgba (cr, 0.67, 0.09, 0.32, 1.0);
170 	cairo_set_line_width (cr, 0.85);
171 	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
172 	cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
173 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
174 	cairo_move_to (cr, width, height);
175 	for (i = 0; (step_size * (i - 1)) <= width; i++)
176 	{
177 		peak = &g_array_index (monitor->history, gfloat, i);
178 		cairo_translate (cr, -step_size, 0);
179 		cairo_line_to (cr, width, (1.0 - ((gdouble)(*peak))) * height);
180 	}
181 	cairo_stroke (cr);
182 
183 	cairo_destroy (cr);
184 
185 	return graph_surface;
186 }
187 
188 static void
xtm_process_monitor_paint(XtmProcessMonitor * monitor,cairo_t * cr)189 xtm_process_monitor_paint (XtmProcessMonitor *monitor, cairo_t *cr)
190 {
191 	cairo_surface_t *graph_surface;
192 	gint width, height;
193 	static const double dashed[] = {1.5};
194 	gint i;
195 	width = gtk_widget_get_allocated_width(GTK_WIDGET(monitor));
196 	height = gtk_widget_get_allocated_height(GTK_WIDGET(monitor));
197 
198 	/* Don't draw anything if the graph is too small */
199 	if (height < 3)
200 		return;
201 
202 	/* Paint the graph's background box */
203 	cairo_rectangle (cr, 0.0, 0.0, width, height);
204 	cairo_set_source_rgb (cr, 0.96, 0.96, 0.96);
205 	cairo_fill_preserve (cr);
206 	cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
207 	cairo_set_line_width (cr, 0.75);
208 	cairo_stroke (cr);
209 
210 	/* Paint dashed lines at 25%, 50% and 75% */
211 	cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.3);
212 	cairo_set_line_width (cr, 1.0);
213 	cairo_set_dash(cr, dashed, 1.0, 0);
214 	for (i = 25; i <= 75; i += 25)
215 	{
216 		cairo_move_to (cr, 1.5, i * height / 100 + 0.5);
217 		cairo_line_to (cr, width - 0.5, i * height / 100 + 0.5);
218 		cairo_stroke (cr);
219 	}
220 
221 	/* Paint the graph on a slightly smaller surface not to overlap with the background box */
222 	graph_surface = xtm_process_monitor_graph_surface_create (monitor, width - 1, height - 1);
223 	if (graph_surface != NULL)
224 	{
225 		cairo_set_source_surface (cr, graph_surface, 0.0, 0.0);
226 		cairo_paint (cr);
227 		cairo_surface_destroy (graph_surface);
228 	}
229 }
230 
231 GtkWidget *
xtm_process_monitor_new(void)232 xtm_process_monitor_new (void)
233 {
234 	return g_object_new (XTM_TYPE_PROCESS_MONITOR, NULL);
235 }
236 
237 void
xtm_process_monitor_add_peak(XtmProcessMonitor * monitor,gfloat peak)238 xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak)
239 {
240 	g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
241 	g_return_if_fail (peak >= 0.0f && peak <= 1.0f);
242 
243 	g_array_prepend_val (monitor->history, peak);
244 	if (monitor->history->len > 1)
245 		g_array_remove_index (monitor->history, monitor->history->len - 1);
246 
247 	if (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET(monitor))))
248 		gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET(monitor)), NULL, FALSE);
249 }
250 
251 void
xtm_process_monitor_set_step_size(XtmProcessMonitor * monitor,gfloat step_size)252 xtm_process_monitor_set_step_size (XtmProcessMonitor *monitor, gfloat step_size)
253 {
254 	g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
255 	g_object_set (monitor, "step_size", step_size, NULL);
256 	if (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET(monitor))))
257 		gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET(monitor)), NULL, FALSE);
258 }
259 
260 void
xtm_process_monitor_set_type(XtmProcessMonitor * monitor,gint type)261 xtm_process_monitor_set_type (XtmProcessMonitor *monitor, gint type)
262 {
263 	g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
264 	g_object_set (monitor, "type", type, NULL);
265 }
266 
267 void
xtm_process_monitor_clear(XtmProcessMonitor * monitor)268 xtm_process_monitor_clear (XtmProcessMonitor *monitor)
269 {
270 	g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
271 	g_array_set_size (monitor->history, 0);
272 	if (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET(monitor))))
273 		gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET(monitor)), NULL, FALSE);
274 }
275