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