Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / cpu / cpu.c
1 /**
2 * CPU usage plugin to lxpanel
3 *
4 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
5 * Copyright (C) 2004 by Alexandre Pereira da Silva <alexandre.pereira@poli.usp.br>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22 /*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
23
24 #include <string.h>
25 #include <sys/time.h>
26 #include <time.h>
27 #include <sys/sysinfo.h>
28 #include <stdlib.h>
29 #include <glib/gi18n.h>
30
31 #include "plugin.h"
32 #include "panel.h"
33 #include "misc.h"
34
35 #define BORDER_SIZE 2
36
37 #include "dbg.h"
38
39 typedef unsigned long CPUTick; /* Value from /proc/stat */
40 typedef float CPUSample; /* Saved CPU utilization value as 0.0..1.0 */
41
42 struct cpu_stat {
43 CPUTick u, n, s, i; /* User, nice, system, idle */
44 };
45
46 /* Private context for CPU plugin. */
47 typedef struct {
48 GdkColor foreground_color; /* Foreground color for drawing area */
49 GtkWidget * da; /* Drawing area */
50 cairo_surface_t * pixmap; /* Pixmap to be drawn on drawing area */
51
52 guint timer; /* Timer for periodic update */
53 CPUSample * stats_cpu; /* Ring buffer of CPU utilization values */
54 unsigned int ring_cursor; /* Cursor for ring buffer */
55 int pixmap_width; /* Width of drawing area pixmap; also size of ring buffer; does not include border size */
56 int pixmap_height; /* Height of drawing area pixmap; does not include border size */
57 struct cpu_stat previous_cpu_stat; /* Previous value of cpu_stat */
58 } CPUPlugin;
59
60 static void redraw_pixmap(CPUPlugin * c);
61 static gboolean cpu_update(CPUPlugin * c);
62 static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c);
63 static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c);
64 static int cpu_constructor(Plugin * p, char ** fp);
65 static void cpu_destructor(Plugin * p);
66
67 /* Redraw after timer callback or resize. */
68 static void redraw_pixmap(CPUPlugin * c)
69 {
70 cairo_t * cr = cairo_create(c->pixmap);
71 cairo_set_line_width (cr, 1.0);
72 /* Erase pixmap. */
73 cairo_rectangle(cr, 0, 0, c->pixmap_width, c->pixmap_height);
74 gdk_cairo_set_source_color(cr, &c->da->style->black);
75 cairo_fill(cr);
76
77 /* Recompute pixmap. */
78 unsigned int i;
79 unsigned int drawing_cursor = c->ring_cursor;
80 gdk_cairo_set_source_color(cr, &c->foreground_color);
81 for (i = 0; i < c->pixmap_width; i++)
82 {
83 /* Draw one bar of the CPU usage graph. */
84 if (c->stats_cpu[drawing_cursor] != 0.0)
85 {
86 cairo_move_to(cr, i + 0.5, c->pixmap_height);
87 cairo_line_to(cr, i + 0.5, c->pixmap_height - c->stats_cpu[drawing_cursor] * c->pixmap_height);
88 cairo_stroke(cr);
89 }
90
91 /* Increment and wrap drawing cursor. */
92 drawing_cursor += 1;
93 if (drawing_cursor >= c->pixmap_width)
94 drawing_cursor = 0;
95 }
96
97 check_cairo_status(cr);
98 cairo_destroy(cr);
99
100 /* Redraw pixmap. */
101 gtk_widget_queue_draw(c->da);
102 }
103
104 /* Periodic timer callback. */
105 static gboolean cpu_update(CPUPlugin * c)
106 {
107 if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
108 {
109 /* Open statistics file and scan out CPU usage. */
110 struct cpu_stat cpu;
111 FILE * stat = fopen("/proc/stat", "r");
112 if (stat == NULL)
113 return TRUE;
114 int fscanf_result = fscanf(stat, "cpu %lu %lu %lu %lu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
115 fclose(stat);
116
117 /* Ensure that fscanf succeeded. */
118 if (fscanf_result == 4)
119 {
120 /* Compute delta from previous statistics. */
121 struct cpu_stat cpu_delta;
122 cpu_delta.u = cpu.u - c->previous_cpu_stat.u;
123 cpu_delta.n = cpu.n - c->previous_cpu_stat.n;
124 cpu_delta.s = cpu.s - c->previous_cpu_stat.s;
125 cpu_delta.i = cpu.i - c->previous_cpu_stat.i;
126
127 /* Copy current to previous. */
128 memcpy(&c->previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
129
130 /* Compute user+nice+system as a fraction of total.
131 * Introduce this sample to ring buffer, increment and wrap ring buffer cursor. */
132 float cpu_uns = cpu_delta.u + cpu_delta.n + cpu_delta.s;
133 c->stats_cpu[c->ring_cursor] = cpu_uns / (cpu_uns + cpu_delta.i);
134 c->ring_cursor += 1;
135 if (c->ring_cursor >= c->pixmap_width)
136 c->ring_cursor = 0;
137
138 /* Redraw with the new sample. */
139 redraw_pixmap(c);
140 }
141 }
142 return TRUE;
143 }
144
145 /* Handler for configure_event on drawing area. */
146 static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
147 {
148 /* Allocate pixmap and statistics buffer without border pixels. */
149 int new_pixmap_width = widget->allocation.width - BORDER_SIZE * 2;
150 int new_pixmap_height = widget->allocation.height - BORDER_SIZE * 2;
151 if ((new_pixmap_width > 0) && (new_pixmap_height > 0))
152 {
153 /* If statistics buffer does not exist or it changed size, reallocate and preserve existing data. */
154 if ((c->stats_cpu == NULL) || (new_pixmap_width != c->pixmap_width))
155 {
156 CPUSample * new_stats_cpu = g_new0(typeof(*c->stats_cpu), new_pixmap_width);
157 if (c->stats_cpu != NULL)
158 {
159 if (new_pixmap_width > c->pixmap_width)
160 {
161 /* New allocation is larger.
162 * Introduce new "oldest" samples of zero following the cursor. */
163 memcpy(&new_stats_cpu[0],
164 &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
165 memcpy(&new_stats_cpu[new_pixmap_width - c->pixmap_width + c->ring_cursor],
166 &c->stats_cpu[c->ring_cursor], (c->pixmap_width - c->ring_cursor) * sizeof(CPUSample));
167 }
168 else if (c->ring_cursor <= new_pixmap_width)
169 {
170 /* New allocation is smaller, but still larger than the ring buffer cursor.
171 * Discard the oldest samples following the cursor. */
172 memcpy(&new_stats_cpu[0],
173 &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
174 memcpy(&new_stats_cpu[c->ring_cursor],
175 &c->stats_cpu[c->pixmap_width - new_pixmap_width + c->ring_cursor], (new_pixmap_width - c->ring_cursor) * sizeof(CPUSample));
176 }
177 else
178 {
179 /* New allocation is smaller, and also smaller than the ring buffer cursor.
180 * Discard all oldest samples following the ring buffer cursor and additional samples at the beginning of the buffer. */
181 memcpy(&new_stats_cpu[0],
182 &c->stats_cpu[c->ring_cursor - new_pixmap_width], new_pixmap_width * sizeof(CPUSample));
183 c->ring_cursor = 0;
184 }
185 g_free(c->stats_cpu);
186 }
187 c->stats_cpu = new_stats_cpu;
188 }
189
190 /* Allocate or reallocate pixmap. */
191 c->pixmap_width = new_pixmap_width;
192 c->pixmap_height = new_pixmap_height;
193 if (c->pixmap)
194 cairo_surface_destroy(c->pixmap);
195 c->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, c->pixmap_width, c->pixmap_height);
196 check_cairo_surface_status(&c->pixmap);
197
198 /* Redraw pixmap at the new size. */
199 redraw_pixmap(c);
200 }
201 return TRUE;
202 }
203
204 /* Handler for expose_event on drawing area. */
205 static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
206 {
207 /* Draw the requested part of the pixmap onto the drawing area.
208 * Translate it in both x and y by the border size. */
209 if (c->pixmap != NULL)
210 {
211 cairo_t * cr = gdk_cairo_create(widget->window);
212 gdk_cairo_region(cr, event->region);
213 cairo_clip(cr);
214 gdk_cairo_set_source_color(cr, &c->da->style->black);
215 cairo_set_source_surface(cr, c->pixmap,
216 BORDER_SIZE, BORDER_SIZE);
217 cairo_paint(cr);
218 check_cairo_status(cr);
219 cairo_destroy(cr);
220 }
221 return FALSE;
222 }
223
224 /* Plugin constructor. */
225 static int cpu_constructor(Plugin * p, char ** fp)
226 {
227 /* Allocate plugin context and set into Plugin private data pointer. */
228 CPUPlugin * c = g_new0(CPUPlugin, 1);
229 p->priv = c;
230
231 /* Allocate top level widget and set into Plugin widget pointer. */
232 p->pwid = gtk_event_box_new();
233 gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 1);
234 GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
235
236 /* Allocate drawing area as a child of top level widget. Enable button press events. */
237 c->da = gtk_drawing_area_new();
238 gtk_widget_set_size_request(c->da, 40, PANEL_HEIGHT_DEFAULT);
239 gtk_widget_add_events(c->da, GDK_BUTTON_PRESS_MASK);
240 gtk_container_add(GTK_CONTAINER(p->pwid), c->da);
241
242 /* Clone a graphics context and set "green" as its foreground color.
243 * We will use this to draw the graph. */
244 gdk_color_parse("green", &c->foreground_color);
245
246 /* Connect signals. */
247 g_signal_connect(G_OBJECT(c->da), "configure_event", G_CALLBACK(configure_event), (gpointer) c);
248 g_signal_connect(G_OBJECT(c->da), "expose_event", G_CALLBACK(expose_event), (gpointer) c);
249 g_signal_connect(c->da, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
250
251 /* Show the widget. Connect a timer to refresh the statistics. */
252 gtk_widget_show(c->da);
253 c->timer = g_timeout_add(1500, (GSourceFunc) cpu_update, (gpointer) c);
254 return 1;
255 }
256
257 /* Plugin destructor. */
258 static void cpu_destructor(Plugin * p)
259 {
260 CPUPlugin * c = (CPUPlugin *) p->priv;
261
262 /* Disconnect the timer. */
263 g_source_remove(c->timer);
264
265 /* Deallocate memory. */
266 cairo_surface_destroy(c->pixmap);
267 g_free(c->stats_cpu);
268 g_free(c);
269 }
270
271 /* Plugin descriptor. */
272 PluginClass cpu_plugin_class = {
273
274 PLUGINCLASS_VERSIONING,
275
276 type : "cpu",
277 name : N_("CPU Usage Monitor"),
278 version: "1.0",
279 description : N_("Display CPU usage"),
280
281 constructor : cpu_constructor,
282 destructor : cpu_destructor,
283 config : NULL,
284 save : NULL
285 };