Fix compilation warnings in plugin "cpu".
[lxde/lxpanel.git] / src / plugins / cpu / cpu.c
CommitLineData
9dd114c4 1/**
a52c2257
HJYP
2 * CPU usage plugin to lxpanel
3 *
89b621ca 4 * Copyright (c) 2008-2014 LxDE Developers, see the file AUTHORS for details.
a52c2257
HJYP
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
4542c20d 20 *
a52c2257
HJYP
21 */
22/*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
23
a52c2257
HJYP
24#include <string.h>
25#include <sys/time.h>
26#include <time.h>
27#include <sys/sysinfo.h>
28#include <stdlib.h>
e7cb732b 29#include <glib/gi18n.h>
a52c2257 30
89b621ca 31#include <lxpanel/plugin.h>
a52c2257 32
4a1b2fde 33#define BORDER_SIZE 2
89b621ca 34#define PANEL_HEIGHT_DEFAULT 26 /* from panel defaults */
4a1b2fde 35
89b621ca 36/* #include "../../dbg.h" */
9dd114c4 37
201ffd7e 38typedef unsigned long long CPUTick; /* Value from /proc/stat */
9dd114c4 39typedef float CPUSample; /* Saved CPU utilization value as 0.0..1.0 */
a52c2257
HJYP
40
41struct cpu_stat {
9dd114c4 42 CPUTick u, n, s, i; /* User, nice, system, idle */
a52c2257
HJYP
43};
44
9dd114c4 45/* Private context for CPU plugin. */
a52c2257 46typedef struct {
9dd114c4 47 GdkColor foreground_color; /* Foreground color for drawing area */
48 GtkWidget * da; /* Drawing area */
f176735a 49 cairo_surface_t * pixmap; /* Pixmap to be drawn on drawing area */
9dd114c4 50
2918994e 51 guint timer; /* Timer for periodic update */
9dd114c4 52 CPUSample * stats_cpu; /* Ring buffer of CPU utilization values */
53 unsigned int ring_cursor; /* Cursor for ring buffer */
a7ff7071
AG
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 */
9dd114c4 56 struct cpu_stat previous_cpu_stat; /* Previous value of cpu_stat */
57} CPUPlugin;
58
59static void redraw_pixmap(CPUPlugin * c);
60static gboolean cpu_update(CPUPlugin * c);
61static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c);
62static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c);
89b621ca
AG
63
64static void cpu_destructor(gpointer user_data);
9dd114c4 65
66/* Redraw after timer callback or resize. */
67static void redraw_pixmap(CPUPlugin * c)
a52c2257 68{
f176735a
RM
69 cairo_t * cr = cairo_create(c->pixmap);
70 cairo_set_line_width (cr, 1.0);
9dd114c4 71 /* Erase pixmap. */
f176735a
RM
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);
9dd114c4 75
76 /* Recompute pixmap. */
a52c2257 77 unsigned int i;
9dd114c4 78 unsigned int drawing_cursor = c->ring_cursor;
f176735a 79 gdk_cairo_set_source_color(cr, &c->foreground_color);
9dd114c4 80 for (i = 0; i < c->pixmap_width; i++)
176fb687 81 {
9dd114c4 82 /* Draw one bar of the CPU usage graph. */
83 if (c->stats_cpu[drawing_cursor] != 0.0)
f176735a 84 {
61a23b4e
HG
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);
f176735a
RM
87 cairo_stroke(cr);
88 }
9dd114c4 89
90 /* Increment and wrap drawing cursor. */
91 drawing_cursor += 1;
89b621ca 92 if (drawing_cursor >= c->pixmap_width)
9dd114c4 93 drawing_cursor = 0;
a52c2257 94 }
e4f8b15f 95
89b621ca 96 /* check_cairo_status(cr); */
f176735a 97 cairo_destroy(cr);
9dd114c4 98
99 /* Redraw pixmap. */
a52c2257 100 gtk_widget_queue_draw(c->da);
a52c2257
HJYP
101}
102
9dd114c4 103/* Periodic timer callback. */
104static gboolean cpu_update(CPUPlugin * c)
a52c2257 105{
9dd114c4 106 if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
c150eb9b 107 {
9dd114c4 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;
201ffd7e 113 int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
9dd114c4 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 }
c150eb9b 140 }
9dd114c4 141 return TRUE;
a52c2257
HJYP
142}
143
9dd114c4 144/* Handler for configure_event on drawing area. */
145static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
a52c2257 146{
9dd114c4 147 /* Allocate pixmap and statistics buffer without border pixels. */
a7ff7071
AG
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);
9dd114c4 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)
f176735a
RM
193 cairo_surface_destroy(c->pixmap);
194 c->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, c->pixmap_width, c->pixmap_height);
89b621ca 195 /* check_cairo_surface_status(&c->pixmap); */
9dd114c4 196
197 /* Redraw pixmap at the new size. */
198 redraw_pixmap(c);
199 }
200 return TRUE;
a52c2257
HJYP
201}
202
9dd114c4 203/* Handler for expose_event on drawing area. */
204static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
7414a73f 205{
9dd114c4 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)
7414a73f 209 {
f176735a
RM
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);
89b621ca 217 /* check_cairo_status(cr); */
f176735a 218 cairo_destroy(cr);
7414a73f
HJYP
219 }
220 return FALSE;
221}
222
9dd114c4 223/* Plugin constructor. */
89b621ca 224static GtkWidget *cpu_constructor(Panel *panel, config_setting_t *settings)
a52c2257 225{
9dd114c4 226 /* Allocate plugin context and set into Plugin private data pointer. */
227 CPUPlugin * c = g_new0(CPUPlugin, 1);
89b621ca 228 GtkWidget * p;
a52c2257 229
9dd114c4 230 /* Allocate top level widget and set into Plugin widget pointer. */
89b621ca
AG
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);
4542c20d 235
9dd114c4 236 /* Allocate drawing area as a child of top level widget. Enable button press events. */
a52c2257 237 c->da = gtk_drawing_area_new();
9dd114c4 238 gtk_widget_set_size_request(c->da, 40, PANEL_HEIGHT_DEFAULT);
239 gtk_widget_add_events(c->da, GDK_BUTTON_PRESS_MASK);
89b621ca 240 gtk_container_add(GTK_CONTAINER(p), c->da);
4542c20d 241
9dd114c4 242 /* Clone a graphics context and set "green" as its foreground color.
243 * We will use this to draw the graph. */
9dd114c4 244 gdk_color_parse("green", &c->foreground_color);
9dd114c4 245
246 /* Connect signals. */
93a217b3
AG
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);
4542c20d 249
9dd114c4 250 /* Show the widget. Connect a timer to refresh the statistics. */
251 gtk_widget_show(c->da);
4a1b2fde 252 c->timer = g_timeout_add(1500, (GSourceFunc) cpu_update, (gpointer) c);
89b621ca 253 return p;
a52c2257
HJYP
254}
255
9dd114c4 256/* Plugin destructor. */
89b621ca 257static void cpu_destructor(gpointer user_data)
a52c2257 258{
89b621ca 259 CPUPlugin * c = (CPUPlugin *)user_data;
9dd114c4 260
261 /* Disconnect the timer. */
262 g_source_remove(c->timer);
a52c2257 263
9dd114c4 264 /* Deallocate memory. */
f176735a 265 cairo_surface_destroy(c->pixmap);
a52c2257 266 g_free(c->stats_cpu);
9dd114c4 267 g_free(c);
a52c2257
HJYP
268}
269
89b621ca 270FM_DEFINE_MODULE(lxpanel_gtk, cpu)
2918994e 271
89b621ca
AG
272/* Plugin descriptor. */
273LXPanelPluginInit fm_module_init_lxpanel_gtk = {
3c3e9c9e 274 .name = N_("CPU Usage Monitor"),
3c3e9c9e 275 .description = N_("Display CPU usage"),
89b621ca 276 .new_instance = cpu_constructor,
a52c2257 277};