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