1 /* Hot-babe
2  * Copyright (C) 2002 DindinX <David@dindinx.org>
3  * Copyright (C) 2002 Bruno Bellamy.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the artistic License
7  *
8  * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
9  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
10  * OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License for more details.
12  *
13  * this code is using some ideas from wmbubble (timecop@japan.co.jp)
14  *
15  */
16 
17 /* general includes */
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #if defined(__FreeBSD__) || defined(__DragonFly__)
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/types.h>
26 #include <sys/sysctl.h>
27 #ifndef CPUSTATES
28 #include <sys/dkstat.h>
29 #endif
30 #endif
31 
32 /* x11 includes */
33 #include <gdk/gdk.h>
34 #include <gdk/gdkx.h>
35 #include <gdk-pixbuf/gdk-pixbuf.h>
36 
37 #include "loader.h"
38 
39 static int system_cpu(void);
40 static void hotbabe_setup_samples(void);
41 static void hotbabe_update(void);
42 static void create_hotbabe_window(void);
43 static void print_usage(void);
44 
45 /* global variables */
46 
47 typedef struct
48 {
49   /* X11 stuff */
50   GdkWindow *win;        /* main window */
51   HotBabeAnim anim;
52   gint x,y;
53   guchar **pixels;
54   guchar *dest;
55 
56   int samples;
57 
58   /* CPU percentage stuff.  soon to go away */
59   int loadIndex;
60   u_int64_t *load, *total;
61   guint threshold;
62 
63   /* optional stuff */
64   gboolean incremental;
65   gboolean noNice;
66   guint    delay;
67 } HotBabeData;
68 
69 HotBabeData bm;
70 
71 #if 0
72 /* FIXME New BSD and Solaris code.. to check.
73  * doesn't work with Linux (getloadavg return 1.000) */
74 static int system_cpu(void)
75 {
76   int rc;
77   double loadavg[15];
78   rc=getloadavg(loadavg, 1);
79   while( rc-- )
80     printf( "load = %f\n", loadavg[rc] );
81   rc=100*loadavg[0];
82   return rc;
83 }
84 #endif
85 
86 /* returns current CPU load in percent, 0 to 256 */
system_cpu(void)87 static int system_cpu(void)
88 {
89   unsigned int  cpuload;
90   int           i;
91 #ifdef __linux__
92   u_int64_t     load, total, oload, ototal;
93   u_int64_t     ab, ac, ad, ae;
94   FILE         *stat;
95 #endif
96 #if defined(__FreeBSD__) || defined(__DragonFly__)
97   long load, total, oload, ototal;
98   long ab, ac, ad, ae;
99   long cp_time[CPUSTATES];
100   size_t len = sizeof(cp_time);
101 #endif
102 
103 #ifdef __linux__
104   stat = fopen("/proc/stat", "r");
105   fscanf(stat, "%*s %Ld %Ld %Ld %Ld", &ab, &ac, &ad, &ae);
106   fclose(stat);
107 #endif
108 #if defined(__FreeBSD__) || defined(__DragonFly__)
109   if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) < 0)
110     (void)fprintf(stderr, "Cannot get kern.cp_time");
111 
112   ab = cp_time[CP_USER];
113   ac = cp_time[CP_NICE];
114   ad = cp_time[CP_SYS];
115   ae = cp_time[CP_IDLE];
116 #endif
117 
118 
119   /* Find out the CPU load */
120   /* user + sys = load
121    * total = total */
122   load = ab + ad;  /* cpu.user + cpu.sys; */
123   if(!bm.noNice) load += ac;
124   total = ab + ac + ad + ae;  /* cpu.total; */
125 
126   i = bm.loadIndex;
127   oload = bm.load[i];
128   ototal = bm.total[i];
129 
130   bm.load[i] = load;
131   bm.total[i] = total;
132   bm.loadIndex = (i + 1) % bm.samples;
133 
134   /*
135    *   Because the load returned from libgtop is a value accumulated
136    *   over time, and not the current load, the current load percentage
137    *   is calculated as the extra amount of work that has been performed
138    *   since the last sample. yah, right, what the fuck does that mean?
139    */
140   if (ototal == 0 || total==ototal)    /* ototal == 0 means that this is the first time we get here */
141     cpuload = 0;
142   else
143     cpuload = (256 * (load - oload)) / (total - ototal);
144 
145   return cpuload;
146 }
147 
148 GdkPixmap     *pixmap;
149 GdkGC *gc;
150 
151 /* This is the function that actually creates the display widgets */
create_hotbabe_window(void)152 static void create_hotbabe_window(void)
153 {
154 #define MASK GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
155   GdkWindowAttr  attr;
156   GdkBitmap     *mask;
157   GdkScreen *defscrn;
158 
159   bm.anim.width = gdk_pixbuf_get_width(bm.anim.pixbuf[0]);
160   bm.anim.height = gdk_pixbuf_get_height(bm.anim.pixbuf[0]);
161   defscrn=gdk_screen_get_default();
162 
163   attr.width = bm.anim.width;
164   attr.height = bm.anim.height;
165   if( bm.x < 0 ) bm.x += 1 + gdk_screen_get_width(defscrn) - attr.width;
166   if( bm.y < 0 ) bm.y += 1 + gdk_screen_get_height(defscrn) - attr.height;
167   attr.x = bm.x;
168   attr.y = bm.y;
169   attr.title = "hot-babe";
170   attr.event_mask = MASK;
171   attr.wclass = GDK_INPUT_OUTPUT;
172   attr.visual = gdk_visual_get_system();
173   attr.colormap = gdk_colormap_get_system();
174   attr.wmclass_name = "hot-babe";
175   attr.wmclass_class = "hot-babe";
176   attr.window_type = GDK_WINDOW_TOPLEVEL;
177 
178   bm.win = gdk_window_new(NULL, &attr,
179       GDK_WA_TITLE | GDK_WA_WMCLASS |
180       GDK_WA_VISUAL | GDK_WA_COLORMAP |
181       GDK_WA_X | GDK_WA_Y);
182   if (!bm.win)
183   {
184     fprintf(stderr, "Cannot make toplevel window\n");
185     exit (-1);
186   }
187   gdk_window_set_decorations(bm.win, 0);
188   gdk_window_set_skip_taskbar_hint(bm.win, TRUE);
189   gdk_window_set_skip_pager_hint(bm.win, TRUE);
190   gdk_window_set_type_hint(bm.win, GDK_WINDOW_TYPE_HINT_DOCK);
191 //  gdk_window_set_keep_below(bm.win, TRUE);
192 
193   gdk_pixbuf_render_pixmap_and_mask( bm.anim.pixbuf[bm.anim.samples-1], &pixmap, &mask, 127 );
194 
195   gdk_window_shape_combine_mask(bm.win, mask, 0, 0);
196   gdk_pixbuf_render_pixmap_and_mask( bm.anim.pixbuf[0], &pixmap, &mask, 127 );
197   gdk_window_set_back_pixmap(bm.win, pixmap, False);
198 
199   gdk_window_show(bm.win);
200 
201   gc = gdk_gc_new (pixmap);
202 #undef MASK
203 }
204 
hotbabe_update(void)205 static void hotbabe_update(void)
206 {
207   guint   loadPercentage;
208   static guint   old_percentage = 0;
209   guint   i;
210   guchar *pixels1, *pixels2, *src1, *src2, *dest;
211   static gint    robinet = 0;
212 
213   /* Find out the CPU load */
214   loadPercentage = system_cpu();
215 
216   if (bm.threshold)
217   {
218     if (loadPercentage < bm.threshold || bm.threshold>255)
219       loadPercentage = 0;
220     else
221       loadPercentage = (loadPercentage-bm.threshold)*256/(256-bm.threshold);
222   }
223 
224   robinet +=loadPercentage/50-3;
225 
226   robinet = CLAMP(robinet, 0, 256);
227 
228   if (bm.incremental)
229     loadPercentage = robinet;
230 
231   if (loadPercentage != old_percentage)
232   {
233     gint range = 256  / (bm.anim.samples-1);
234     gint index = loadPercentage/range;
235 
236     old_percentage = loadPercentage;
237     if  (index>bm.anim.samples-1) index = bm.anim.samples-1;
238     pixels1 = bm.pixels[index];
239     if (index  == bm.anim.samples-1) pixels2 = bm.pixels[index];
240     else pixels2 = bm.pixels[index+1];
241 
242     loadPercentage = loadPercentage % range;
243     dest = bm.dest; src1 = pixels1; src2 = pixels2;
244     for (i=0  ;  i<bm.anim.height*bm.anim.width ;  i++)
245     {
246       guint val, j;
247 
248       if (src1[3])
249       {
250         for (j=0;j<3;j++)
251         {
252           val = ((guint)*(src2++))*loadPercentage+((guint)*(src1++))*(range-loadPercentage);
253           *(dest++) = val/range;
254           //*(dest++) = (val >> 6);  // bad hack!
255         }
256         src1++;
257         src2++;
258       } else
259       {
260         src1+=4;src2+=4;dest+=3;
261       }
262     }
263 
264     gdk_draw_rgb_image(pixmap, gc, 0, 0, bm.anim.width, bm.anim.height, GDK_RGB_DITHER_NONE,
265         bm.dest, 3 * bm.anim.width);
266     gdk_window_set_back_pixmap(bm.win, pixmap, False);
267     gdk_window_clear(bm.win);
268   }
269 }
270 
hotbabe_setup_samples(void)271 static void hotbabe_setup_samples(void)
272 {
273   int       i;
274   u_int64_t load = 0, total = 0;
275 
276   bm.loadIndex = 0;
277   bm.load = malloc(bm.samples * sizeof(u_int64_t));
278   bm.total = malloc(bm.samples * sizeof(u_int64_t));
279   for (i = 0; i < bm.samples;i++)
280   {
281     bm.load[i] = load;
282     bm.total[i] = total;
283   }
284 }
285 
print_version(void)286 static void print_version(void)
287 {
288   g_print("hot-babe version " VERSION "\n\n");
289 }
290 
print_usage(void)291 static void print_usage(void)
292 {
293   g_print("Usage: hot-babe [OPTIONS]\n\n");
294   g_print("OPTIONS are from the following:\n\n");
295   g_print(" -t, --threshold n    use only the first picture before n%%.\n");
296   g_print(" -i, --incremental    incremental (slow) mode.\n");
297   g_print(" -d, --delay  n       update every n millisecondes.\n");
298   g_print(" -h, --help           show this message and exit.\n");
299   g_print(" -N, --noNice         don't count nice time in usage.\n");
300   g_print(" -n, --nice  n        set self-nice to n.\n");
301   g_print("     --dir directory  use images from directory.\n");
302   g_print("     --geometry {+|-}x{+|-}y  position the hot-babe.\n");
303   g_print(" -v, --version        show version and exit.\n");
304 }
305 
parse_geometry(char * arg)306 void parse_geometry( char *arg )
307 {
308   char sign[2];
309   guint val[2];
310   int i = 0;
311 
312   i = sscanf( arg, "%c%u%c%u", &sign[0], &val[0], &sign[1], &val[1] );
313   if( i != 4 )
314     return;
315 
316   if( sign[0] == '+' ) bm.x = val[0];
317   if( sign[0] == '-' ) bm.x = -1-val[0];
318   if( sign[1] == '+' ) bm.y = val[1];
319   if( sign[1] == '-' ) bm.y = -1-val[1];
320 }
321 
main(int argc,char ** argv)322 int main(int argc, char **argv)
323 {
324   GdkEvent *event;
325 
326   gint      i;
327   gchar *dir;
328   char conf[256];
329   FILE *f;
330 
331   /* initialize GDK */
332   if (!gdk_init_check(&argc, &argv))
333   {
334     fprintf(stderr,
335         "GDK init failed, bye bye.  Check \"DISPLAY\" variable.\n");
336     exit(-1);
337   }
338   gdk_rgb_init();
339 
340   /* zero data structure */
341   memset(&bm, 0, sizeof(bm));
342 
343   bm.samples     = 16;
344   bm.incremental = FALSE;
345   bm.delay       = 15000;
346   bm.noNice      = FALSE;
347   bm.x = -1;
348   bm.y = -1;
349 
350   dir            = NULL;
351 
352 
353   snprintf( conf, 256, "%s/.hot-babe/config", g_get_home_dir() );
354   f = fopen( conf, "r" );
355   if( f )
356   {
357     char line[256], *l;
358     guint uval;
359     gint val;
360     char sval[260];
361 
362     while( (l=fgets( line, 255, f )) )
363     {
364       while( *l )
365       {
366         if( *l == '\n' || *l == '#' ) *l = 0;
367         l++;
368       }
369       if( !*line ) continue;
370 
371       if( sscanf( line, "threshold %u", &uval ) == 1 )
372       {
373         bm.threshold = uval*256/100;
374         bm.threshold = MIN (255, bm.threshold);
375       } else if ( !strcmp(line, "incremental") )
376       {
377         bm.incremental = TRUE;
378       } else if (!strcmp(line, "noNice") )
379       {
380         bm.noNice = TRUE;
381       } else if ( sscanf( line, "nice %d", &val) == 1 )
382       {
383         nice( val );
384       } else if ( sscanf( line, "delay %u", &uval) == 1 )
385       {
386         bm.delay = uval*1000;
387       } else if ( sscanf( line, "dir %s", sval) == 1)
388       {
389         dir = strdup( sval );
390       } else if ( sscanf( line, "geometry %s", sval) == 1)
391       {
392         parse_geometry( sval );
393       }
394     }
395     fclose(f);
396   }
397 
398   for (i=1 ; i<argc ; i++)
399   {
400     if (!strcmp(argv[i], "--threshold") || !strcmp(argv[i], "-t"))
401     {
402       i++;
403       if  (i<argc)
404       {
405         bm.threshold = atoi(argv[i])*256/100;
406         bm.threshold = MIN (255, bm.threshold);
407       }
408     } else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v"))
409     {
410       print_version();
411       exit(0);
412     } else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
413     {
414       print_usage();
415       exit(0);
416     } else if (!strcmp(argv[i], "--incremental") || !strcmp(argv[i], "-i"))
417     {
418       bm.incremental = TRUE;
419     } else if (!strcmp(argv[i], "--noNice") || !strcmp(argv[i], "-N"))
420     {
421       bm.noNice = TRUE;
422     } else if (!strcmp(argv[i], "--nice") || !strcmp(argv[i], "-n"))
423     {
424       i++;
425       if  (i<argc)
426       {
427         nice( atoi(argv[i]) );
428       }
429     } else if (!strcmp(argv[i], "--delay") || !strcmp(argv[i], "-d"))
430     {
431       i++;
432       if  (i<argc)
433       {
434         bm.delay = atoi(argv[i])*1000;
435       }
436     } else if (!strcmp(argv[i], "--dir"))
437     {
438       i++;
439       if  (i<argc)
440       {
441         dir = argv[i];
442       }
443     } else if (!strcmp(argv[i], "--geometry"))
444     {
445       i++;
446       if  (i<argc)
447       {
448         parse_geometry( argv[i] );
449       }
450     }
451   }
452 
453   if( dir != NULL ) {
454     char path[256], home[256];
455     snprintf( path, 256, PREFIX "/share/hot-babe/%s", dir );
456     snprintf( home, 256, "%s/.hot-babe/%s", g_get_home_dir(), dir );
457     if( load_anim( &bm.anim, path ) &&
458         load_anim( &bm.anim, home ) &&
459         load_anim( &bm.anim, dir ) ) {
460       fprintf( stderr, "Can't find pictures\n" );
461       return 1;
462     }
463   } else {
464     if( load_anim( &bm.anim, PREFIX "/share/hot-babe/hb01" ) &&
465         load_anim( &bm.anim, "hb01" ) ) {
466       fprintf( stderr, "Can't find pictures\n" );
467       return 1;
468     }
469   }
470   create_hotbabe_window();
471 
472   bm.pixels = malloc( sizeof(guchar*) * bm.anim.samples );
473   for( i = 0 ; i < bm.anim.samples ; i++ )
474     bm.pixels[i] = gdk_pixbuf_get_pixels( bm.anim.pixbuf[i] );
475   bm.dest = malloc( bm.anim.width * bm.anim.height * 3 );
476 
477   hotbabe_setup_samples();
478 
479   while (1)
480   {
481     while (gdk_events_pending())
482     {
483       event = gdk_event_get();
484       if (event)
485       {
486         switch (event->type)
487         {
488           case GDK_DESTROY:
489             gdk_exit(0);
490             exit(0);
491             break;
492           case GDK_BUTTON_PRESS:
493             if (event->button.button == 3)
494             {
495               exit(0);
496               break;
497             }
498             break;
499           default:
500             break;
501         }
502       }
503     }
504     usleep(bm.delay);
505     hotbabe_update();
506   }
507   return 0;
508 }
509 
510