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