2
* CPU usage plugin to lxpanel
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>
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.
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.
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
22
/*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
27
#include <sys/sysinfo.h>
29
#include <glib/gi18n.h>
39
typedef unsigned long long CPUTick; /* Value from /proc/stat */
40
typedef float CPUSample; /* Saved CPU utilization value as 0.0..1.0 */
43
CPUTick u, n, s, i; /* User, nice, system, idle */
46
/* Private context for CPU plugin. */
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 */
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 */
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);
67
/* Redraw after timer callback or resize. */
68
static void redraw_pixmap(CPUPlugin * c)
70
cairo_t * cr = cairo_create(c->pixmap);
71
cairo_set_line_width (cr, 1.0);
73
cairo_rectangle(cr, 0, 0, c->pixmap_width, c->pixmap_height);
74
gdk_cairo_set_source_color(cr, &c->da->style->black);
77
/* Recompute pixmap. */
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++)
83
/* Draw one bar of the CPU usage graph. */
84
if (c->stats_cpu[drawing_cursor] != 0.0)
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);
91
/* Increment and wrap drawing cursor. */
93
if (drawing_cursor >= c->pixmap_width)
97
check_cairo_status(cr);
101
gtk_widget_queue_draw(c->da);
104
/* Periodic timer callback. */
105
static gboolean cpu_update(CPUPlugin * c)
107
if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
109
/* Open statistics file and scan out CPU usage. */
111
FILE * stat = fopen("/proc/stat", "r");
114
int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
117
/* Ensure that fscanf succeeded. */
118
if (fscanf_result == 4)
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;
127
/* Copy current to previous. */
128
memcpy(&c->previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
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);
135
if (c->ring_cursor >= c->pixmap_width)
138
/* Redraw with the new sample. */
145
/* Handler for configure_event on drawing area. */
146
static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
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))
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))
156
CPUSample * new_stats_cpu = g_new0(typeof(*c->stats_cpu), new_pixmap_width);
157
if (c->stats_cpu != NULL)
159
if (new_pixmap_width > c->pixmap_width)
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));
168
else if (c->ring_cursor <= new_pixmap_width)
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));
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));
185
g_free(c->stats_cpu);
187
c->stats_cpu = new_stats_cpu;
190
/* Allocate or reallocate pixmap. */
191
c->pixmap_width = new_pixmap_width;
192
c->pixmap_height = new_pixmap_height;
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);
198
/* Redraw pixmap at the new size. */
204
/* Handler for expose_event on drawing area. */
205
static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
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)
211
cairo_t * cr = gdk_cairo_create(widget->window);
212
gdk_cairo_region(cr, event->region);
214
gdk_cairo_set_source_color(cr, &c->da->style->black);
215
cairo_set_source_surface(cr, c->pixmap,
216
BORDER_SIZE, BORDER_SIZE);
218
check_cairo_status(cr);
224
/* Plugin constructor. */
225
static int cpu_constructor(Plugin * p, char ** fp)
227
/* Allocate plugin context and set into Plugin private data pointer. */
228
CPUPlugin * c = g_new0(CPUPlugin, 1);
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);
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);
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);
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);
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);
257
/* Plugin destructor. */
258
static void cpu_destructor(Plugin * p)
260
CPUPlugin * c = (CPUPlugin *) p->priv;
262
/* Disconnect the timer. */
263
g_source_remove(c->timer);
265
/* Deallocate memory. */
266
cairo_surface_destroy(c->pixmap);
267
g_free(c->stats_cpu);
271
/* Plugin descriptor. */
272
PluginClass cpu_plugin_class = {
274
PLUGINCLASS_VERSIONING,
277
name : N_("CPU Usage Monitor"),
279
description : N_("Display CPU usage"),
281
constructor : cpu_constructor,
282
destructor : cpu_destructor,