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