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