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