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