1 #include <config.h>
2 #include <stdio.h>
3 #include <sys/stat.h>
4 #include <unistd.h>
5 #include <signal.h>
6 #include <dirent.h>
7 #include <string.h>
8 #include <time.h>
9 #include <glib.h>
10 #include <gdk/gdkx.h>
11 #include <gtk/gtk.h>
12 #include <gio/gio.h>
13 #include <mate-panel-applet.h>
14 #include <mate-panel-applet-gsettings.h>
15 #include <math.h>
16 
17 #include "global.h"
18 
19 /*
20   Shifts data right
21 
22   data[i+1] = data[i]
23 
24   data[i] are int*, so we just move the pointer, not the data.
25   But moving data loses data[n-1], so we save data[n-1] and reuse
26   it as new data[0]. In fact, we rotate data[].
27 
28 */
29 
30 static void
shift_right(LoadGraph * g)31 shift_right(LoadGraph *g)
32 {
33     guint64 *last_data;
34     gsize i;
35 
36     /* data[g->draw_width - 1] becomes data[0] */
37     last_data = g->data[g->draw_width - 1];
38 
39     /* data[i+1] = data[i] */
40     for (i = g->draw_width - 1; i != 0; --i)
41       g->data[i] = g->data[i - 1];
42 
43     g->data[0] = last_data;
44 }
45 
46 
47 /* Redraws the backing pixmap for the load graph and updates the window */
48 static void
load_graph_draw(LoadGraph * g)49 load_graph_draw (LoadGraph *g)
50 {
51   guint i, j, k;
52   cairo_t *cr;
53   MultiloadApplet *multiload;
54 
55   multiload = g->multiload;
56 
57   /* we might get called before the configure event so that
58    * g->disp->allocation may not have the correct size
59    * (after the user resized the applet in the prop dialog). */
60 
61   if (!g->surface)
62     g->surface = gdk_window_create_similar_surface (gtk_widget_get_window (g->disp),
63                                                     CAIRO_CONTENT_COLOR,
64                                                     (int) g->draw_width,
65                                                     (int) g->draw_height);
66 
67   cr = cairo_create (g->surface);
68   cairo_set_line_width (cr, 1.0);
69   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
70   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
71 
72   /* all graphs except Load and Net go this path */
73   switch (g->id) {
74 
75   /* This is for network graph */
76   case graph_netload2: {
77     guint64 maxnet = 1;
78     guint64 segments = 1;
79     guint64 net_threshold;
80     guint   level = 0;
81     double  ratio;
82     double  spacing;
83 
84     for (i = 0; i < g->draw_width; i++)
85     {
86       g->pos [i] = g->draw_height - 1;
87       if (g->data[i][3] > maxnet)
88         maxnet = g->data[i][3];
89     }
90     //printf("max = %d ", maxnet);
91     if (maxnet > multiload->net_threshold3) {
92       net_threshold = multiload->net_threshold3;
93       level = 3;
94     }
95     else
96       if (maxnet > multiload->net_threshold2) {
97         net_threshold = multiload->net_threshold2;
98         level = 2;
99       }
100       else {
101         net_threshold = multiload->net_threshold1;
102         if (maxnet >= multiload->net_threshold1)
103             level = 1;
104       }
105 
106     //printf("level %d maxnet = %d ", level, maxnet);
107     maxnet = maxnet/net_threshold;
108     segments = MAX (maxnet+1,1);
109     ratio = (double) g->draw_height / (double) (net_threshold*segments);
110     //printf("segments %d ratio = %f t1=%ld t2=%ld t3=%ld t=%ld\n", segments, ratio, multiload->net_threshold1, multiload->net_threshold2, multiload->net_threshold3, multiload->net_threshold);
111 
112     for (j = 0; j < g->n-1; j++)
113     {
114       gdk_cairo_set_source_rgba (cr, &(g->colors [j]));
115 
116       for (i = 0; i < g->draw_width; i++)
117       {
118         double x = (double) (g->draw_width - i) - 0.5;
119         cairo_move_to (cr, x, (double) g->pos[i] + 0.5);
120         cairo_line_to (cr, x, (double) g->pos[i] - 0.5 - (((double) g->data [i][j] * ratio)));
121         g->pos [i] -= (guint64) ((double) g->data [i][j] * ratio);
122       }
123       cairo_stroke (cr);
124     }
125 
126     for (j = g->n-1; j < g->n; j++)
127     {
128       gdk_cairo_set_source_rgba (cr, &(g->colors [j]));
129       for (i = 0; i < g->draw_width; i++)
130       {
131           double x = (double) (g->draw_width - i) - 0.5;
132           cairo_move_to (cr, x, (double) g->pos[i] + 0.5);
133           cairo_line_to (cr, x, 0.5);
134       }
135       cairo_stroke (cr);
136     }
137 
138     /* draw grid lines if needed */
139     gdk_cairo_set_source_rgba (cr, &(g->colors [4]));
140     for (k = 0; k < segments -1; k++)
141     {
142       spacing = ((double) g->draw_height / (double) segments) * (k+1);
143       cairo_move_to (cr, 0.5, spacing);
144       cairo_line_to (cr, (double) g->draw_width - 0.5, spacing);
145     }
146     cairo_stroke (cr);
147     /* draw indicator if needed */
148     if (level > 0)
149     {
150       gdk_cairo_set_source_rgba (cr, &(g->colors [5]));
151       for (k = 0; k< level; k++ )
152         cairo_rectangle (cr,
153                          0.5, (double) k * 2.0 * (double) g->draw_height / 5.0,
154                          5.0, (double) g->draw_height / 5.0);
155       cairo_fill(cr);
156     }
157     cairo_stroke (cr);
158     break;
159   }
160 
161   /* this is Load graph */
162   case graph_loadavg: {
163     double load;
164     guint64 maxload = 1;
165     for (i = 0; i < g->draw_width; i++)
166     {
167       g->pos [i] = g->draw_height - 1;
168       /* find maximum value */
169       if (g->data[i][0] > maxload)
170         maxload = g->data[i][0];
171     }
172     load = ceil ((double) maxload / (double) g->draw_height) + 1.0;
173 
174     for (j = 0; j < g->n; j++)
175     {
176       gdk_cairo_set_source_rgba (cr, &(g->colors [j]));
177 
178       for (i = 0; i < g->draw_width; i++)
179       {
180         double x = (double) (g->draw_width - i) - 0.5;
181         cairo_move_to (cr, x, (double) g->pos[i] + 0.5);
182         if (j == 0)
183         {
184           cairo_line_to (cr, x, (double) g->pos[i] - (((double) g->data [i][j] - 0.5)/load));
185         }
186         else
187         {
188           cairo_line_to (cr, x, 0.5);
189         }
190         g->pos [i] -= (guint64) ((double) g->data [i][j] / load);
191       }
192       cairo_stroke (cr);
193     }
194 
195     /* draw grid lines in Load graph if needed */
196     gdk_cairo_set_source_rgba (cr, &(g->colors [2]));
197 
198     double spacing;
199     for (k = 0; k < load - 1; k++)
200     {
201       spacing = ((double) g->draw_height/load) * (k+1);
202       cairo_move_to (cr, 0.5, spacing);
203       cairo_line_to (cr, (double) g->draw_width - 0.5, spacing);
204     }
205 
206     cairo_stroke (cr);
207     break;
208   }
209 
210   default:
211     for (i = 0; i < g->draw_width; i++)
212       g->pos [i] = g->draw_height - 1;
213 
214     for (j = 0; j < g->n; j++)
215     {
216       gdk_cairo_set_source_rgba (cr, &(g->colors [j]));
217 
218       for (i = 0; i < g->draw_width; i++)
219       {
220         if (g->data [i][j] != 0)
221         {
222           double x = (double) (g->draw_width - i) - 0.5;
223           cairo_move_to (cr, x, (double) g->pos[i] + 0.5);
224           cairo_line_to (cr, x, (double) g->pos[i] - (double) g->data [i][j] - 0.5);
225         }
226         g->pos [i] -= g->data [i][j];
227       }
228       cairo_stroke (cr);
229     }
230   }
231 
232   gtk_widget_queue_draw (g->disp);
233 
234   cairo_destroy (cr);
235 }
236 
237 /* Updates the load graph when the timeout expires */
238 static gboolean
load_graph_update(LoadGraph * g)239 load_graph_update (LoadGraph *g)
240 {
241     if (g->data == NULL)
242         return TRUE;
243 
244     shift_right(g);
245 
246     if (g->tooltip_update)
247         multiload_applet_tooltip_update (g);
248 
249     g->get_data (g->draw_height, g->data [0], g);
250 
251     load_graph_draw (g);
252     return TRUE;
253 }
254 
255 void
load_graph_unalloc(LoadGraph * g)256 load_graph_unalloc (LoadGraph *g)
257 {
258     gsize i;
259 
260     if (!g->allocated)
261         return;
262 
263     for (i = 0; i < g->draw_width; i++)
264     {
265         g_free (g->data [i]);
266     }
267 
268     g_free (g->data);
269     g_free (g->pos);
270 
271     g->pos = NULL;
272     g->data = NULL;
273 
274     g->size = CLAMP (g_settings_get_uint (g->multiload->settings, GRAPH_SIZE_KEY),
275                      GRAPH_SIZE_MIN,
276                      GRAPH_SIZE_MAX);
277 
278     if (g->surface) {
279         cairo_surface_destroy (g->surface);
280         g->surface = NULL;
281     }
282 
283     g->allocated = FALSE;
284 }
285 
286 static void
load_graph_alloc(LoadGraph * g)287 load_graph_alloc (LoadGraph *g)
288 {
289     gsize i;
290     gsize data_size;
291 
292     if (g->allocated)
293         return;
294 
295     g->data = g_new0 (guint64 *, g->draw_width);
296     g->pos = g_new0 (guint64, g->draw_width);
297 
298     data_size = sizeof (guint64) * g->n;
299 
300     for (i = 0; i < g->draw_width; i++) {
301         g->data [i] = g_malloc0 (data_size);
302     }
303 
304     g->allocated = TRUE;
305 }
306 
307 static gint
load_graph_configure(GtkWidget * widget,GdkEventConfigure * event,gpointer data_ptr)308 load_graph_configure (GtkWidget *widget, GdkEventConfigure *event,
309                       gpointer data_ptr)
310 {
311     GtkAllocation allocation;
312     LoadGraph *c = (LoadGraph *) data_ptr;
313 
314     load_graph_unalloc (c);
315 
316     gtk_widget_get_allocation (c->disp, &allocation);
317 
318     c->draw_width = (gsize) allocation.width;
319     c->draw_height = (guint64) allocation.height;
320     c->draw_width = MAX (c->draw_width, 1);
321     c->draw_height = MAX (c->draw_height, 1);
322 
323     load_graph_alloc (c);
324 
325     if (!c->surface)
326         c->surface = gdk_window_create_similar_surface (gtk_widget_get_window (c->disp),
327                                                         CAIRO_CONTENT_COLOR,
328                                                         (int) c->draw_width,
329                                                         (int) c->draw_height);
330     gtk_widget_queue_draw (widget);
331 
332     return TRUE;
333 }
334 
335 static gint
load_graph_expose(GtkWidget * widget,cairo_t * cr,gpointer data_ptr)336 load_graph_expose (GtkWidget *widget,
337                    cairo_t *cr,
338                    gpointer data_ptr)
339 {
340     LoadGraph *g = (LoadGraph *) data_ptr;
341 
342     cairo_set_source_surface (cr, g->surface, 0, 0);
343     cairo_paint (cr);
344 
345     return FALSE;
346 }
347 
348 static void
load_graph_destroy(GtkWidget * widget,gpointer data_ptr)349 load_graph_destroy (GtkWidget *widget, gpointer data_ptr)
350 {
351     LoadGraph *g = (LoadGraph *) data_ptr;
352 
353     load_graph_stop (g);
354 
355     gtk_widget_destroy(widget);
356 }
357 
358 static gboolean
load_graph_clicked(GtkWidget * widget,GdkEventButton * event,LoadGraph * load)359 load_graph_clicked (GtkWidget *widget, GdkEventButton *event, LoadGraph *load)
360 {
361     load->multiload->last_clicked = load->id;
362 
363     return FALSE;
364 }
365 
366 static gboolean
load_graph_enter_cb(GtkWidget * widget,GdkEventCrossing * event,gpointer data)367 load_graph_enter_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
368 {
369     LoadGraph *graph;
370     graph = (LoadGraph *)data;
371 
372     graph->tooltip_update = TRUE;
373     multiload_applet_tooltip_update(graph);
374 
375     return TRUE;
376 }
377 
378 static gboolean
load_graph_leave_cb(GtkWidget * widget,GdkEventCrossing * event,gpointer data)379 load_graph_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
380 {
381     LoadGraph *graph;
382     graph = (LoadGraph *)data;
383 
384     graph->tooltip_update = FALSE;
385 
386     return TRUE;
387 }
388 
389 static void
load_graph_load_config(LoadGraph * g)390 load_graph_load_config (LoadGraph *g)
391 {
392     gchar *name, *temp;
393     guint i;
394 
395     if (!g->colors)
396         g->colors = g_new0(GdkRGBA, g->n);
397 
398     for (i = 0; i < g->n; i++)
399     {
400         name = g_strdup_printf ("%s-color%u", g->name, i);
401         temp = g_settings_get_string(g->multiload->settings, name);
402         if (!temp)
403             temp = g_strdup ("#000000");
404         gdk_rgba_parse(&(g->colors[i]), temp);
405         g_free(temp);
406         g_free(name);
407     }
408 }
409 
410 LoadGraph *
load_graph_new(MultiloadApplet * ma,guint n,const gchar * label,gint id,guint speed,guint size,gboolean visible,const gchar * name,LoadGraphDataFunc get_data)411 load_graph_new (MultiloadApplet *ma, guint n, const gchar *label,
412                 gint id, guint speed, guint size, gboolean visible,
413                 const gchar *name, LoadGraphDataFunc get_data)
414 {
415     LoadGraph *g;
416     MatePanelAppletOrient orient;
417 
418     g = g_new0 (LoadGraph, 1);
419     g->visible = visible;
420     g->name = name;
421     g->n = n;
422     g->id = id;
423     g->speed = speed;
424     g->size = size;
425     g->pixel_size = mate_panel_applet_get_size (ma->applet);
426     g->tooltip_update = FALSE;
427     g->multiload = ma;
428 
429     g->main_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
430 
431     g->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
432 
433     orient = mate_panel_applet_get_orient (g->multiload->applet);
434     switch (orient)
435     {
436     case MATE_PANEL_APPLET_ORIENT_UP:
437     case MATE_PANEL_APPLET_ORIENT_DOWN:
438     {
439         g->orient = FALSE;
440         break;
441     }
442     case MATE_PANEL_APPLET_ORIENT_LEFT:
443     case MATE_PANEL_APPLET_ORIENT_RIGHT:
444     {
445         g->orient = TRUE;
446         break;
447     }
448     default:
449         g_assert_not_reached ();
450     }
451 
452     g->frame = gtk_frame_new (NULL);
453     gtk_frame_set_shadow_type (GTK_FRAME (g->frame), GTK_SHADOW_IN);
454     gtk_container_add (GTK_CONTAINER (g->frame), g->box);
455     gtk_box_pack_start (GTK_BOX (g->main_widget), g->frame, TRUE, TRUE, 0);
456 
457     load_graph_load_config (g);
458 
459     g->get_data = get_data;
460 
461     g->timer_index = -1;
462 
463     if (g->orient)
464         gtk_widget_set_size_request (g->main_widget, -1, (gint) g->size);
465     else
466         gtk_widget_set_size_request (g->main_widget, (gint) g->size, -1);
467 
468     g->disp = gtk_drawing_area_new ();
469     gtk_widget_set_events (g->disp, GDK_EXPOSURE_MASK |
470                                     GDK_ENTER_NOTIFY_MASK |
471                                     GDK_LEAVE_NOTIFY_MASK |
472                                     GDK_BUTTON_PRESS_MASK);
473 
474     g_signal_connect (g->disp, "draw",
475                       G_CALLBACK (load_graph_expose), g);
476     g_signal_connect (g->disp, "configure_event",
477                       G_CALLBACK (load_graph_configure), g);
478     g_signal_connect (g->disp, "destroy",
479                       G_CALLBACK (load_graph_destroy), g);
480     g_signal_connect (g->disp, "button-press-event",
481                       G_CALLBACK (load_graph_clicked), g);
482     g_signal_connect (g->disp, "enter-notify-event",
483                       G_CALLBACK(load_graph_enter_cb), g);
484     g_signal_connect (g->disp, "leave-notify-event",
485                       G_CALLBACK(load_graph_leave_cb), g);
486 
487     gtk_box_pack_start (GTK_BOX (g->box), g->disp, TRUE, TRUE, 0);
488     gtk_widget_show_all(g->box);
489 
490     return g;
491 }
492 
493 void
load_graph_start(LoadGraph * g)494 load_graph_start (LoadGraph *g)
495 {
496     guint event_source_id;
497 
498     if (g->timer_index != -1)
499         g_source_remove ((guint) g->timer_index);
500 
501     event_source_id = g_timeout_add (g->speed,
502                                     (GSourceFunc) load_graph_update, g);
503 
504     g->timer_index = (gint) event_source_id;
505 }
506 
507 void
load_graph_stop(LoadGraph * g)508 load_graph_stop (LoadGraph *g)
509 {
510     if (g->timer_index != -1)
511         g_source_remove ((guint) g->timer_index);
512 
513     g->timer_index = -1;
514 }
515