1 #include <config.h>
2
3 #include <gdkmm/pixbuf.h>
4
5 #include <stdio.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <signal.h>
9 #include <dirent.h>
10 #include <string.h>
11 #include <time.h>
12
13 #include <glib/gi18n.h>
14
15 #include <glibtop.h>
16 #include <glibtop/cpu.h>
17 #include <glibtop/mem.h>
18 #include <glibtop/swap.h>
19 #include <glibtop/netload.h>
20 #include <glibtop/netlist.h>
21 #include <math.h>
22
23 #include <algorithm>
24
25 #include "procman.h"
26 #include "load-graph.h"
27 #include "util.h"
28 #include "gsm_color_button.h"
29
clear_background()30 void LoadGraph::clear_background()
31 {
32 if (background) {
33 cairo_pattern_destroy (background);
34 this->background = NULL;
35 }
36 }
37
38
num_bars() const39 unsigned LoadGraph::num_bars() const
40 {
41 unsigned n;
42
43 // keep 100 % num_bars == 0
44 switch (static_cast<int>(this->draw_height / (fontsize + 14)))
45 {
46 case 0:
47 case 1:
48 n = 1;
49 break;
50 case 2:
51 case 3:
52 n = 2;
53 break;
54 case 4:
55 n = 4;
56 break;
57 default:
58 n = 5;
59 }
60
61 return n;
62 }
63
64
65
66 #define FRAME_WIDTH 4
draw_background(LoadGraph * graph)67 static void draw_background(LoadGraph *graph) {
68 GtkAllocation allocation;
69 cairo_t *cr;
70 guint i;
71 unsigned num_bars;
72 g_autofree gchar *caption;
73 PangoLayout* layout;
74 PangoFontDescription* font_desc;
75 PangoRectangle extents;
76 cairo_surface_t *surface;
77 GdkRGBA fg, bg;
78
79 num_bars = graph->num_bars();
80 graph->graph_dely = (graph->draw_height - 15) / num_bars; /* round to int to avoid AA blur */
81 graph->real_draw_height = graph->graph_dely * num_bars;
82 graph->graph_delx = (graph->draw_width - 2.0 - graph->rmargin - graph->indent) / (LoadGraph::NUM_POINTS - 3);
83 graph->graph_buffer_offset = (int) (1.5 * graph->graph_delx) + FRAME_WIDTH ;
84
85 gtk_widget_get_allocation (graph->disp, &allocation);
86 surface = gdk_window_create_similar_surface (gtk_widget_get_window (graph->disp), CAIRO_CONTENT_COLOR_ALPHA, allocation.width, allocation.height);
87 cr = cairo_create (surface);
88
89 GtkStyleContext *context = gtk_widget_get_style_context (ProcData::get_instance()->notebook);
90 gtk_style_context_save (context);
91 gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
92 gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &bg);
93 gtk_style_context_get_color (context, gtk_style_context_get_state (context), &fg);
94 gtk_style_context_restore (context);
95
96 // set the background color
97 gdk_cairo_set_source_rgba (cr, &bg);
98 cairo_paint (cr);
99
100 layout = pango_cairo_create_layout (cr);
101 gtk_style_context_save (context);
102 gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
103 gtk_style_context_get (context,
104 gtk_style_context_get_state (context),
105 GTK_STYLE_PROPERTY_FONT,
106 &font_desc, NULL);
107 gtk_style_context_restore (context);
108 pango_font_description_set_size (font_desc, 0.8 * graph->fontsize * PANGO_SCALE);
109 pango_layout_set_font_description (layout, font_desc);
110 pango_font_description_free (font_desc);
111
112 /* draw frame */
113 cairo_translate (cr, FRAME_WIDTH, FRAME_WIDTH);
114
115 /* Draw background rectangle */
116 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
117 cairo_rectangle (cr, graph->rmargin + graph->indent, 0,
118 graph->draw_width - graph->rmargin - graph->indent, graph->real_draw_height);
119 cairo_fill(cr);
120
121 cairo_set_line_width (cr, 1.0);
122 cairo_set_source_rgba (cr, 0.89, 0.89, 0.89, 1.0);
123
124 bool network_in_bits = ProcData::get_instance()->config.network_in_bits;
125 for (i = 0; i <= num_bars; ++i) {
126 double y;
127
128 if (i == 0)
129 y = 0.5 + graph->fontsize / 2.0;
130 else if (i == num_bars)
131 y = i * graph->graph_dely + 0.5;
132 else
133 y = i * graph->graph_dely + graph->fontsize / 2.0;
134
135 gdk_cairo_set_source_rgba (cr, &fg);
136 if (graph->type == LOAD_GRAPH_NET) {
137 // operation orders matters so it's 0 if i == num_bars
138 guint64 rate = graph->net.max - (i * graph->net.max / num_bars);
139 caption = g_format_size_full (network_in_bits ? rate*8 : rate, network_in_bits ? G_FORMAT_SIZE_BITS : G_FORMAT_SIZE_DEFAULT);
140 pango_layout_set_text (layout, caption, -1);
141 pango_layout_get_extents (layout, NULL, &extents);
142 cairo_move_to (cr, graph->indent - 1.0 * extents.width / PANGO_SCALE + 20, y - 1.0 * extents.height / PANGO_SCALE / 2);
143 pango_cairo_show_layout (cr, layout);
144 } else {
145 // operation orders matters so it's 0 if i == num_bars
146 caption = g_strdup_printf("%d%%", 100 - i * (100 / num_bars));
147 pango_layout_set_text (layout, caption, -1);
148 pango_layout_get_extents (layout, NULL, &extents);
149 cairo_move_to (cr, graph->indent - 1.0 * extents.width / PANGO_SCALE + 20, y - 1.0 * extents.height / PANGO_SCALE / 2);
150 pango_cairo_show_layout (cr, layout);
151 }
152
153 if (i==0 || i==num_bars)
154 cairo_set_source_rgba (cr, 0.70, 0.71, 0.70, 1.0);
155 else
156 cairo_set_source_rgba (cr, 0.89, 0.89, 0.89, 1.0);
157 cairo_move_to (cr, graph->rmargin + graph->indent - 3, i * graph->graph_dely + 0.5);
158 cairo_line_to (cr, graph->draw_width - 0.5, i * graph->graph_dely + 0.5);
159 cairo_stroke (cr);
160 }
161
162 const unsigned total_seconds = graph->speed * (LoadGraph::NUM_POINTS - 2) / 1000;
163
164 for (unsigned int i = 0; i < 7; i++) {
165 double x = (i) * (graph->draw_width - graph->rmargin - graph->indent) / 6;
166 if (i==0 || i==6)
167 cairo_set_source_rgba (cr, 0.70, 0.71, 0.70, 1.0);
168 else
169 cairo_set_source_rgba (cr, 0.89, 0.89, 0.89, 1.0);
170 cairo_move_to (cr, (ceil(x) + 0.5) + graph->rmargin + graph->indent, 0.5);
171 cairo_line_to (cr, (ceil(x) + 0.5) + graph->rmargin + graph->indent, graph->real_draw_height + 4.5);
172 cairo_stroke(cr);
173 unsigned seconds = total_seconds - i * total_seconds / 6;
174 const char* format;
175 if (i == 0)
176 format = dngettext(GETTEXT_PACKAGE, "%u second", "%u seconds", seconds);
177 else
178 format = "%u";
179 caption = g_strdup_printf(format, seconds);
180 pango_layout_set_text (layout, caption, -1);
181 pango_layout_get_extents (layout, NULL, &extents);
182 cairo_move_to (cr, ((ceil(x) + 0.5) + graph->rmargin + graph->indent) - (1.0 * extents.width / PANGO_SCALE/2), graph->draw_height - 1.0 * extents.height / PANGO_SCALE);
183 gdk_cairo_set_source_rgba (cr, &fg);
184 pango_cairo_show_layout (cr, layout);
185 }
186 g_object_unref(layout);
187 cairo_stroke (cr);
188 cairo_destroy (cr);
189 graph->background = cairo_pattern_create_for_surface (surface);
190 cairo_surface_destroy (surface);
191 }
192
193 /* Redraws the backing buffer for the load graph and updates the window */
194 void
load_graph_queue_draw(LoadGraph * graph)195 load_graph_queue_draw (LoadGraph *graph)
196 {
197 /* repaint */
198 gtk_widget_queue_draw (graph->disp);
199 }
200
201 static int load_graph_update (gpointer user_data); // predeclare load_graph_update so we can compile ;)
202
203 static gboolean
load_graph_configure(GtkWidget * widget,GdkEventConfigure * event,gpointer data_ptr)204 load_graph_configure (GtkWidget *widget,
205 GdkEventConfigure *event,
206 gpointer data_ptr)
207 {
208 GtkAllocation allocation;
209 LoadGraph * const graph = static_cast<LoadGraph*>(data_ptr);
210
211 gtk_widget_get_allocation (widget, &allocation);
212 graph->draw_width = allocation.width - 2 * FRAME_WIDTH;
213 graph->draw_height = allocation.height - 2 * FRAME_WIDTH;
214
215 graph->clear_background();
216
217 load_graph_queue_draw (graph);
218
219 return TRUE;
220 }
221
load_graph_draw(GtkWidget * widget,cairo_t * context,gpointer data_ptr)222 static gboolean load_graph_draw (GtkWidget *widget, cairo_t *context, gpointer data_ptr)
223 {
224 LoadGraph * const graph = static_cast<LoadGraph*>(data_ptr);
225 GdkWindow *window;
226
227 guint i, j;
228 gdouble sample_width, x_offset;
229
230 window = gtk_widget_get_window (graph->disp);
231
232 /* Number of pixels wide for one graph point */
233 sample_width = (float)(graph->draw_width - graph->rmargin - graph->indent) / (float)LoadGraph::NUM_POINTS;
234 /* General offset */
235 x_offset = graph->draw_width - graph->rmargin + (sample_width*2);
236
237 /* Subframe offset */
238 x_offset += graph->rmargin - ((sample_width / graph->frames_per_unit) * graph->render_counter);
239
240 /* draw the graph */
241 cairo_t* cr;
242
243 cr = gdk_cairo_create (window);
244
245 if (graph->background == NULL) {
246 draw_background(graph);
247 }
248 cairo_set_source (cr, graph->background);
249 cairo_paint (cr);
250
251 cairo_set_line_width (cr, 1);
252 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
253 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
254 cairo_rectangle (cr, graph->rmargin + graph->indent + FRAME_WIDTH + 1, FRAME_WIDTH - 1,
255 graph->draw_width - graph->rmargin - graph->indent - 1, graph->real_draw_height + FRAME_WIDTH - 1);
256 cairo_clip(cr);
257
258 for (j = 0; j < graph->n; ++j) {
259 cairo_move_to (cr, x_offset, (1.0f - graph->data[0][j]) * graph->real_draw_height);
260 gdk_cairo_set_source_rgba (cr, &(graph->colors [j]));
261
262 for (i = 1; i < LoadGraph::NUM_POINTS; ++i) {
263 if (graph->data[i][j] == -1.0f)
264 continue;
265 cairo_curve_to (cr,
266 x_offset - ((i - 0.5f) * graph->graph_delx),
267 (1.0f - graph->data[i-1][j]) * graph->real_draw_height + 3.5f,
268 x_offset - ((i - 0.5f) * graph->graph_delx),
269 (1.0f - graph->data[i][j]) * graph->real_draw_height + 3.5f,
270 x_offset - (i * graph->graph_delx),
271 (1.0f - graph->data[i][j]) * graph->real_draw_height + 3.5f);
272 }
273 cairo_stroke (cr);
274
275 }
276
277 cairo_destroy (cr);
278
279 return TRUE;
280 }
281
282 static void
get_load(LoadGraph * graph)283 get_load (LoadGraph *graph)
284 {
285 guint i;
286 glibtop_cpu cpu;
287
288 glibtop_get_cpu (&cpu);
289
290 #undef NOW
291 #undef LAST
292 #define NOW (graph->cpu.times[graph->cpu.now])
293 #define LAST (graph->cpu.times[graph->cpu.now ^ 1])
294
295 if (graph->n == 1) {
296 NOW[0][CPU_TOTAL] = cpu.total;
297 NOW[0][CPU_USED] = cpu.user + cpu.nice + cpu.sys;
298 } else {
299 for (i = 0; i < graph->n; i++) {
300 NOW[i][CPU_TOTAL] = cpu.xcpu_total[i];
301 NOW[i][CPU_USED] = cpu.xcpu_user[i] + cpu.xcpu_nice[i]
302 + cpu.xcpu_sys[i];
303 }
304 }
305
306 // on the first call, LAST is 0
307 // which means data is set to the average load since boot
308 // that value has no meaning, we just want all the
309 // graphs to be aligned, so the CPU graph needs to start
310 // immediately
311
312 for (i = 0; i < graph->n; i++) {
313 float load;
314 float total, used;
315 gchar *text;
316
317 total = NOW[i][CPU_TOTAL] - LAST[i][CPU_TOTAL];
318 used = NOW[i][CPU_USED] - LAST[i][CPU_USED];
319
320 load = used / MAX(total, 1.0f);
321 graph->data[0][i] = load;
322
323 /* Update label */
324 text = g_strdup_printf("%.1f%%", load * 100.0f);
325 gtk_label_set_text(GTK_LABEL(graph->labels.cpu[i]), text);
326 g_free(text);
327 }
328
329 graph->cpu.now ^= 1;
330
331 #undef NOW
332 #undef LAST
333 }
334
335
336 namespace
337 {
338
set_memory_label_and_picker(GtkLabel * label,GSMColorButton * picker,guint64 used,guint64 total,double percent)339 void set_memory_label_and_picker(GtkLabel* label, GSMColorButton* picker,
340 guint64 used, guint64 total, double percent)
341 {
342 char* used_text;
343 char* total_text;
344 char* text;
345
346 used_text = g_format_size_full(used, G_FORMAT_SIZE_IEC_UNITS);
347 total_text = g_format_size_full(total, G_FORMAT_SIZE_IEC_UNITS);
348 if (total == 0) {
349 text = g_strdup(_("not available"));
350 } else {
351 // xgettext: 540MiB (53 %) of 1.0 GiB
352 text = g_strdup_printf(_("%s (%.1f%%) of %s"), used_text, 100.0 * percent, total_text);
353 }
354 gtk_label_set_text(label, text);
355 g_free(used_text);
356 g_free(total_text);
357 g_free(text);
358
359 if (picker)
360 gsm_color_button_set_fraction(picker, percent);
361 }
362 }
363
364 static void
get_memory(LoadGraph * graph)365 get_memory (LoadGraph *graph)
366 {
367 float mempercent, swappercent;
368
369 glibtop_mem mem;
370 glibtop_swap swap;
371
372 glibtop_get_mem (&mem);
373 glibtop_get_swap (&swap);
374
375 /* There's no swap on LiveCD : 0.0f is better than NaN :) */
376 swappercent = (swap.total ? (float)swap.used / (float)swap.total : 0.0f);
377 mempercent = (float)mem.user / (float)mem.total;
378
379 set_memory_label_and_picker(GTK_LABEL(graph->labels.memory),
380 GSM_COLOR_BUTTON(graph->mem_color_picker),
381 mem.user, mem.total, mempercent);
382
383 set_memory_label_and_picker(GTK_LABEL(graph->labels.swap),
384 GSM_COLOR_BUTTON(graph->swap_color_picker),
385 swap.used, swap.total, swappercent);
386
387 graph->data[0][0] = mempercent;
388 graph->data[0][1] = swappercent;
389 }
390
391 /* Nice Numbers for Graph Labels after Paul Heckbert
392 nicenum: find a "nice" number approximately equal to x.
393 Round the number if round=1, take ceiling if round=0 */
394
395 static double
nicenum(double x,int round)396 nicenum (double x, int round)
397 {
398 int expv; /* exponent of x */
399 double f; /* fractional part of x */
400 double nf; /* nice, rounded fraction */
401
402 expv = floor( log10(x) );
403 f = x/pow( 10.0, expv ); /* between 1 and 10 */
404 if (round) {
405 if ( f < 1.5 ) nf = 1.0;
406 else if ( f < 3.0 ) nf = 2.0;
407 else if ( f < 7.0 ) nf = 5.0;
408 else nf = 10.0;
409 } else {
410 if ( f <= 1.0 ) nf = 1.0;
411 else if ( f <= 2.0 ) nf = 2.0;
412 else if ( f <= 5.0 ) nf = 5.0;
413 else nf = 10.0;
414 }
415 return nf * pow(10.0, expv);
416 }
417
418 static void
net_scale(LoadGraph * graph,guint64 din,guint64 dout)419 net_scale (LoadGraph *graph, guint64 din, guint64 dout)
420 {
421 graph->data[0][0] = 1.0f * din / graph->net.max;
422 graph->data[0][1] = 1.0f * dout / graph->net.max;
423
424 guint64 dmax = std::max(din, dout);
425 graph->net.values[graph->net.cur] = dmax;
426 graph->net.cur = (graph->net.cur + 1) % LoadGraph::NUM_POINTS;
427
428 guint64 new_max;
429 // both way, new_max is the greatest value
430 if (dmax >= graph->net.max)
431 new_max = dmax;
432 else
433 new_max = *std::max_element(&graph->net.values[0],
434 &graph->net.values[LoadGraph::NUM_POINTS]);
435
436 //
437 // Round network maximum
438 //
439
440 const guint64 bak_max(new_max);
441
442 if (ProcData::get_instance()->config.network_in_bits) {
443 // nice number is for the ticks
444 unsigned ticks = graph->num_bars();
445
446 // gets messy at low values due to division by 8
447 guint64 bit_max = std::max( new_max*8, G_GUINT64_CONSTANT(10000) );
448
449 // our tick size leads to max
450 double d = nicenum(bit_max/ticks, 0);
451 bit_max = ticks * d;
452 new_max = bit_max / 8;
453
454 procman_debug("bak*8 %" G_GUINT64_FORMAT ", ticks %d, d %f"
455 ", bit_max %" G_GUINT64_FORMAT ", new_max %" G_GUINT64_FORMAT,
456 bak_max*8, ticks, d, bit_max, new_max );
457 } else {
458 // round up to get some extra space
459 // yes, it can overflow
460 new_max = 1.1 * new_max;
461 // make sure max is not 0 to avoid / 0
462 // default to 1 KiB
463 new_max = std::max(new_max, G_GUINT64_CONSTANT(1024));
464
465 // decompose new_max = coef10 * 2**(base10 * 10)
466 // where coef10 and base10 are integers and coef10 < 2**10
467 //
468 // e.g: ceil(100.5 KiB) = 101 KiB = 101 * 2**(1 * 10)
469 // where base10 = 1, coef10 = 101, pow2 = 16
470
471 guint64 pow2 = std::floor(log(new_max) / log(2));
472 guint64 base10 = pow2 / 10.0;
473 guint64 coef10 = std::ceil(new_max / double(G_GUINT64_CONSTANT(1) << (base10 * 10)));
474 g_assert(new_max <= (coef10 * (G_GUINT64_CONSTANT(1) << (base10 * 10))));
475
476 // then decompose coef10 = x * 10**factor10
477 // where factor10 is integer and x < 10
478 // so we new_max has only 1 significant digit
479
480 guint64 factor10 = std::pow(10.0, std::floor(std::log10(coef10)));
481 coef10 = std::ceil(coef10 / double(factor10)) * factor10;
482
483 // then make coef10 divisible by num_bars
484 if (coef10 % graph->num_bars() != 0)
485 coef10 = coef10 + (graph->num_bars() - coef10 % graph->num_bars());
486 g_assert(coef10 % graph->num_bars() == 0);
487 new_max = coef10 * (G_GUINT64_CONSTANT(1) << guint64(base10 * 10));
488 procman_debug("bak %" G_GUINT64_FORMAT " new_max %" G_GUINT64_FORMAT
489 "pow2 %" G_GUINT64_FORMAT " coef10 %" G_GUINT64_FORMAT,
490 bak_max, new_max, pow2, coef10);
491 }
492
493 if (bak_max > new_max) {
494 procman_debug("overflow detected: bak=%" G_GUINT64_FORMAT
495 " new=%" G_GUINT64_FORMAT,
496 bak_max, new_max);
497 new_max = bak_max;
498 }
499
500 // if max is the same or has decreased but not so much, don't
501 // do anything to avoid rescaling
502 if ((0.8 * graph->net.max) < new_max && new_max <= graph->net.max)
503 return;
504
505 const double scale = 1.0f * graph->net.max / new_max;
506
507 for (size_t i = 0; i < LoadGraph::NUM_POINTS; i++) {
508 if (graph->data[i][0] >= 0.0f) {
509 graph->data[i][0] *= scale;
510 graph->data[i][1] *= scale;
511 }
512 }
513
514 procman_debug("rescale dmax = %" G_GUINT64_FORMAT
515 " max = %" G_GUINT64_FORMAT
516 " new_max = %" G_GUINT64_FORMAT,
517 dmax, graph->net.max, new_max);
518
519 graph->net.max = new_max;
520
521 // force the graph background to be redrawn now that scale has changed
522 graph->clear_background();
523 }
524
525 static void
get_net(LoadGraph * graph)526 get_net (LoadGraph *graph)
527 {
528 glibtop_netlist netlist;
529 char **ifnames;
530 guint32 i;
531 guint64 in = 0, out = 0;
532 #if GLIB_CHECK_VERSION(2,61,2)
533 gint64 time;
534 #else
535 GTimeVal time;
536 #endif
537 guint64 din, dout;
538
539 ifnames = glibtop_get_netlist(&netlist);
540
541 for (i = 0; i < netlist.number; ++i)
542 {
543 glibtop_netload netload;
544 glibtop_get_netload (&netload, ifnames[i]);
545
546 if (netload.if_flags & (1 << GLIBTOP_IF_FLAGS_LOOPBACK))
547 continue;
548
549 /* Skip interfaces without any IPv4/IPv6 address (or
550 those with only a LINK ipv6 addr) However we need to
551 be able to exclude these while still keeping the
552 value so when they get online (with NetworkManager
553 for example) we don't get a suddent peak. Once we're
554 able to get this, ignoring down interfaces will be
555 possible too. */
556 if (not (netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS6)
557 and netload.scope6 != GLIBTOP_IF_IN6_SCOPE_LINK)
558 and not (netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS)))
559 continue;
560
561 /* Don't skip interfaces that are down (GLIBTOP_IF_FLAGS_UP)
562 to avoid spikes when they are brought up */
563
564 in += netload.bytes_in;
565 out += netload.bytes_out;
566 }
567
568 g_strfreev(ifnames);
569
570 #if GLIB_CHECK_VERSION(2,61,2)
571 time = g_get_real_time ();
572
573 if (in >= graph->net.last_in && out >= graph->net.last_out &&
574 graph->net.time != 0) {
575 float dtime = (double) (time - graph->net.time) / G_USEC_PER_SEC;
576 #else
577 g_get_current_time (&time);
578 if (in >= graph->net.last_in && out >= graph->net.last_out &&
579 graph->net.time.tv_sec != 0) {
580 float dtime;
581 dtime = time.tv_sec - graph->net.time.tv_sec +
582 (double) (time.tv_usec - graph->net.time.tv_usec) / G_USEC_PER_SEC;
583 #endif
584 din = static_cast<guint64>((in - graph->net.last_in) / dtime);
585 dout = static_cast<guint64>((out - graph->net.last_out) / dtime);
586 } else {
587 /* Don't calc anything if new data is less than old (interface
588 removed, counters reset, ...) or if it is the first time */
589 din = 0;
590 dout = 0;
591 }
592
593 graph->net.last_in = in;
594 graph->net.last_out = out;
595 graph->net.time = time;
596
597 net_scale(graph, din, dout);
598
599 bool network_in_bits = ProcData::get_instance()->config.network_in_bits;
600 g_autofree gchar *str=NULL, *formatted_str=NULL;
601
602 str = g_format_size_full (network_in_bits ? din*8 : din, network_in_bits ? G_FORMAT_SIZE_BITS : G_FORMAT_SIZE_DEFAULT);
603 formatted_str = g_strdup_printf(_("%s/s"), str);
604 gtk_label_set_text (GTK_LABEL (graph->labels.net_in), formatted_str);
605
606 str = g_format_size_full (network_in_bits ? in*8 : in, network_in_bits ? G_FORMAT_SIZE_BITS : G_FORMAT_SIZE_DEFAULT);
607 gtk_label_set_text (GTK_LABEL (graph->labels.net_in_total), str);
608
609 str = g_format_size_full (network_in_bits ? dout*8 : dout, network_in_bits ? G_FORMAT_SIZE_BITS : G_FORMAT_SIZE_DEFAULT);
610 formatted_str = g_strdup_printf(_("%s/s"), str);
611 gtk_label_set_text (GTK_LABEL (graph->labels.net_out), formatted_str);
612
613 str = g_format_size_full (network_in_bits ? out*8 : out, network_in_bits ? G_FORMAT_SIZE_BITS : G_FORMAT_SIZE_DEFAULT);
614 gtk_label_set_text (GTK_LABEL (graph->labels.net_out_total), str);
615 }
616
617
618 /* Updates the load graph when the timeout expires */
619 static gboolean
620 load_graph_update (gpointer user_data)
621 {
622 LoadGraph * const graph = static_cast<LoadGraph*>(user_data);
623
624 if (graph->render_counter == graph->frames_per_unit - 1) {
625 std::rotate(&graph->data[0], &graph->data[LoadGraph::NUM_POINTS - 1], &graph->data[LoadGraph::NUM_POINTS]);
626
627 switch (graph->type) {
628 case LOAD_GRAPH_CPU:
629 get_load(graph);
630 break;
631 case LOAD_GRAPH_MEM:
632 get_memory(graph);
633 break;
634 case LOAD_GRAPH_NET:
635 get_net(graph);
636 break;
637 default:
638 g_assert_not_reached();
639 }
640 }
641
642 if (graph->draw)
643 load_graph_queue_draw (graph);
644
645 graph->render_counter++;
646
647 if (graph->render_counter >= graph->frames_per_unit)
648 graph->render_counter = 0;
649
650 return TRUE;
651 }
652
653
654
655 LoadGraph::~LoadGraph()
656 {
657 load_graph_stop(this);
658
659 if (timer_index)
660 g_source_remove(timer_index);
661
662 clear_background();
663 }
664
665
666
667 static gboolean
668 load_graph_destroy (GtkWidget *widget, gpointer data_ptr)
669 {
670 LoadGraph * const graph = static_cast<LoadGraph*>(data_ptr);
671
672 delete graph;
673
674 return FALSE;
675 }
676
677
678 LoadGraph::LoadGraph(guint type)
679 : fontsize(8.0),
680 rmargin(3.5 * fontsize),
681 indent(24.0),
682 n(0),
683 type(type),
684 speed(0),
685 draw_width(0),
686 draw_height(0),
687 render_counter(0),
688 frames_per_unit(10), // this will be changed but needs initialising
689 graph_dely(0),
690 real_draw_height(0),
691 graph_delx(0.0),
692 graph_buffer_offset(0),
693 main_widget(NULL),
694 disp(NULL),
695 background(NULL),
696 timer_index(0),
697 draw(FALSE),
698 mem_color_picker(NULL),
699 swap_color_picker(NULL)
700 {
701 LoadGraph * const graph = this;
702
703 // FIXME:
704 // on configure, graph->frames_per_unit = graph->draw_width/(LoadGraph::NUM_POINTS);
705 // knock FRAMES down to 5 until cairo gets faster
706
707 switch (type) {
708 case LOAD_GRAPH_CPU:
709 memset(&cpu, 0, sizeof cpu);
710 n = ProcData::get_instance()->config.num_cpus;
711
712 for(guint i = 0; i < G_N_ELEMENTS(labels.cpu); ++i)
713 labels.cpu[i] = gtk_label_new(NULL);
714
715 break;
716
717 case LOAD_GRAPH_MEM:
718 n = 2;
719 labels.memory = gtk_label_new(NULL);
720 labels.swap = gtk_label_new(NULL);
721 break;
722
723 case LOAD_GRAPH_NET:
724 memset(&net, 0, sizeof net);
725 n = 2;
726 net.max = 1;
727 labels.net_in = gtk_label_new(NULL);
728 labels.net_in_total = gtk_label_new(NULL);
729 labels.net_out = gtk_label_new(NULL);
730 labels.net_out_total = gtk_label_new(NULL);
731 break;
732 }
733
734 speed = ProcData::get_instance()->config.graph_update_interval;
735
736 colors.resize(n);
737
738 switch (type) {
739 case LOAD_GRAPH_CPU:
740 memcpy(&colors[0], ProcData::get_instance()->config.cpu_color,
741 n * sizeof colors[0]);
742 break;
743 case LOAD_GRAPH_MEM:
744 colors[0] = ProcData::get_instance()->config.mem_color;
745 colors[1] = ProcData::get_instance()->config.swap_color;
746 mem_color_picker = gsm_color_button_new (&colors[0],
747 GSMCP_TYPE_PIE);
748 swap_color_picker = gsm_color_button_new (&colors[1],
749 GSMCP_TYPE_PIE);
750 break;
751 case LOAD_GRAPH_NET:
752 colors[0] = ProcData::get_instance()->config.net_in_color;
753 colors[1] = ProcData::get_instance()->config.net_out_color;
754 break;
755 }
756
757 timer_index = 0;
758 render_counter = (frames_per_unit - 1);
759 draw = FALSE;
760
761 main_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
762 gtk_widget_set_size_request(main_widget, -1, LoadGraph::GRAPH_MIN_HEIGHT);
763 gtk_widget_show (main_widget);
764
765 disp = gtk_drawing_area_new ();
766 gtk_widget_show (disp);
767 g_signal_connect (G_OBJECT (disp), "draw", G_CALLBACK (load_graph_draw), graph);
768 g_signal_connect (G_OBJECT(disp), "configure_event",
769 G_CALLBACK (load_graph_configure), graph);
770 g_signal_connect (G_OBJECT(disp), "destroy",
771 G_CALLBACK (load_graph_destroy), graph);
772
773 gtk_widget_set_events (disp, GDK_EXPOSURE_MASK);
774
775 gtk_box_pack_start (GTK_BOX (main_widget), disp, TRUE, TRUE, 0);
776
777
778 /* Allocate data in a contiguous block */
779 data_block = std::vector<float>(n * LoadGraph::NUM_POINTS, -1.0f);
780
781 for (guint i = 0; i < LoadGraph::NUM_POINTS; ++i)
782 data[i] = &data_block[0] + i * n;
783
784 gtk_widget_show_all (main_widget);
785 }
786
787 void
788 load_graph_start (LoadGraph *graph)
789 {
790 if(!graph->timer_index) {
791
792 load_graph_update(graph);
793
794 graph->timer_index = g_timeout_add (graph->speed / graph->frames_per_unit,
795 load_graph_update,
796 graph);
797 }
798
799 graph->draw = TRUE;
800 }
801
802 void
803 load_graph_stop (LoadGraph *graph)
804 {
805 /* don't draw anymore, but continue to poll */
806 graph->draw = FALSE;
807 }
808
809 void
810 load_graph_change_speed (LoadGraph *graph,
811 guint new_speed)
812 {
813 if (graph->speed == new_speed)
814 return;
815
816 graph->speed = new_speed;
817
818 if(graph->timer_index) {
819 g_source_remove (graph->timer_index);
820 graph->timer_index = g_timeout_add (graph->speed / graph->frames_per_unit,
821 load_graph_update,
822 graph);
823 }
824
825 graph->clear_background();
826 }
827
828
829 LoadGraphLabels*
830 load_graph_get_labels (LoadGraph *graph)
831 {
832 return &graph->labels;
833 }
834
835 GtkWidget*
836 load_graph_get_widget (LoadGraph *graph)
837 {
838 return graph->main_widget;
839 }
840
841 GtkWidget*
842 load_graph_get_mem_color_picker(LoadGraph *graph)
843 {
844 return graph->mem_color_picker;
845 }
846
847 GtkWidget*
848 load_graph_get_swap_color_picker(LoadGraph *graph)
849 {
850 return graph->swap_color_picker;
851 }
852