Adding upstream version 0.9.0.
[debian/lxpanel.git] / plugins / monitors / monitors.c
CommitLineData
514580cf
DB
1/**
2 * Plugin for the lxpanel.
3 *
4 * Displays several monitors in the panel.
5 *
6 * A lot of code in this plugin comes from the CPU plugin (that only displays a
7 * CPU monitor), that is distributed under the following terms :
8 *
0688b017
AG
9 * Copyright (C) 2010 Cyril Roelandt <steap@users.sourceforge.net>
10 * 2012-2014 Henry Gebhardt <hsggebhardt@googlemail.com>
11 * 2012 Rafał Mużyło <galtgendo@gmail.com>
7a1c5048
AG
12 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
13 * 2015 Rafał Mużyło <galtgendo@gmail.com>
0688b017 14 *
514580cf 15 * <terms>
6b775dbb 16 * Copyright (c) 2008-2014 LxDE Developers, see the file AUTHORS for details.
514580cf
DB
17 * Copyright (C) 2004 by Alexandre Pereira da Silva <alexandre.pereira@poli.usp.br>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 * </terms>
33 *
34 */
35
36/*
37 * HOWTO : Add your own monitor for the resource "foo".
38 *
39 * 1) Write the foo_update() function, that fills in the stats.
40 * 2) Write the foo_tooltip_update() function, that updates your tooltip. This
41 * is optional, but recommended.
42 * 3) Add a #define FOO_POSITION, and increment N_MONITORS.
43 * 4) Add :
44 * - the default color of your plugin ("default_colors" table)
45 * - the update function ("update_functions" table)
46 * - the tooltip update function ("tooltip_update" table)
47 * 5) Configuration :
48 * - edit the monitors_config() function so that a "Display FOO usage"
49 * checkbox appears in the prefs dialog.
50 * - edit the monitors_save() function so that a "DisplayFOO" string appears
51 * in the config file ("~/.config/lxpanel/<profile>/config")
52 * - edit the monitors_config() function so that a "FOO color" entry appears
53 * in the prefs dialog.
54 * - edit the monitors_save() function so that a "FOOColor" string appears
55 * in the config file.
56 * - edit the monitors_constructor() function so that options are correctly
57 * aplied. Adding something like :
6b775dbb 58 *
514580cf
DB
59 * else if (g_ascii_strcasecmp(s.t[0], "DisplayFOO") == 0)
60 * mp->displayed_monitors[FOO_POSITION] = atoi(s.t[1])
61 * else if (g_ascii_strcasecmp(s.t[0], "FOOColor") == 0)
6b775dbb 62 * colors[FOO_POSITION] = g_strndup(s.t[1], COLOR_SIZE-1);
514580cf
DB
63 *
64 * should be enough.
65 * 6) Enjoy.
66 */
67
68/*
6b775dbb 69 * FIXME : known BUGS :
514580cf
DB
70 * - when removing a monitor and re-adding it, it is drawn with a white
71 * border of BORDER_SIZE pixels around it.
72 */
73
74#include <stdlib.h>
75#include <glib/gi18n.h>
6b775dbb
AG
76#include <errno.h>
77#include <libfm/fm-gtk.h>
514580cf
DB
78
79#include "plugin.h"
514580cf
DB
80
81#include "dbg.h"
82
83
84#define PLUGIN_NAME "MonitorsPlugin"
85#define BORDER_SIZE 2 /* Pixels */
86#define DEFAULT_WIDTH 40 /* Pixels */
eea54180 87#define UPDATE_PERIOD 1 /* Seconds */
514580cf
DB
88#define COLOR_SIZE 8 /* In chars : #xxxxxx\0 */
89
90#ifndef ENTER
91#define ENTER fprintf(stderr, "Entering %s\n", __func__);
92#endif
93
94/*
6b775dbb 95 * Stats are stored in a circular buffer.
514580cf
DB
96 * Newest values are on the left of the ring cursor.
97 * Oldest values are on the right of the ring cursor.
98 */
99typedef float stats_set;
100
101struct Monitor {
514580cf
DB
102 GdkColor foreground_color; /* Foreground color for drawing area */
103 GtkWidget *da; /* Drawing area */
aaccad27 104 cairo_surface_t *pixmap; /* Pixmap to be drawn on drawing area */
514580cf
DB
105 gint pixmap_width; /* Width and size of the buffer */
106 gint pixmap_height; /* Does not include border size */
107 stats_set *stats; /* Circular buffer of values */
eea54180 108 stats_set total; /* Maximum possible value, as in mem_total*/
514580cf
DB
109 gint ring_cursor; /* Cursor for ring/circular buffer */
110 gchar *color; /* Color of the graph */
111 gboolean (*update) (struct Monitor *); /* Update function */
112 void (*update_tooltip) (struct Monitor *);
113};
114
115typedef struct Monitor Monitor;
116typedef gboolean (*update_func) (Monitor *);
117typedef void (*tooltip_update_func) (Monitor *);
118
6b775dbb 119/*
514580cf
DB
120 * Position of our monitors : monitor 0 will always be on the left of the
121 * plugin, monitor 1 on the right of monitor 0 (or on the left of the plugin if
122 * monitor 0 is not displayed), etc.
123 */
124#define CPU_POSITION 0
125#define MEM_POSITION 1
126#define N_MONITORS 2
127
128/* Our plugin */
129typedef struct {
6b775dbb
AG
130 LXPanel *panel;
131 config_setting_t *settings;
514580cf
DB
132 Monitor *monitors[N_MONITORS]; /* Monitors */
133 int displayed_monitors[N_MONITORS]; /* Booleans */
eea54180 134 char *action; /* What to do on click */
514580cf
DB
135 guint timer; /* Timer for regular updates */
136} MonitorsPlugin;
137
6b775dbb
AG
138/*
139 * Prototypes
514580cf 140 */
6b775dbb 141static void monitor_set_foreground_color(MonitorsPlugin *, Monitor *, const gchar *);
514580cf
DB
142
143/* CPU Monitor */
144static gboolean cpu_update(Monitor *);
145static void cpu_tooltip_update (Monitor *m);
146
147/* RAM Monitor */
148static gboolean mem_update(Monitor *);
149static void mem_tooltip_update (Monitor *m);
150
151
152static gboolean configure_event(GtkWidget*, GdkEventConfigure*, gpointer);
7a1c5048 153#if !GTK_CHECK_VERSION(3, 0, 0)
514580cf 154static gboolean expose_event(GtkWidget *, GdkEventExpose *, Monitor *);
7a1c5048
AG
155#else
156static gboolean draw(GtkWidget *, cairo_t *, Monitor *);
157#endif
514580cf
DB
158static void redraw_pixmap (Monitor *m);
159
160/* Monitors functions */
6b775dbb
AG
161static void monitors_destructor(gpointer);
162static gboolean monitors_apply_config(gpointer);
514580cf
DB
163
164
165/******************************************************************************
166 * Monitor functions *
167 ******************************************************************************/
168static Monitor*
6b775dbb 169monitor_init(MonitorsPlugin *mp, Monitor *m, gchar *color)
514580cf
DB
170{
171 ENTER;
172
173 m->da = gtk_drawing_area_new();
7a1c5048
AG
174 gtk_widget_add_events(m->da, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
175 GDK_BUTTON_MOTION_MASK);
6b775dbb 176 gtk_widget_set_size_request(m->da, DEFAULT_WIDTH, panel_get_height(mp->panel));
514580cf 177
6b775dbb 178 monitor_set_foreground_color(mp, m, color);
514580cf
DB
179
180 /* Signals */
6b775dbb 181 g_signal_connect(G_OBJECT(m->da), "configure-event",
514580cf 182 G_CALLBACK(configure_event), (gpointer) m);
7a1c5048 183#if !GTK_CHECK_VERSION(3, 0, 0)
514580cf
DB
184 g_signal_connect (G_OBJECT(m->da), "expose-event",
185 G_CALLBACK(expose_event), (gpointer) m);
7a1c5048
AG
186#else
187 g_signal_connect (G_OBJECT(m->da), "draw",
188 G_CALLBACK(draw), (gpointer) m);
189#endif
514580cf
DB
190
191 return m;
192}
193
194static void
195monitor_free(Monitor *m)
196{
197 if (!m)
198 return;
199
200 g_free(m->color);
514580cf 201 if (m->pixmap)
aaccad27 202 cairo_surface_destroy(m->pixmap);
514580cf
DB
203 if (m->stats)
204 g_free(m->stats);
205 g_free(m);
206
207 return;
208}
209
210static void
6b775dbb 211monitor_set_foreground_color(MonitorsPlugin *mp, Monitor *m, const gchar *color)
514580cf
DB
212{
213 g_free(m->color);
214 m->color = g_strndup(color, COLOR_SIZE - 1);
215 gdk_color_parse(color, &m->foreground_color);
514580cf
DB
216}
217/******************************************************************************
218 * End of monitor functions *
219 ******************************************************************************/
220
221/******************************************************************************
222 * CPU monitor *
223 ******************************************************************************/
6b775dbb 224typedef unsigned long long CPUTick;/* Value from /proc/stat */
514580cf
DB
225typedef float CPUSample; /* Saved CPU utilization value as 0.0..1.0 */
226
227struct cpu_stat {
228 CPUTick u, n, s, i; /* User, nice, system, idle */
229};
230
231static gboolean
232cpu_update(Monitor * c)
233{
234 static struct cpu_stat previous_cpu_stat = { 0, 0, 0, 0 };
235
236 if ((c->stats != NULL) && (c->pixmap != NULL))
237 {
238 /* Open statistics file and scan out CPU usage. */
239 struct cpu_stat cpu;
240 FILE * stat = fopen("/proc/stat", "r");
241 if (stat == NULL)
242 return TRUE;
6b775dbb 243 int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu",
514580cf
DB
244 &cpu.u, &cpu.n, &cpu.s, &cpu.i);
245 fclose(stat);
246
247 /* Ensure that fscanf succeeded. */
248 if (fscanf_result == 4)
249 {
250 /* Comcolors delta from previous statistics. */
251 struct cpu_stat cpu_delta;
252 cpu_delta.u = cpu.u - previous_cpu_stat.u;
253 cpu_delta.n = cpu.n - previous_cpu_stat.n;
254 cpu_delta.s = cpu.s - previous_cpu_stat.s;
255 cpu_delta.i = cpu.i - previous_cpu_stat.i;
256
257 /* Copy current to previous. */
258 memcpy(&previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
259
260 /* Comcolors user+nice+system as a fraction of total.
261 * Introduce this sample to ring buffer, increment and wrap ring
262 * buffer cursor. */
263 float cpu_uns = cpu_delta.u + cpu_delta.n + cpu_delta.s;
264 c->stats[c->ring_cursor] = cpu_uns / (cpu_uns + cpu_delta.i);
265 c->ring_cursor += 1;
266 if (c->ring_cursor >= c->pixmap_width)
267 c->ring_cursor = 0;
268
269 /* Redraw with the new sample. */
270 redraw_pixmap(c);
271 }
272 }
273 return TRUE;
274}
275
276static void
277cpu_tooltip_update (Monitor *m)
278{
514580cf 279 if (m && m->stats) {
eea54180 280 gchar *tooltip_text;
514580cf
DB
281 gint ring_pos = (m->ring_cursor == 0)
282 ? m->pixmap_width - 1 : m->ring_cursor - 1;
eea54180 283 tooltip_text = g_strdup_printf(_("CPU usage: %.2f%%"),
514580cf
DB
284 m->stats[ring_pos] * 100);
285 gtk_widget_set_tooltip_text(m->da, tooltip_text);
eea54180 286 g_free(tooltip_text);
514580cf
DB
287 }
288}
289
290/******************************************************************************
291 * End of CPU Monitor *
292 ******************************************************************************/
293
294/******************************************************************************
295 * RAM Monitor *
296 ******************************************************************************/
297static gboolean
298mem_update(Monitor * m)
299{
6b775dbb 300 ENTER;
514580cf
DB
301
302 FILE *meminfo;
19ab5cea 303 char buf[80];
6b775dbb
AG
304 long int mem_total = 0;
305 long int mem_free = 0;
306 long int mem_buffers = 0;
307 long int mem_cached = 0;
308 unsigned int readmask = 0x8 | 0x4 | 0x2 | 0x1;
309
310 if (!m->stats || !m->pixmap)
311 RET(TRUE);
312
313 meminfo = fopen("/proc/meminfo", "r");
314 if (!meminfo) {
315 g_warning("monitors: Could not open /proc/meminfo: %d, %s",
316 errno, strerror(errno));
317 RET(FALSE);
318 }
514580cf 319
19ab5cea 320 while (readmask && fgets(buf, sizeof(buf), meminfo)) {
6b775dbb
AG
321 if (sscanf(buf, "MemTotal: %ld kB\n", &mem_total) == 1) {
322 readmask ^= 0x1;
323 continue;
514580cf 324 }
6b775dbb
AG
325 if (sscanf(buf, "MemFree: %ld kB\n", &mem_free) == 1) {
326 readmask ^= 0x2;
327 continue;
514580cf 328 }
6b775dbb
AG
329 if (sscanf(buf, "Buffers: %ld kB\n", &mem_buffers) == 1) {
330 readmask ^= 0x4;
331 continue;
514580cf 332 }
6b775dbb
AG
333 if (sscanf(buf, "Cached: %ld kB\n", &mem_cached) == 1) {
334 readmask ^= 0x8;
335 continue;
514580cf 336 }
6b775dbb 337 }
514580cf 338
6b775dbb 339 fclose(meminfo);
514580cf 340
6b775dbb
AG
341 if (readmask) {
342 g_warning("monitors: Couldn't read all values from /proc/meminfo: "
343 "readmask %x", readmask);
344 RET(FALSE);
345 }
eea54180 346
6b775dbb 347 m->total = mem_total;
514580cf 348
6b775dbb
AG
349 /* Adding stats to the buffer:
350 * It is debatable if 'mem_buffers' counts as free or not. I'll go with
351 * 'free', because it can be flushed fairly quickly, and generally
352 * isn't necessary to keep in memory.
353 * It is hard to draw the line, which caches should be counted as free,
354 * and which not. Pagecaches, dentry, and inode caches are quickly
355 * filled up again for almost any use case. Hence I would not count
356 * them as 'free'.
357 * 'mem_cached' definitely counts as 'free' because it is immediately
358 * released should any application need it. */
359 m->stats[m->ring_cursor] = (mem_total - mem_buffers - mem_free -
360 mem_cached) / (float)mem_total;
514580cf 361
6b775dbb
AG
362 m->ring_cursor++;
363 if (m->ring_cursor >= m->pixmap_width)
364 m->ring_cursor = 0;
514580cf 365
6b775dbb
AG
366 /* Redraw the pixmap, with the new sample */
367 redraw_pixmap (m);
514580cf
DB
368
369 RET(TRUE);
370}
371
372static void
373mem_tooltip_update (Monitor *m)
374{
514580cf 375 if (m && m->stats) {
eea54180 376 gchar *tooltip_text;
514580cf
DB
377 gint ring_pos = (m->ring_cursor == 0)
378 ? m->pixmap_width - 1 : m->ring_cursor - 1;
eea54180
DB
379 tooltip_text = g_strdup_printf(_("RAM usage: %.1fMB (%.2f%%)"),
380 m->stats[ring_pos] * m->total / 1024,
514580cf
DB
381 m->stats[ring_pos] * 100);
382 gtk_widget_set_tooltip_text(m->da, tooltip_text);
eea54180 383 g_free(tooltip_text);
514580cf
DB
384 }
385}
386/******************************************************************************
387 * End of RAM Monitor *
388 ******************************************************************************/
389
390/******************************************************************************
391 * Basic events handlers *
392 ******************************************************************************/
393static gboolean
6b775dbb 394configure_event(GtkWidget* widget, GdkEventConfigure* dummy, gpointer data)
514580cf
DB
395{
396 (void) dummy;
6b775dbb 397 GtkAllocation allocation;
514580cf
DB
398
399 int new_pixmap_width, new_pixmap_height;
400
6b775dbb
AG
401 gtk_widget_get_allocation(widget, &allocation);
402 new_pixmap_width = allocation.width - BORDER_SIZE * 2;
403 new_pixmap_height = allocation.height - BORDER_SIZE *2;
514580cf
DB
404 Monitor *m;
405
406 m = (Monitor *) data;
407
408 if (new_pixmap_width > 0 && new_pixmap_height > 0)
409 {
410 /*
411 * If the stats buffer does not exist (first time we get inside this
412 * function) or its size changed, reallocate the buffer and preserve
413 * existing data.
414 */
415 if (!m->stats || (new_pixmap_width != m->pixmap_width))
416 {
417 stats_set *new_stats = g_new0(stats_set, new_pixmap_width);
6b775dbb 418
514580cf
DB
419 if (!new_stats)
420 return TRUE;
421
422 if (m->stats)
423 {
424 /* New allocation is larger.
425 * Add new "oldest" samples of zero following the cursor*/
426 if (new_pixmap_width > m->pixmap_width)
427 {
428 /* Number of values between the ring cursor and the end of
429 * the buffer */
430 int nvalues = m->pixmap_width - m->ring_cursor;
431
432 memcpy(new_stats,
433 m->stats,
434 m->ring_cursor * sizeof (stats_set));
435 memcpy(new_stats + nvalues,
436 m->stats + m->ring_cursor,
437 nvalues * sizeof(stats_set));
438 }
439 /* New allocation is smaller, but still larger than the ring
440 * buffer cursor */
441 else if (m->ring_cursor <= new_pixmap_width)
442 {
443 /* Numver of values that can be stored between the end of
444 * the new buffer and the ring cursor */
445 int nvalues = new_pixmap_width - m->ring_cursor;
446 memcpy(new_stats,
447 m->stats,
448 m->ring_cursor * sizeof(stats_set));
449 memcpy(new_stats + m->ring_cursor,
450 m->stats + m->pixmap_width - nvalues,
451 nvalues * sizeof(stats_set));
452 }
453 /* New allocation is smaller, and also smaller than the ring
6b775dbb
AG
454 * buffer cursor. Discard all oldest samples following the ring
455 * buffer cursor and additional samples at the beginning of the
514580cf
DB
456 * buffer. */
457 else
458 {
459 memcpy(new_stats,
460 m->stats + m->ring_cursor - new_pixmap_width,
461 new_pixmap_width * sizeof(stats_set));
462 }
463 g_free(m->stats);
464 }
465 m->stats = new_stats;
466 }
467
468 m->pixmap_width = new_pixmap_width;
469 m->pixmap_height = new_pixmap_height;
470 if (m->pixmap)
aaccad27
AL
471 cairo_surface_destroy(m->pixmap);
472 m->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
514580cf 473 m->pixmap_width,
aaccad27
AL
474 m->pixmap_height);
475 check_cairo_surface_status(&m->pixmap);
514580cf
DB
476 redraw_pixmap(m);
477 }
6b775dbb 478
514580cf
DB
479 return TRUE;
480}
481
7a1c5048 482#if !GTK_CHECK_VERSION(3, 0, 0)
514580cf 483static gboolean
6b775dbb 484expose_event(GtkWidget * widget, GdkEventExpose * event, Monitor *m)
7a1c5048
AG
485#else
486static gboolean
487draw(GtkWidget * widget, cairo_t * cr, Monitor *m)
488#endif
514580cf
DB
489{
490 /* Draw the requested part of the pixmap onto the drawing area.
491 * Translate it in both x and y by the border size. */
492 if (m->pixmap != NULL)
493 {
7a1c5048 494#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb
AG
495 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
496 GtkStyle *style = gtk_widget_get_style(m->da);
aaccad27
AL
497 gdk_cairo_region(cr, event->region);
498 cairo_clip(cr);
6b775dbb 499 gdk_cairo_set_source_color(cr, &style->black);
7a1c5048
AG
500#else
501 cairo_set_source_rgb(cr, 0, 0, 0); // FIXME: set the color from style
502#endif
aaccad27
AL
503 cairo_set_source_surface(cr, m->pixmap, BORDER_SIZE, BORDER_SIZE);
504 cairo_paint(cr);
505 check_cairo_status(cr);
7a1c5048 506#if !GTK_CHECK_VERSION(3, 0, 0)
aaccad27 507 cairo_destroy(cr);
7a1c5048 508#endif
514580cf 509 }
6b775dbb 510
514580cf 511 return FALSE;
eea54180
DB
512}
513
514
6b775dbb 515static gboolean monitors_button_press_event(GtkWidget* widget, GdkEventButton* evt, LXPanel *panel)
eea54180 516{
7a1c5048
AG
517 MonitorsPlugin* mp;
518
519 if (evt->button != 1)
520 return FALSE;
eea54180 521
7a1c5048 522 mp = lxpanel_plugin_get_data(widget);
eea54180 523 if (mp->action != NULL)
6b775dbb 524 fm_launch_command_simple(NULL, NULL, 0, mp->action, NULL);
eea54180 525 else
6b775dbb 526 fm_launch_command_simple(NULL, NULL, 0, "lxtask", NULL);
eea54180
DB
527
528 return TRUE;
529}
514580cf
DB
530/******************************************************************************
531 * End of basic events handlers *
532 ******************************************************************************/
533
534static void
535redraw_pixmap (Monitor *m)
536{
537 int i;
aaccad27 538 cairo_t *cr = cairo_create(m->pixmap);
6b775dbb
AG
539 GtkStyle *style = gtk_widget_get_style(m->da);
540
aaccad27 541 cairo_set_line_width (cr, 1.0);
514580cf
DB
542
543 /* Erase pixmap */
6b775dbb 544 gdk_cairo_set_source_color(cr, &style->black);
aaccad27 545 cairo_paint(cr);
514580cf 546
aaccad27 547 gdk_cairo_set_source_color(cr, &m->foreground_color);
514580cf
DB
548 for (i = 0; i < m->pixmap_width; i++)
549 {
eea54180 550 unsigned int drawing_cursor = (m->ring_cursor + i) % m->pixmap_width;
514580cf 551
eea54180 552 /* Draw one bar of the graph */
aaccad27
AL
553 cairo_move_to(cr, i + 0.5, m->pixmap_height);
554 cairo_line_to(cr, i + 0.5, (1.0 - m->stats[drawing_cursor]) * m->pixmap_height);
555 cairo_stroke(cr);
514580cf
DB
556 }
557
aaccad27
AL
558 check_cairo_status(cr);
559 cairo_destroy(cr);
514580cf
DB
560 /* Redraw pixmap */
561 gtk_widget_queue_draw(m->da);
562}
563
564
6b775dbb
AG
565static update_func update_functions [N_MONITORS] = {
566 [CPU_POSITION] = cpu_update,
567 [MEM_POSITION] = mem_update
514580cf
DB
568};
569
570static char *default_colors[N_MONITORS] = {
571 [CPU_POSITION] = "#0000FF",
572 [MEM_POSITION] = "#FF0000"
573};
574
575
576static tooltip_update_func tooltip_update[N_MONITORS] = {
577 [CPU_POSITION] = cpu_tooltip_update,
578 [MEM_POSITION] = mem_tooltip_update
579};
580
581/* Colors currently used. We cannot store them in the "struct Monitor"s where
582 * they belong, because we free these when the user removes them. And since we
583 * want the colors to stay the same even after removing/adding a widget... */
6b775dbb
AG
584static char *colors[N_MONITORS] = {
585 NULL,
586 NULL
514580cf
DB
587};
588
6b775dbb 589/*
eea54180 590 * This function is called every UPDATE_PERIOD seconds. It updates all
514580cf
DB
591 * monitors.
592 */
593static gboolean
594monitors_update(gpointer data)
595{
596 MonitorsPlugin *mp;
597 int i;
6b775dbb
AG
598
599 if (g_source_is_destroyed(g_main_current_source()))
600 return FALSE;
514580cf
DB
601 mp = (MonitorsPlugin *) data;
602 if (!mp)
603 RET(FALSE);
604
605 for (i = 0; i < N_MONITORS; i++)
606 {
607 if (mp->monitors[i])
608 {
609 mp->monitors[i]->update(mp->monitors[i]);
610 if (mp->monitors[i]->update_tooltip)
611 mp->monitors[i]->update_tooltip(mp->monitors[i]);
612 }
613 }
614
615 return TRUE;
616}
617
618static Monitor*
6b775dbb 619monitors_add_monitor (GtkWidget *p, MonitorsPlugin *mp, update_func update,
514580cf
DB
620 tooltip_update_func update_tooltip, gchar *color)
621{
622 ENTER;
623
624 Monitor *m;
625
626 m = g_new0(Monitor, 1);
6b775dbb 627 m = monitor_init(mp, m, color);
514580cf
DB
628 m->update = update;
629 m->update_tooltip = update_tooltip;
6b775dbb 630 gtk_box_pack_start(GTK_BOX(p), m->da, FALSE, FALSE, 0);
514580cf
DB
631 gtk_widget_show(m->da);
632
633 RET(m);
634}
635
6b775dbb
AG
636static GtkWidget *
637monitors_constructor(LXPanel *panel, config_setting_t *settings)
514580cf
DB
638{
639 ENTER;
640 int i;
641 MonitorsPlugin *mp;
6b775dbb
AG
642 GtkWidget *p;
643 const char *tmp;
514580cf
DB
644
645 mp = g_new0(MonitorsPlugin, 1);
6b775dbb
AG
646 mp->panel = panel;
647 mp->settings = settings;
514580cf 648
6b775dbb
AG
649 p = gtk_hbox_new(TRUE, 2);
650 lxpanel_plugin_set_data(p, mp, monitors_destructor);
514580cf 651
6b775dbb
AG
652 /* First time we use this plugin : only display CPU usage */
653 mp->displayed_monitors[CPU_POSITION] = 1;
514580cf 654
6b775dbb
AG
655 /* Apply options */
656 config_setting_lookup_int(settings, "DisplayCPU",
657 &mp->displayed_monitors[CPU_POSITION]);
658 config_setting_lookup_int(settings, "DisplayRAM",
659 &mp->displayed_monitors[MEM_POSITION]);
660 if (config_setting_lookup_string(settings, "Action", &tmp))
661 mp->action = g_strdup(tmp);
662 if (config_setting_lookup_string(settings, "CPUColor", &tmp))
663 colors[CPU_POSITION] = g_strndup(tmp, COLOR_SIZE-1);
664 if (config_setting_lookup_string(settings, "RAMColor", &tmp))
665 colors[MEM_POSITION] = g_strndup(tmp, COLOR_SIZE-1);
666
667 /* Initializing monitors */
514580cf
DB
668 for (i = 0; i < N_MONITORS; i++)
669 {
670 if (!colors[i])
671 colors[i] = g_strndup(default_colors[i], COLOR_SIZE-1);
672
673 if (mp->displayed_monitors[i])
674 {
6b775dbb
AG
675 mp->monitors[i] = monitors_add_monitor(p, mp,
676 update_functions[i],
677 tooltip_update[i],
514580cf
DB
678 colors[i]);
679 }
680 }
6b775dbb 681
514580cf 682 /* Adding a timer : monitors will be updated every UPDATE_PERIOD
eea54180
DB
683 * seconds */
684 mp->timer = g_timeout_add_seconds(UPDATE_PERIOD, (GSourceFunc) monitors_update,
514580cf 685 (gpointer) mp);
6b775dbb 686 RET(p);
514580cf
DB
687}
688
689static void
6b775dbb 690monitors_destructor(gpointer user_data)
514580cf
DB
691{
692 ENTER;
693 int i;
694 MonitorsPlugin *mp;
6b775dbb
AG
695
696 mp = (MonitorsPlugin *) user_data;
514580cf
DB
697
698 /* Removing timer */
699 g_source_remove(mp->timer);
6b775dbb 700
514580cf
DB
701 /* Freeing all monitors */
702 for (i = 0; i < N_MONITORS; i++)
703 {
704 if (mp->monitors[i])
705 monitor_free(mp->monitors[i]);
706 }
707
eea54180 708 g_free(mp->action);
6b775dbb 709 g_free(mp);
514580cf
DB
710
711 RET();
712}
713
714
6b775dbb
AG
715static GtkWidget *
716monitors_config (LXPanel *panel, GtkWidget *p)
514580cf
DB
717{
718 ENTER;
719
720 GtkWidget *dialog;
721 MonitorsPlugin *mp;
722
6b775dbb 723 mp = lxpanel_plugin_get_data(p);
514580cf 724
6b775dbb
AG
725 dialog = lxpanel_generic_config_dlg(_("Resource monitors"),
726 panel, monitors_apply_config, p,
514580cf
DB
727 _("Display CPU usage"), &mp->displayed_monitors[0], CONF_TYPE_BOOL,
728 _("CPU color"), &colors[CPU_POSITION], CONF_TYPE_STR,
729 _("Display RAM usage"), &mp->displayed_monitors[1], CONF_TYPE_BOOL,
730 _("RAM color"), &colors[MEM_POSITION], CONF_TYPE_STR,
eea54180 731 _("Action when clicked (default: lxtask)"), &mp->action, CONF_TYPE_STR,
514580cf 732 NULL);
514580cf 733
6b775dbb 734 RET(dialog);
514580cf
DB
735}
736
6b775dbb
AG
737static gboolean
738monitors_apply_config (gpointer user_data)
514580cf
DB
739{
740 ENTER;
6b775dbb 741 GtkWidget *p = user_data;
514580cf 742 MonitorsPlugin *mp;
6b775dbb 743 mp = lxpanel_plugin_get_data(p);
514580cf
DB
744
745 int i;
746 int current_n_monitors = 0;
747
748start:
749 for (i = 0; i < N_MONITORS; i++)
750 {
751 if (mp->displayed_monitors[i])
752 current_n_monitors++;
753
754 if (mp->displayed_monitors[i] && !mp->monitors[i])
755 {
756 /* We've just activated monitor<i> */
6b775dbb
AG
757 mp->monitors[i] = monitors_add_monitor(p, mp,
758 update_functions[i],
759 tooltip_update[i],
514580cf
DB
760 colors[i]);
761 /*
762 * It is probably best for users if their monitors are always
763 * displayed in the same order : the CPU monitor always on the left,
764 * the RAM monitor always on the right of the CPU monitor (if the
765 * CPU monitor is displayed), etc. That's why we do not just use
766 * gtk_box_pack_start/gtk_box_pack_end, and use
767 * gtk_box_reorder_child.
768 */
6b775dbb 769 gtk_box_reorder_child(GTK_BOX(p),
514580cf
DB
770 mp->monitors[i]->da,current_n_monitors-1);
771 }
772 else if (!mp->displayed_monitors[i] && mp->monitors[i])
773 {
774 /* We've just removed monitor<i> */
6b775dbb 775 gtk_widget_destroy(mp->monitors[i]->da);
514580cf
DB
776 monitor_free(mp->monitors[i]);
777 mp->monitors[i] = NULL;
778 }
6b775dbb 779 if (mp->monitors[i] &&
514580cf
DB
780 strncmp(mp->monitors[i]->color, colors[i], COLOR_SIZE) != 0)
781 {
782 /* We've changed the color */
6b775dbb 783 monitor_set_foreground_color(mp, mp->monitors[i], colors[i]);
514580cf
DB
784 }
785 }
786
787 /* Workaround meant to prevent users to display no monitor at all.
788 * FIXME : write something clean. When there is only one monitor displayed,
789 * its toggle button should not be clickable in the prefs. */
790 if (current_n_monitors == 0)
791 {
792 mp->displayed_monitors[0] = 1;
793 goto start;
794 }
6b775dbb
AG
795 config_group_set_int(mp->settings, "DisplayCPU", mp->displayed_monitors[CPU_POSITION]);
796 config_group_set_int(mp->settings, "DisplayRAM", mp->displayed_monitors[MEM_POSITION]);
797 config_group_set_string(mp->settings, "Action", mp->action);
798 config_group_set_string(mp->settings, "CPUColor",
799 mp->monitors[CPU_POSITION] ? colors[CPU_POSITION] : NULL);
800 config_group_set_string(mp->settings, "RAMColor",
801 mp->monitors[MEM_POSITION] ? colors[MEM_POSITION] : NULL);
802
803 RET(FALSE);
514580cf
DB
804}
805
514580cf 806
6b775dbb 807FM_DEFINE_MODULE(lxpanel_gtk, monitors)
514580cf 808
6b775dbb
AG
809LXPanelPluginInit fm_module_init_lxpanel_gtk = {
810 .name = N_("Resource monitors"),
811 .description = N_("Display monitors (CPU, RAM)"),
812 .new_instance = monitors_constructor,
813 .config = monitors_config,
814 .button_press_event = monitors_button_press_event
514580cf 815};
eea54180
DB
816
817/* vim: set sw=4 sts=4 et : */