implemented network load bar (third bar)
[cpumem-applet] / src / cpumem_status_area_item.c
1 /*
2  * Cpumem-applet - status area plugin
3  * 
4  * Copyright (c) 2005-2009 Jakub Pavelek
5  * Copyright (c) 2009-2010 Tuomo Tanskanen
6  */
7
8 #include <gtk/gtk.h>
9 #include <hildon/hildon.h>
10 #include <glib/gerror.h>
11 #include <glib.h>
12 #include <string.h>
13 #include <libosso.h>
14 typedef unsigned long long uint64_t;
15
16 #include "cpumem_status_area_item.h"
17
18 #define CPUMEM_ICON_WIDTH  16
19 #define CPUMEM_ICON_HEIGHT 16
20 #define CPUMEM_BOX_WIDTH   4
21 #define CPUMEM_BOX_HEIGHT  3
22 #define CPUMEM_CPU_MAX 5
23
24
25
26 #define CPUMEM_APPLET_STATUS_AREA_ITEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj, CPUMEM_APPLET_TYPE_STATUS_AREA_ITEM, CpumemAppletStatusAreaItemPrivate))
27
28 struct _CpumemAppletStatusAreaItemPrivate {
29         guint timeout_id;
30         gint lastU, lastN, lastIO, lastI;
31         guchar last_mem_level;
32         guchar last_cpu_level;
33         guchar last_net_level;
34         uint64_t last_in_oct;
35         uint64_t last_out_oct;
36         GdkPixbuf *pixbuf;
37         GdkPixbuf *pixbuf_on;
38         GdkPixbuf *pixbuf_red;
39         GdkPixbuf *pixbuf_off;
40         osso_context_t *osso;
41         gboolean red;
42 };
43
44 HD_DEFINE_PLUGIN_MODULE (CpumemAppletStatusAreaItem, cpumem_applet_status_area_item, HD_TYPE_STATUS_PLUGIN_ITEM);
45
46
47 /*
48  * Read current MEM usage and return indicator between 5 and 1 - how many bars are "full"
49  */
50 static guchar
51 check_mem (CpumemAppletStatusAreaItemPrivate *priv)
52 {
53         #define MEMFILE "/proc/meminfo"
54         #define MAX_READ_CHARS 128
55         char read_buffer[MAX_READ_CHARS];
56         FILE *fin;
57         int mem_used = 0;
58         int mem_total = 0;
59         int mem_cached = 0;
60         int mem_buffers = 0;
61         int mem_free = 0;
62
63         //Open the memory info file and get current free memory
64         fin = fopen(MEMFILE, "r");
65         if (fin == NULL) {
66                 g_warning("Can't open "MEMFILE"\n");
67                 return TRUE;
68         }
69         while (fgets(read_buffer, MAX_READ_CHARS, fin) != NULL) {
70                 if (strncmp(read_buffer, "MemTotal", 8) == 0) {
71                         sscanf(read_buffer + 10, "%d", &mem_total);
72                 } else if (strncmp(read_buffer, "MemFree", 6) == 0) {
73                         sscanf(read_buffer + 9, "%d", &mem_free);
74                 } else if (strncmp(read_buffer, "Buffers", 6) == 0) {
75                         sscanf(read_buffer + 9, "%d", &mem_buffers);
76                 } else if (strncmp(read_buffer, "Cached", 6) == 0) {
77                         sscanf(read_buffer + 8, "%d", &mem_cached);
78                         break;
79                 }
80         }
81         fclose(fin);
82
83         mem_used = mem_total - mem_free - mem_buffers - mem_cached;
84
85         if (mem_used > 0.9*mem_total)
86                 return 5;
87         else if (mem_used > 0.7*mem_total)
88                 return 4;
89         else if (mem_used > 0.5*mem_total)
90                 return 3;
91         else if (mem_used > 0.3*mem_total)
92                 return 2;
93         else
94                 return 1;
95 }
96
97
98 /*
99  * Read current CPU usage and return indicator between 5 and 1 - how many bars are "full"
100  */
101 static guchar
102 check_cpu (CpumemAppletStatusAreaItemPrivate *priv)
103 {
104         #define CPUFILE "/proc/stat"
105         gint curU, curN, curIO, curI;
106         gint deltaU, deltaN, deltaIO, deltaI;
107         int load, idle;
108         GError *error = NULL;
109         gchar *contents;
110         gsize lenght;
111         gchar **splits;
112
113         if (!g_file_get_contents (CPUFILE, &contents, &lenght, &error)) {
114                 fprintf (stderr, "ERR: can't read file %s: %s\n", CPUFILE, error->message);
115                 g_error_free (error);
116                 return 0;
117         }
118         
119         splits = g_strsplit_set (contents, " ",  -1);
120
121         sscanf(splits[2], "%d", &curU);
122         sscanf(splits[3], "%d", &curN);
123         sscanf(splits[4], "%d", &curIO);
124         sscanf(splits[5], "%d", &curI);
125         
126         g_strfreev (splits);
127         g_free (contents);
128     
129         idle = (curI - priv->lastI);
130         if (idle == 0) load = 100;
131         else load = 100-idle;
132         if (load>100) load = 0;
133         deltaU = curU - priv->lastU;
134         deltaN = curN - priv->lastN;
135         deltaIO = curIO - priv->lastIO;
136         deltaI = curI - priv->lastI;
137         priv->lastU = curU;
138         priv->lastN = curN;
139         priv->lastIO = curIO;
140         priv->lastI = curI;
141
142         if (load > 90)
143                 return 5;
144         else if (load > 70)
145                 return 4;
146         else if (load > 45)
147                 return 3;
148         else if (load > 19)
149                 return 2;
150         else
151                 return 1;
152 }
153
154 static guchar
155 check_net (CpumemAppletStatusAreaItemPrivate *priv)
156 {
157         #define NETFILE "/proc/net/dev"
158     uint64_t cur_in_oct, cur_out_oct;
159         GError *error;
160         gchar *contents;
161         char *pos;
162         char *token;
163     int next, cnt;
164     uint64_t inoct, outoct;
165     guchar l;
166
167         if (!g_file_get_contents (NETFILE, &contents, NULL, &error)) {
168                 fprintf (stderr, "ERR: can't read file %s: %s\n", NETFILE, error->message);
169                 g_error_free (error);
170                 return 0;
171         }
172         
173         cur_in_oct = cur_out_oct = 0;
174         next = 0;
175         token = strtok_r(contents, ":\n\r ", &pos);
176         while(token != NULL) {
177                 if(!strcmp(token, "gprs0") || !strcmp(token, "wlan0")) {
178                         cnt = 1;
179                         next = 1;
180                 } else {
181                         if(next == 1) {
182                                 if(--cnt == 0) {
183                                         cur_in_oct += strtoull(token, NULL, 10);
184                                         next = 2;
185                                         cnt = 8;
186                                 }
187                         } else if(next == 2) {
188                                 if(--cnt == 0) {
189                                         cur_out_oct += strtoull(token, NULL, 10);
190                                         next = 0;
191                                 }
192
193                         }
194                 }       
195                 token = strtok_r(NULL, ":\n\r ", &pos);
196         }
197         g_free (contents);
198     
199     inoct = cur_in_oct - priv->last_in_oct;
200     outoct = cur_out_oct - priv->last_out_oct;
201     if(inoct < outoct) {l=6; inoct = outoct;} else l=1;
202     if(inoct > 0) l++;
203     if(inoct > 1024) l++;
204     if(inoct > 1024*10) l++;
205     if(inoct > 1024*100) l++;
206     priv->last_in_oct = cur_in_oct;
207     priv->last_out_oct = cur_out_oct;
208     return l;
209 }
210
211
212 /*
213  * Compose and blit the current status of memory bars
214  */
215 static void
216 blit_mem_bars(const guchar level, CpumemAppletStatusAreaItemPrivate *priv)
217 {
218         guint x, y;
219         
220         gdk_pixbuf_fill(priv->pixbuf, 0x00000000);
221
222         x = 6;
223         y = 1;
224         if (level > 4)
225                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
226                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
227         y = 5;
228         if (level > 3)
229                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
230                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
231         y = 9;
232         if (level > 2)
233                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
234                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
235         y = 13;
236         if (level > 1)
237                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
238                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
239         else
240                 gdk_pixbuf_composite(priv->pixbuf_off, priv->pixbuf, x, y, 
241                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
242 }
243
244 static void
245 blit_net_bars(guchar level, CpumemAppletStatusAreaItemPrivate *priv)
246 {
247         guint x, y;
248         GdkPixbuf *color;
249         
250         x = 11;
251         y = 1;
252         if(level > 5) {color = priv->pixbuf_red; level -= 5;}
253         else color = priv->pixbuf_on;
254         if (level > 4)
255                 gdk_pixbuf_composite(color, priv->pixbuf, x, y, 
256                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
257         y = 5;
258         if (level > 3)
259                 gdk_pixbuf_composite(color, priv->pixbuf, x, y, 
260                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
261         y = 9;
262         if (level > 2)
263                 gdk_pixbuf_composite(color, priv->pixbuf, x, y, 
264                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
265         y = 13;
266         if (level > 1)
267                 gdk_pixbuf_composite(color, priv->pixbuf, x, y, 
268                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
269         else
270                 gdk_pixbuf_composite(priv->pixbuf_off, priv->pixbuf, x, y, 
271                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
272 }
273
274
275 /* 
276  * Compose and blit current status of CPU bars
277  */
278 static void
279 blit_cpu_bars (const guchar level, CpumemAppletStatusAreaItemPrivate *priv)
280 {
281         guint x, y;
282         
283         x = 1;
284         y = 1;
285         if (level > 4)
286         {
287                 if (priv->red == TRUE) {
288                         gdk_pixbuf_composite(priv->pixbuf_red, priv->pixbuf, x, y, 
289                                 CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
290                         priv->red = FALSE;
291                 } else {
292                         gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
293                                 CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
294                         priv->red = TRUE;
295                 }
296         }
297         y = 5;
298         if (level > 3)
299                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
300                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
301         y = 9;
302         if (level > 2)
303                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
304                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
305         y = 13;
306         if (level > 1)
307                 gdk_pixbuf_composite(priv->pixbuf_on, priv->pixbuf, x, y, 
308                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
309         else
310                 gdk_pixbuf_composite(priv->pixbuf_off, priv->pixbuf, x, y, 
311                         CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT, x, y, 1, 1, GDK_INTERP_NEAREST, 255);
312 }
313
314
315 /*
316  * Ran to check and update the CPU and memory reading
317  */
318 static gboolean
319 check_load (gpointer data)
320 {
321         guchar current_cpu_level;
322         guchar current_mem_level;
323         guchar current_net_level;
324         CpumemAppletStatusAreaItem *item = (CpumemAppletStatusAreaItem*)data;
325         CpumemAppletStatusAreaItemPrivate *priv = (CpumemAppletStatusAreaItemPrivate*)item->priv;
326    
327         current_cpu_level = check_cpu(priv); 
328         current_net_level = check_net(priv); 
329         current_mem_level = check_mem(priv);
330         //g_debug(g_strdup_printf("LOADAPLET - UPDATED CPU %d MEM %d", current_cpu_level, current_mem_level));
331         
332         //Update and blit only if data changed!
333         if ((current_mem_level != priv->last_mem_level) || (current_cpu_level != priv->last_cpu_level)
334                     || current_net_level != priv->last_net_level) {
335                 blit_mem_bars (current_mem_level, priv);
336                 blit_cpu_bars (current_cpu_level, priv);
337                 blit_net_bars (current_net_level, priv);
338                 if (current_cpu_level == CPUMEM_CPU_MAX)
339                         priv->red = FALSE;
340                 hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM(data), priv->pixbuf);
341                 priv->last_mem_level = current_mem_level;
342                 priv->last_cpu_level = current_cpu_level;
343                 priv->last_net_level = current_net_level;
344         } else if (current_cpu_level == CPUMEM_CPU_MAX) {
345                 //Pulsate max CPU load icon also when CPU load stays at max
346                 blit_cpu_bars (current_cpu_level, priv);
347                 hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM(data), priv->pixbuf);
348         }
349
350         return TRUE;  
351 }
352
353 /*
354  * Get callback when display state changes
355  */
356 static void 
357 cpumem_applet_status_area_item_display_cb(osso_display_state_t state, gpointer user_data)
358 {
359         CpumemAppletStatusAreaItem *item = CPUMEM_APPLET_STATUS_AREA_ITEM(user_data);
360
361         g_return_if_fail (item != NULL && item->priv != NULL);
362
363         if (state == OSSO_DISPLAY_ON)
364     {
365                 //Restart the updates, do one right away
366                 if (item->priv->timeout_id == 0) 
367                 {
368                         item->priv->timeout_id = gtk_timeout_add(1000, check_load, item);
369                         check_load(item);
370                 }
371     } else {
372                 //Suspend the updates - screen is off
373                 if (item->priv->timeout_id != 0) {
374                         g_source_remove(item->priv->timeout_id);
375                         item->priv->timeout_id = 0;
376                 }
377         }
378 }
379
380
381 /*****************************************************************************
382  *
383  * Boilerplate code area - do not enter
384  *
385  *****************************************************************************/
386
387 static void
388 cpumem_applet_status_area_item_set_area_icon (CpumemAppletStatusAreaItem *item)
389 {
390         item->priv = CPUMEM_APPLET_STATUS_AREA_ITEM_GET_PRIVATE (item);
391         
392         hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM(item), item->priv->pixbuf);
393 }
394
395
396 static void
397 cpumem_applet_status_area_item_class_finalize (CpumemAppletStatusAreaItemClass *klass)
398 {
399 }
400
401
402
403 static void
404 cpumem_applet_status_area_item_finalize (GObject *object)
405 {
406         CpumemAppletStatusAreaItemPrivate *priv = CPUMEM_APPLET_STATUS_AREA_ITEM(object)->priv;
407         // Release and clean our stuff
408         G_OBJECT_CLASS (cpumem_applet_status_area_item_parent_class)->finalize (object);
409         if (priv->osso)
410     {
411                 osso_deinitialize(priv->osso);
412                 priv->osso = NULL;
413     }
414
415 }
416
417
418
419 static void
420 cpumem_applet_status_area_item_class_init (CpumemAppletStatusAreaItemClass *klass)
421 {
422         GObjectClass *object_class = G_OBJECT_CLASS (klass);
423
424         object_class->finalize = cpumem_applet_status_area_item_finalize;
425
426         g_type_class_add_private (klass, sizeof (CpumemAppletStatusAreaItemPrivate));
427
428 }
429
430 static void
431 cpumem_applet_status_area_item_init (CpumemAppletStatusAreaItem *item)
432 {
433         item->priv = CPUMEM_APPLET_STATUS_AREA_ITEM_GET_PRIVATE (item);
434         
435         item->priv->last_mem_level = -1;
436         item->priv->last_cpu_level = -1;
437         item->priv->last_net_level = -1;
438         item->priv->timeout_id = -1;
439         item->priv->red = FALSE;
440         item->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, CPUMEM_ICON_WIDTH, CPUMEM_ICON_HEIGHT);
441         gdk_pixbuf_fill(item->priv->pixbuf, 0x00000000);
442         item->priv->pixbuf_on = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT);
443         gdk_pixbuf_fill(item->priv->pixbuf_on, 0xffffffff);
444         item->priv->pixbuf_red = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT);
445         gdk_pixbuf_fill(item->priv->pixbuf_red, 0xff0000ff);    
446         item->priv->pixbuf_off = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, CPUMEM_BOX_WIDTH, CPUMEM_BOX_HEIGHT);
447         gdk_pixbuf_fill(item->priv->pixbuf_off, 0x777777ff);
448         cpumem_applet_status_area_item_set_area_icon(item);
449
450         item->priv->osso = osso_initialize ("cpumem_applet_status_area_item", "Maemo5", TRUE, NULL);
451         item->priv->timeout_id = gtk_timeout_add(1000, check_load, item);
452         osso_hw_set_display_event_cb (item->priv->osso, cpumem_applet_status_area_item_display_cb, item);
453 }
454