Fix compilation warnings in plugin "cpu".
[lxde/lxpanel.git] / src / plugins / cpu / cpu.c
1 /**
2 * CPU usage plugin to lxpanel
3 *
4 * Copyright (c) 2008-2014 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 <lxpanel/plugin.h>
32
33 #define BORDER_SIZE 2
34 #define PANEL_HEIGHT_DEFAULT 26 /* from panel defaults */
35
36 /* #include "../../dbg.h" */
37
38 typedef unsigned long long CPUTick; /* Value from /proc/stat */
39 typedef float CPUSample; /* Saved CPU utilization value as 0.0..1.0 */
40
41 struct cpu_stat {
42 CPUTick u, n, s, i; /* User, nice, system, idle */
43 };
44
45 /* Private context for CPU plugin. */
46 typedef struct {
47 GdkColor foreground_color; /* Foreground color for drawing area */
48 GtkWidget * da; /* Drawing area */
49 cairo_surface_t * pixmap; /* Pixmap to be drawn on drawing area */
50
51 guint timer; /* Timer for periodic update */
52 CPUSample * stats_cpu; /* Ring buffer of CPU utilization values */
53 unsigned int ring_cursor; /* Cursor for ring buffer */
54 guint pixmap_width; /* Width of drawing area pixmap; also size of ring buffer; does not include border size */
55 guint pixmap_height; /* Height of drawing area pixmap; does not include border size */
56 struct cpu_stat previous_cpu_stat; /* Previous value of cpu_stat */
57 } CPUPlugin;
58
59 static void redraw_pixmap(CPUPlugin * c);
60 static gboolean cpu_update(CPUPlugin * c);
61 static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c);
62 static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c);
63
64 static void cpu_destructor(gpointer user_data);
65
66 /* Redraw after timer callback or resize. */
67 static void redraw_pixmap(CPUPlugin * c)
68 {
69 cairo_t * cr = cairo_create(c->pixmap);
70 cairo_set_line_width (cr, 1.0);
71 /* Erase pixmap. */
72 cairo_rectangle(cr, 0, 0, c->pixmap_width, c->pixmap_height);
73 gdk_cairo_set_source_color(cr, &c->da->style->black);
74 cairo_fill(cr);
75
76 /* Recompute pixmap. */
77 unsigned int i;
78 unsigned int drawing_cursor = c->ring_cursor;
79 gdk_cairo_set_source_color(cr, &c->foreground_color);
80 for (i = 0; i < c->pixmap_width; i++)
81 {
82 /* Draw one bar of the CPU usage graph. */
83 if (c->stats_cpu[drawing_cursor] != 0.0)
84 {
85 cairo_move_to(cr, i + 0.5, c->pixmap_height);
86 cairo_line_to(cr, i + 0.5, c->pixmap_height - c->stats_cpu[drawing_cursor] * c->pixmap_height);
87 cairo_stroke(cr);
88 }
89
90 /* Increment and wrap drawing cursor. */
91 drawing_cursor += 1;
92 if (drawing_cursor >= c->pixmap_width)
93 drawing_cursor = 0;
94 }
95
96 /* check_cairo_status(cr); */
97 cairo_destroy(cr);
98
99 /* Redraw pixmap. */
100 gtk_widget_queue_draw(c->da);
101 }
102
103 /* Periodic timer callback. */
104 static gboolean cpu_update(CPUPlugin * c)
105 {
106 if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
107 {
108 /* Open statistics file and scan out CPU usage. */
109 struct cpu_stat cpu;
110 FILE * stat = fopen("/proc/stat", "r");
111 if (stat == NULL)
112 return TRUE;
113 int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
114 fclose(stat);
115
116 /* Ensure that fscanf succeeded. */
117 if (fscanf_result == 4)
118 {
119 /* Compute delta from previous statistics. */
120 struct cpu_stat cpu_delta;
121 cpu_delta.u = cpu.u - c->previous_cpu_stat.u;
122 cpu_delta.n = cpu.n - c->previous_cpu_stat.n;
123 cpu_delta.s = cpu.s - c->previous_cpu_stat.s;
124 cpu_delta.i = cpu.i - c->previous_cpu_stat.i;
125
126 /* Copy current to previous. */
127 memcpy(&c->previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
128
129 /* Compute user+nice+system as a fraction of total.
130 * Introduce this sample to ring buffer, increment and wrap ring buffer cursor. */
131 float cpu_uns = cpu_delta.u + cpu_delta.n + cpu_delta.s;
132 c->stats_cpu[c->ring_cursor] = cpu_uns / (cpu_uns + cpu_delta.i);
133 c->ring_cursor += 1;
134 if (c->ring_cursor >= c->pixmap_width)
135 c->ring_cursor = 0;
136
137 /* Redraw with the new sample. */
138 redraw_pixmap(c);
139 }
140 }
141 return TRUE;
142 }
143
144 /* Handler for configure_event on drawing area. */
145 static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
146 {
147 /* Allocate pixmap and statistics buffer without border pixels. */
148 guint new_pixmap_width = MAX(widget->allocation.width - BORDER_SIZE * 2, 0);
149 guint new_pixmap_height = MAX(widget->allocation.height - BORDER_SIZE * 2, 0);
150 if ((new_pixmap_width > 0) && (new_pixmap_height > 0))
151 {
152 /* If statistics buffer does not exist or it changed size, reallocate and preserve existing data. */
153 if ((c->stats_cpu == NULL) || (new_pixmap_width != c->pixmap_width))
154 {
155 CPUSample * new_stats_cpu = g_new0(typeof(*c->stats_cpu), new_pixmap_width);
156 if (c->stats_cpu != NULL)
157 {
158 if (new_pixmap_width > c->pixmap_width)
159 {
160 /* New allocation is larger.
161 * Introduce new "oldest" samples of zero following the cursor. */
162 memcpy(&new_stats_cpu[0],
163 &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
164 memcpy(&new_stats_cpu[new_pixmap_width - c->pixmap_width + c->ring_cursor],
165 &c->stats_cpu[c->ring_cursor], (c->pixmap_width - c->ring_cursor) * sizeof(CPUSample));
166 }
167 else if (c->ring_cursor <= new_pixmap_width)
168 {
169 /* New allocation is smaller, but still larger than the ring buffer cursor.
170 * Discard the oldest samples following the cursor. */
171 memcpy(&new_stats_cpu[0],
172 &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
173 memcpy(&new_stats_cpu[c->ring_cursor],
174 &c->stats_cpu[c->pixmap_width - new_pixmap_width + c->ring_cursor], (new_pixmap_width - c->ring_cursor) * sizeof(CPUSample));
175 }
176 else
177 {
178 /* New allocation is smaller, and also smaller than the ring buffer cursor.
179 * Discard all oldest samples following the ring buffer cursor and additional samples at the beginning of the buffer. */
180 memcpy(&new_stats_cpu[0],
181 &c->stats_cpu[c->ring_cursor - new_pixmap_width], new_pixmap_width * sizeof(CPUSample));
182 c->ring_cursor = 0;
183 }
184 g_free(c->stats_cpu);
185 }
186 c->stats_cpu = new_stats_cpu;
187 }
188
189 /* Allocate or reallocate pixmap. */
190 c->pixmap_width = new_pixmap_width;
191 c->pixmap_height = new_pixmap_height;
192 if (c->pixmap)
193 cairo_surface_destroy(c->pixmap);
194 c->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, c->pixmap_width, c->pixmap_height);
195 /* check_cairo_surface_status(&c->pixmap); */
196
197 /* Redraw pixmap at the new size. */
198 redraw_pixmap(c);
199 }
200 return TRUE;
201 }
202
203 /* Handler for expose_event on drawing area. */
204 static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
205 {
206 /* Draw the requested part of the pixmap onto the drawing area.
207 * Translate it in both x and y by the border size. */
208 if (c->pixmap != NULL)
209 {
210 cairo_t * cr = gdk_cairo_create(widget->window);
211 gdk_cairo_region(cr, event->region);
212 cairo_clip(cr);
213 gdk_cairo_set_source_color(cr, &c->da->style->black);
214 cairo_set_source_surface(cr, c->pixmap,
215 BORDER_SIZE, BORDER_SIZE);
216 cairo_paint(cr);
217 /* check_cairo_status(cr); */
218 cairo_destroy(cr);
219 }
220 return FALSE;
221 }
222
223 /* Plugin constructor. */
224 static GtkWidget *cpu_constructor(Panel *panel, config_setting_t *settings)
225 {
226 /* Allocate plugin context and set into Plugin private data pointer. */
227 CPUPlugin * c = g_new0(CPUPlugin, 1);
228 GtkWidget * p;
229
230 /* Allocate top level widget and set into Plugin widget pointer. */
231 p = gtk_event_box_new();
232 lxpanel_plugin_set_data(p, c, cpu_destructor);
233 gtk_container_set_border_width(GTK_CONTAINER(p), 1);
234 GTK_WIDGET_SET_FLAGS(p, 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), 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
250 /* Show the widget. Connect a timer to refresh the statistics. */
251 gtk_widget_show(c->da);
252 c->timer = g_timeout_add(1500, (GSourceFunc) cpu_update, (gpointer) c);
253 return p;
254 }
255
256 /* Plugin destructor. */
257 static void cpu_destructor(gpointer user_data)
258 {
259 CPUPlugin * c = (CPUPlugin *)user_data;
260
261 /* Disconnect the timer. */
262 g_source_remove(c->timer);
263
264 /* Deallocate memory. */
265 cairo_surface_destroy(c->pixmap);
266 g_free(c->stats_cpu);
267 g_free(c);
268 }
269
270 FM_DEFINE_MODULE(lxpanel_gtk, cpu)
271
272 /* Plugin descriptor. */
273 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
274 .name = N_("CPU Usage Monitor"),
275 .description = N_("Display CPU usage"),
276 .new_instance = cpu_constructor,
277 };