/* BubbleMon dockapp 1.32 * * - dockapp for Window Maker/Blackbox/E/Afterstep/SawBabble * - Code outside of bubblemon_update copyright 2000, 2001 * timecop@japan.co.jp * - oleg dashevskii made changes to collect memory * and cpu information on FreeBSD. Some major performance improvements * and other cool hacks. Useful ideas - memscreen, load screen, etc. * - Adrian B came up with the idea of load * meter. * - tarzeau@space.ch sent in cute duck gfx and suggestions, plus some * code and duck motion fixes. * - Phil Lu Dan Price - Solaris/SunOS * port * - Everything else copyright one of the guys below * * TODO: Anything else? Add fish? * * ************************************************************************* * * Bubbling Load Monitoring Applet * - A GNOME panel applet that displays the CPU + memory load as a * bubbling liquid. * Copyright (C) 1999-2000 Johan Walles * - d92-jwa@nada.kth.se * Copyright (C) 1999 Merlin Hughes * - merlin@merlin.org * - http://nitric.com/freeware/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. * */ #define _GNU_SOURCE #define VERSION "1.41" /* general includes */ #include #include #include #include #include #include /* x11 includes */ #include #include #include #include "include/master.xpm" #include "include/bubblemon.h" #include "include/sys_include.h" #ifdef ENABLE_DUCK #include "include/ducks.h" #endif #if defined(ENABLE_CPU) #include "include/digits.h" #endif #if defined(ENABLE_MEMSCREEN) #include "include/load_screen.h" #include "include/mem_screen.h" #endif /* #define DEBUG_DUCK 1 */ /* local prototypes *INDENT-OFF* */ static void bubblemon_setup_samples(void); static void bubblemon_setup_colors(void); static void bubblemon_allocate_buffers(void); static void bubblemon_update(int proximity); static void make_new_bubblemon_dockapp(void); static void get_memory_load_percentage(void); static void bubblemon_session_defaults(void); static int get_screen_selection(void); #if defined(ENABLE_CPU) || defined(ENABLE_MEMSCREEN) /* draw functions for load average / memory screens */ static void draw_pixel(unsigned int x, unsigned int y, unsigned char *buf, unsigned char *c); static void draw_history(int num, int size, unsigned int *history, unsigned char *buf); static void draw_digit(int srcx, int srcy, int destx, int desty); static void draw_string(char *string, int x, int y, int color); static void draw_cpudigit(const int what, const int where, unsigned char *kit); static void render_secondary(void); static void realtime_alpha_blend_of_cpu_usage(int cpu, int proximity); static void roll_membuffer(void); static void roll_history(void); #endif #ifdef ENABLE_DUCK static int animate_correctly(void); static void duck_set(int x, int y, int nr, int rev, int upsidedown); static void duck_swimmer(int posy); #endif #ifdef __FreeBSD__ extern int init_stuff(); /* defined in sys_freebsd.c */ #endif /* local prototypes end *INDENT-ON* */ /* global variables */ BubbleMonData bm; /* compiled-in options string */ char options[1024]; #ifdef ENABLE_DUCK int duck_enabled = 1; #ifdef UPSIDE_DOWN_DUCK int upside_down_duck_enabled = 1; #endif /* UPSIDE_DOWN_DUCK */ #endif /* ENABLE_DUCK */ #ifdef ENABLE_CPU int cpu_enabled = 1; #endif /* ENABLE_CPU */ #ifdef ENABLE_MEMSCREEN int memscreen_enabled = 1; int memscreen_megabytes = 0; #endif /* ENABLE_MEMSCREEN */ #define INT_VAL 0 #define DOUBLE_VAL 1 #define COLOR_VAL 2 static void bubblemon_session_defaults(void) { /* handy way to collect all this stuff in one place */ typedef struct { char *name; /* name as appears in Xdefaults */ int type; /* int, double, or color, see up */ void *var; /* pointer to value - only handles INT atm */ } xrm_vars; /* XResource stuff */ char name[BUFSIZ] = "", *ptr; XrmDatabase db = NULL; XrmValue val; char *type; int i; xrm_vars tab[] = { {"bubblemon.maxbubbles", INT_VAL, &bm.maxbubbles}, {"bubblemon.air_noswap", COLOR_VAL, &bm.air_noswap}, {"bubblemon.air_maxswap", COLOR_VAL, &bm.air_maxswap}, {"bubblemon.liquid_noswap", COLOR_VAL, &bm.liquid_noswap}, {"bubblemon.liquid_maxswap", COLOR_VAL, &bm.liquid_maxswap}, {"bubblemon.ripples", DOUBLE_VAL, &bm.ripples}, {"bubblemon.gravity", DOUBLE_VAL, &bm.gravity}, {"bubblemon.volatility", DOUBLE_VAL, &bm.volatility}, {"bubblemon.viscosity", DOUBLE_VAL, &bm.viscosity}, {"bubblemon.speed_limit", DOUBLE_VAL, &bm.speed_limit} }; /* number of CPU load samples */ bm.samples = 16; /* default colors. changeable from Xdefaults */ bm.air_noswap = 0x2299ff; bm.liquid_noswap = 0x0055ff; bm.air_maxswap = 0xff0000; bm.liquid_maxswap = 0xaa0000; /* default bubble engine parameters. Changeable from Xdefaults */ bm.maxbubbles = 100; bm.ripples = 0.2; bm.gravity = 0.01; bm.volatility = 1.0; bm.viscosity = 0.98; bm.speed_limit = 1.0; db = NULL; XrmInitialize(); /* get users's local Xdefaults */ if ((ptr = getenv("HOME")) != NULL) { snprintf(name, sizeof(name), "%s/.Xdefaults", ptr); } /* get the database and parse resources if we have some */ if ((db = XrmGetFileDatabase(name)) != NULL) { for (i = 0; i < (sizeof(tab) / sizeof(tab[0])); i++) { if (XrmGetResource(db, tab[i].name, tab[i].name, &type, &val)) { if (val.size > 0) /* no empty strings and shit like that */ switch (tab[i].type) { case INT_VAL: *(int *) tab[i].var = atoi(val.addr); break; case DOUBLE_VAL: *(double *) tab[i].var = atof(val.addr); break; case COLOR_VAL: sscanf(val.addr, "#%x", (int *) tab[i].var); break; default: /* WTF? */ break; } } } } #define RANGE_CHECK(x, min, max, def) \ if ((x) > (max) || (x) < (min)) { \ fprintf(stderr, #x" value is out of range. Using default value ("#def")\n"); \ (x) = (def); \ } /* range validation. 3l33t hackerz NO PASARAN */ RANGE_CHECK(bm.air_noswap, 0, 0xffffff, 0x2299ff); RANGE_CHECK(bm.liquid_noswap, 0, 0xffffff, 0x0055ff); RANGE_CHECK(bm.air_maxswap, 0, 0xffffff, 0xff0000); RANGE_CHECK(bm.liquid_maxswap, 0, 0xffffff, 0xaa0000); RANGE_CHECK(bm.maxbubbles, 10, 200, 100); RANGE_CHECK(bm.ripples, 0, 1, 0.2); RANGE_CHECK(bm.gravity, 0.005, 0.5, 0.01); RANGE_CHECK(bm.volatility, 0.1, 2, 1.0); RANGE_CHECK(bm.viscosity, 0, 1.0, 0.98); RANGE_CHECK(bm.speed_limit, 0.5, 2, 1.0); #undef RANGE_CHECK /* convert doubles into integer representation */ bm.ripples_int = MAKE_INTEGER(bm.ripples); bm.gravity_int = MAKE_INTEGER(bm.gravity); bm.volatility_int = MAKE_INTEGER(bm.volatility); bm.viscosity_int = MAKE_INTEGER(bm.viscosity); bm.speed_limit_int = MAKE_INTEGER(bm.speed_limit); } #undef INT_VAL #undef DOUBLE_VAL #undef COLOR_VAL /* *INDENT-OFF* */ static void print_usage(void) { printf( "BubbleMon version "VERSION", features: %s\n" "Usage: bubblemon [switches] [program_1] [program_2]\n\n" "Disable compiled-in features\n" #ifdef ENABLE_DUCK " -d\tdisable swimming duck\n" #ifdef UPSIDE_DOWN_DUCK " -u\tdisable upside-down duck\n" #endif /* UPSIDE_DOWN_DUCK */ #endif /* ENABLE_DUCK */ #ifdef ENABLE_CPU " -c\tdisable CPU meter\n" #endif /* ENABLE_CPU */ #ifdef ENABLE_MEMSCREEN " -m\tdisable memory screen\n" #endif /* ENABLE_MEMSCREEN */ "\nGeneral options\n" #ifdef ENABLE_MEMSCREEN " -p\tuse alternative color scheme in memory info screen\n" " -k\tdisplay memory and swap statistics in megabytes\n" #endif " -h\tdisplay this help\n", options /* this is the global static string with compiled features */ ); } /* *INDENT-ON* */ int main(int argc, char **argv) { char execute[256]; int proximity = 0; int ch; #ifdef FPS int f, o; time_t y; #endif #ifdef PRO int cnt = 25000; #endif GdkEvent *event; #ifdef FPS o = f = y = 0; #endif /* initialize GDK */ if (!gdk_init_check(&argc, &argv)) { fprintf(stderr, "GDK init failed, bye bye. Check \"DISPLAY\" variable.\n"); exit(-1); } gdk_rgb_init(); /* dynamically generate getopt string depending on compile options * we are going to borrow 256 char string from exec function, and * also build up the "compiled features" string */ memset(execute, 0, 256); strcat(execute, "h"); /* help, always in */ #ifdef ENABLE_DUCK strcat(options, "DUCK "); strcat(execute, "d"); #ifdef UPSIDE_DOWN_DUCK strcat(options, "INVERT "); strcat(execute, "u"); #endif /* UPSIDE_DOWN_DUCK */ #endif /* ENABLE_DUCK */ #ifdef ENABLE_CPU strcat(options, "CPU "); strcat(execute, "c"); #endif /* ENABLE_CPU */ #ifdef ENABLE_MEMSCREEN strcat(options, "MEMSCREEN "); strcat(execute, "pmk"); #endif /* ENABLE_MEMSCREEN */ /* command line options */ while ((ch = getopt(argc, argv, execute)) != -1) { switch (ch) { #ifdef ENABLE_DUCK case 'd': duck_enabled = 0; break; #ifdef UPSIDE_DOWN_DUCK case 'u': upside_down_duck_enabled = 0; break; #endif /* UPSIDE_DOWN_DUCK */ #endif /* ENABLE_DUCK */ #ifdef ENABLE_CPU case 'c': cpu_enabled = 0; break; #endif /* ENABLE_CPU */ #ifdef ENABLE_MEMSCREEN case 'm': memscreen_enabled = 0; break; case 'p': { /* no sense having -p if memscreen isn't compiled in, right? * what we are going to do is to change the colors as follows: * * (48,140,240) replaced with (158,196,237) - more pale blue * (237,23,23) replaced with (0,255,233) */ unsigned char *p = mem_screen; while (p < mem_screen + sizeof(mem_screen)) if (*p == 48) { /* hopefully no other colors beginning with 48 */ *p++ = 158; *p++ = 196; *p++ = 237; } else if (*p == 237) { /* hopefully no other colors beginning with 237 */ *p++ = 0; *p++ = 255; *p++ = 233; } else p += 3; p = load_screen; while (p < load_screen + sizeof(load_screen)) if (*p == 48) { /* hopefully no other colors beginning with 48 */ *p++ = 158; *p++ = 196; *p++ = 237; } else p += 3; } break; case 'k': memscreen_megabytes = 1; break; #endif /* ENABLE_MEMSCREEN */ default: print_usage(); exit(-1); break; } } argc -= optind; argv += optind; /* zero data structure */ memset(&bm, 0, sizeof(bm)); #ifdef __FreeBSD__ if (init_stuff()) exit(-1); #endif /* set default things, from Xresources or compiled-in defaults */ bubblemon_session_defaults(); /* create dockapp window. creates windows, allocates memory, etc */ make_new_bubblemon_dockapp(); #ifdef PRO while (cnt--) { #else while (1) { #endif while (gdk_events_pending()) { event = gdk_event_get(); if (event) { switch (event->type) { case GDK_DESTROY: gdk_exit(0); exit(0); break; case GDK_BUTTON_PRESS: if (event->button.button == 3) { bm.picture_lock = 1; break; } if (event->button.button <= argc) { snprintf(execute, 250, "%s &", argv[event->button.button - 1]); system(execute); } break; #if defined(ENABLE_CPU) || defined(ENABLE_MEMSCREEN) case GDK_ENTER_NOTIFY: /* mouse in: make it darker, and eventually bring up * meminfo */ proximity = 1; bm.screen_type = get_screen_selection(); bm.picture_lock = 0; break; case GDK_LEAVE_NOTIFY: /* mouse out: back to light */ proximity = 0; break; #endif /* ENABLE_CPU || ENABLE_MEMSCREEN */ default: break; } } } #ifndef PRO usleep(15000); #else /* amazingly enough just calling this function takes insane * amount of time. usleep(0); */ #endif /* get system statistics */ get_memory_load_percentage(); /* update main rgb buffer: bm.rgb_buf */ bubblemon_update(proximity); /* *INDENT-OFF* */ #ifdef FPS /* render frames per second on bottom-right corner :) * This is GCC-specific (functions inside functions) * and very unoptimized. this is obfuscated 'cause its ugly */ f++;{int b;void q(int sx,int sy,int dx,int dy){int i,j;char *from,*to; for(j=0;j<8;j++){from=mem_screen+56*3*(sy+j)+sx*3;to=bm.rgb_buf+56*3* (dy+j)+dx*3;i=12;while(i--)*to++=*from++;}}b=o;if(b>=100){q((b/100)*4, 60,43,46);b=b%100;}q((b/10)*4,60,47,46);q((b%10)*4,60,51,46);}if(time( NULL)!=y){o=f;f=0;y=time(NULL);} #endif /* *INDENT-ON* */ /* actually draw the screen */ #ifndef BLACKBOX gdk_draw_rgb_image(bm.win, bm.gc, 4, 4, 56, 56, GDK_RGB_DITHER_NONE, bm.rgb_buf, 56 * 3); #endif gdk_draw_rgb_image(bm.iconwin, bm.gc, 4, 4, 56, 56, GDK_RGB_DITHER_NONE, bm.rgb_buf, 56 * 3); #ifdef ENABLE_MEMSCREEN /* update graph histories */ if (memscreen_enabled) roll_history(); #endif /* ENABLE_MEMSCREEN */ } return 0; } /* main */ /* * This determines if the left or right shift keys are depressed. */ static int get_screen_selection(void) { static KeyCode lshift_code, rshift_code; static int first_time = 1; char keys[32]; if (first_time) { first_time = 0; lshift_code = XKeysymToKeycode(GDK_WINDOW_XDISPLAY(bm.win), XStringToKeysym("Shift_L")); rshift_code = XKeysymToKeycode(GDK_WINDOW_XDISPLAY(bm.win), XStringToKeysym("Shift_R")); } XQueryKeymap(GDK_WINDOW_XDISPLAY(bm.win), keys); #if 0 if (0) { /* debug */ int i = 0; printf("lshift_code = 0x%x (index = %d, bit = %d\n", lshift_code, lshift_code >> 3, lshift_code % 8); printf("rshift_code = 0x%x (index = %d, bit = %d\n", rshift_code, rshift_code >> 3, rshift_code % 8); for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) { if (0 == (i % 8)) { printf("\n%2d:", i); } printf(" %2d", keys[i]); } printf("\n"); } #endif if ((keys[lshift_code >> 3] == (1 << (lshift_code % 8))) || (keys[rshift_code >> 3] == (1 << (rshift_code % 8)))) { return 0; } else { return 1; } } /* This is the function that actually creates the display widgets */ static void make_new_bubblemon_dockapp(void) { #define MASK GDK_BUTTON_PRESS_MASK | \ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK GdkWindowAttr attr; GdkWindowAttr attri; Window win; Window iconwin; XSizeHints sizehints; XWMHints wmhints; attr.width = 64; attr.height = 64; attr.title = "bubblemon"; attr.event_mask = MASK; attr.wclass = GDK_INPUT_OUTPUT; attr.visual = gdk_visual_get_system(); attr.colormap = gdk_colormap_get_system(); attr.wmclass_name = "bubblemon"; attr.wmclass_class = "bubblemon"; attr.window_type = GDK_WINDOW_TOPLEVEL; sizehints.flags = USSize; sizehints.width = 64; sizehints.height = 64; bm.win = gdk_window_new(NULL, &attr, GDK_WA_TITLE | GDK_WA_WMCLASS | GDK_WA_VISUAL | GDK_WA_COLORMAP); if (!bm.win) fprintf(stderr, "Cannot make toplevel window\n"); attri.width = 64; attri.height = 64; attri.title = "bubblemon"; attri.event_mask = MASK; attri.wclass = GDK_INPUT_OUTPUT; attri.visual = gdk_visual_get_system(); attri.colormap = gdk_colormap_get_system(); attri.wmclass_name = "bubblemon"; attri.wmclass_class = "bubblemon"; attri.window_type = GDK_WINDOW_TOPLEVEL; bm.iconwin = gdk_window_new(bm.win, &attri, GDK_WA_TITLE | GDK_WA_WMCLASS); if (!bm.iconwin) fprintf(stderr, "Cannot make icon window\n"); win = GDK_WINDOW_XWINDOW(bm.win); iconwin = GDK_WINDOW_XWINDOW(bm.iconwin); XSetWMNormalHints(GDK_WINDOW_XDISPLAY(bm.win), win, &sizehints); wmhints.initial_state = WithdrawnState; wmhints.icon_window = iconwin; wmhints.icon_x = 0; wmhints.icon_y = 0; wmhints.window_group = win; wmhints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint; XSetWMHints(GDK_WINDOW_XDISPLAY(bm.win), win, &wmhints); bm.gc = gdk_gc_new(bm.win); bm.pixmap = gdk_pixmap_create_from_xpm_d(bm.win, &(bm.mask), NULL, master_xpm); gdk_window_shape_combine_mask(bm.win, bm.mask, 0, 0); gdk_window_shape_combine_mask(bm.iconwin, bm.mask, 0, 0); gdk_window_set_back_pixmap(bm.win, bm.pixmap, False); gdk_window_set_back_pixmap(bm.iconwin, bm.pixmap, False); gdk_window_show(bm.win); #ifdef KDE_DOCKAPP /* makes the dockapp visible inside KDE wm */ gdk_window_show(bm.iconwin); #endif /* We begin with zero bubbles */ bm.n_bubbles = 0; /* Allocate memory for calculations */ bubblemon_allocate_buffers(); bubblemon_setup_samples(); bubblemon_setup_colors(); #undef MASK } /* make_new_bubblemon_dockapp */ /* * This function, bubblemon_update, gets the CPU usage and updates * the bubble array and main rgb buffer. */ static void bubblemon_update(int proximity) { Bubble *bubbles = bm.bubbles; unsigned int i, loadPercentage, *col, x, y; unsigned char *ptr, *buf, *buf_ptr; unsigned int aircolor, watercolor, aliascolor; unsigned int waterlevels_goal; /*int current_waterlevel_goal; */ /* These values are for keeping track of where we have to start drawing water. */ unsigned int waterlevel_min, waterlevel_max; unsigned int real_waterlevel_min, real_waterlevel_max; /* These values are for keeping track how deep the duck is inside water */ unsigned int action_min = 56; static unsigned int last_action_min = 0; /* Find out the CPU load */ loadPercentage = system_cpu(); #ifdef ENABLE_MEMSCREEN /* get loadavg */ if (memscreen_enabled) system_loadavg(); #endif /* ENABLE_MEMSCREEN */ /* The buf is made up of ints (0-(NUM_COLORS-1)), each pointing out an entry in the color table. A pixel in the buf is accessed using the formula buf[row * w + column]. */ /* y coordinates are counted from here multiplied by 256 */ /* to get actual screen coordinate, divide by 256 */ buf = bm.bubblebuf; col = bm.colors; waterlevel_max = 0; waterlevel_min = MAKEY(56); /* Move the water level with the current memory usage. */ waterlevels_goal = MAKEY(56) - ((bm.mem_percent * MAKEY(56)) / 100); /* Guard against boundary errors */ waterlevels_goal -= (1 << (POWER2 - 1)); bm.waterlevels[0] = waterlevels_goal; bm.waterlevels[55] = waterlevels_goal; for (x = 1; x < 55; x++) { /* Accelerate the current waterlevel towards its correct value */ bm.waterlevels_dy[x] += (((bm.waterlevels[x - 1] + bm.waterlevels[x + 1] - 2 * bm.waterlevels[x]) * bm.volatility_int) >> (POWER2 + 1)); bm.waterlevels_dy[x] *= bm.viscosity_int; bm.waterlevels_dy[x] >>= POWER2; if (bm.waterlevels_dy[x] > bm.speed_limit_int) bm.waterlevels_dy[x] = bm.speed_limit_int; else if (bm.waterlevels_dy[x] < -bm.speed_limit_int) bm.waterlevels_dy[x] = -bm.speed_limit_int; } for (x = 1; x < 55; x++) { /* Move the current water level */ bm.waterlevels[x] = bm.waterlevels[x] + bm.waterlevels_dy[x]; if (bm.waterlevels[x] > MAKEY(56)) { /* Stop the wave if it hits the floor... */ bm.waterlevels[x] = MAKEY(56); bm.waterlevels_dy[x] = 0; } else if (bm.waterlevels[x] < 0) { /* ... or the ceiling. */ bm.waterlevels[x] = 0; bm.waterlevels_dy[x] = 0; } /* Keep track of the highest and lowest water levels */ if (bm.waterlevels[x] > waterlevel_max) waterlevel_max = bm.waterlevels[x]; if (bm.waterlevels[x] < waterlevel_min) waterlevel_min = bm.waterlevels[x]; } real_waterlevel_min = REALY(waterlevel_min); real_waterlevel_max = REALY(waterlevel_max); if (action_min > real_waterlevel_min) action_min = real_waterlevel_min; /* Vary the colors of air and water with how many percent of the available swap space that is in use. 32 = (99_numcolors / 3) - 1 */ watercolor = ((32 * bm.swap_percent) / 100) * 3; aliascolor = watercolor + 1; aircolor = watercolor + 2; /* Draw the air-and-water background The waterlevel_max is the HIGHEST VALUE for the water level, which is actually the LOWEST VISUAL POINT of the water. Confusing enough? So we want to draw from top to bottom: Just air from (y == 0) to (y <= waterlevel_min) Mixed air and water from (y == waterlevel_min) to (y <= waterlevel_max) Just water from (y == waterlevel_max) to (y <= h) Three loops is more code than one, but should be faster (fewer comparisons) */ /* Air only */ memset(buf, aircolor, real_waterlevel_min * 56); /* Air and water */ for (x = 0; x < 56; x++) { /* Air... */ for (y = real_waterlevel_min; (signed) y < REALY(bm.waterlevels[x]); y++) buf[y * 56 + x] = aircolor; /* ... and water */ for (; y < real_waterlevel_max; y++) buf[y * 56 + x] = watercolor; } /* Water only */ memset(buf + real_waterlevel_max * 56, watercolor, (56 - real_waterlevel_max) * 56); /* Here comes the bubble magic. Pixels are drawn by setting values in buf to 0-NUM_COLORS. */ /* Create a new bubble if the planets are correctly aligned... */ if ((bm.n_bubbles < bm.maxbubbles) && ((rand() % 101) <= loadPercentage)) { /* We don't allow bubbles on the edges 'cause we'd have to clip them */ bubbles[bm.n_bubbles].x = (rand() % 54) + 1; bubbles[bm.n_bubbles].y = MAKEY(56) - 256; bubbles[bm.n_bubbles].dy = 0; #ifdef DEBUG_DUCK fprintf (stderr, "new bubble: bubbles[bm.n_bubbles].x = %i\n", bubbles[bm.n_bubbles].x); #endif if (bm.ripples_int != 0) { /* Raise the water level above where the bubble is created */ if (bubbles[bm.n_bubbles].x > 2) bm.waterlevels[bubbles[bm.n_bubbles].x - 2] -= bm.ripples_int; bm.waterlevels[bubbles[bm.n_bubbles].x - 1] -= bm.ripples_int; bm.waterlevels[bubbles[bm.n_bubbles].x] -= bm.ripples_int; bm.waterlevels[bubbles[bm.n_bubbles].x + 1] -= bm.ripples_int; if (bubbles[bm.n_bubbles].x < 53) bm.waterlevels[bubbles[bm.n_bubbles].x + 2] -= bm.ripples_int; } /* Count the new bubble */ bm.n_bubbles++; } /* Update and draw the bubbles */ for (i = 0; i < bm.n_bubbles; i++) { /* Accelerate the bubble */ bubbles[i].dy -= bm.gravity_int; /* Move the bubble vertically */ bubbles[i].y += bubbles[i].dy; /* is the bubble grossly out of bounds? */ if (bubbles[i].x < 1 || bubbles[i].x > 54 || bubbles[i].y > MAKEY(56)) { #ifdef DEBUG_DUCK fprintf (stderr, "bubble out of bounds " "bubbles[%i].x=%i, bubbles[%i].y=%i\n", i, bubbles[i].x, i, bubbles[i].y); #endif /* Yes; nuke it */ bubbles[i].x = bubbles[bm.n_bubbles - 1].x; bubbles[i].y = bubbles[bm.n_bubbles - 1].y; bubbles[i].dy = bubbles[bm.n_bubbles - 1].dy; bm.n_bubbles--; /* We must check the previously last bubble, which is now the current bubble, also. */ i--; continue; } /* Did we lose it? */ if (bubbles[i].y < bm.waterlevels[bubbles[i].x]) { if (bm.ripples_int != 0) { /* Lower the water level around where the bubble is about to vanish */ bm.waterlevels[bubbles[i].x - 1] += bm.ripples_int; bm.waterlevels[bubbles[i].x] += 3 * bm.ripples_int; bm.waterlevels[bubbles[i].x + 1] += bm.ripples_int; } /* Yes; nuke it */ bubbles[i].x = bubbles[bm.n_bubbles - 1].x; bubbles[i].y = bubbles[bm.n_bubbles - 1].y; bubbles[i].dy = bubbles[bm.n_bubbles - 1].dy; bm.n_bubbles--; /* We must check the previously last bubble, which is now the current bubble, also. */ i--; continue; } /* Draw the bubble */ x = bubbles[i].x; y = bubbles[i].y; /* Clipping is not necessary for x, but it *is* for y. To prevent ugliness, we draw aliascolor only on top of watercolor, and aircolor on top of aliascolor. */ /* Top row */ buf_ptr = &(buf[(((REALY(y) - 1) * 56) + 56) + x - 1]); if (y > bm.waterlevels[x]) { if (*buf_ptr != aircolor) { (*buf_ptr)++; } buf_ptr++; *buf_ptr = aircolor; buf_ptr++; if (*buf_ptr != aircolor) { (*buf_ptr)++; } buf_ptr += 54; } else { buf_ptr += 56; } /* Middle row - no clipping necessary */ *buf_ptr = aircolor; buf_ptr++; *buf_ptr = aircolor; buf_ptr++; *buf_ptr = aircolor; buf_ptr += 54; /* Bottom row */ if (y < (MAKEY(56) - 256)) { if (*buf_ptr != aircolor) { (*buf_ptr)++; } buf_ptr++; *buf_ptr = aircolor; buf_ptr++; if (*buf_ptr != aircolor) { (*buf_ptr)++; } } } /* Drawing magic resides below this point */ ptr = bm.rgb_buf; buf_ptr = buf; i = 56 * 56; while (i--) { unsigned char *rgb = (unsigned char *) &col[*buf_ptr++]; #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(_LITTLE_ENDIAN) && !defined(__FreeBSD__)) || (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN)) *ptr++ = rgb[2]; *ptr++ = rgb[1]; *ptr++ = rgb[0]; #else /* big-endian. */ *ptr++ = rgb[1]; *ptr++ = rgb[2]; *ptr++ = rgb[3]; #endif } #ifdef ENABLE_DUCK if (duck_enabled) { duck_swimmer((last_action_min < action_min) ? last_action_min - 14 : action_min - 14); } #endif /* damn, thats ugly. Of course, the first solution that comes to mind * about this isn't the right one :) */ #if defined(ENABLE_CPU) && defined(ENABLE_MEMSCREEN) if (cpu_enabled || memscreen_enabled) { realtime_alpha_blend_of_cpu_usage(loadPercentage, proximity); } #endif #if defined(ENABLE_CPU) && !defined(ENABLE_MEMSCREEN) if (cpu_enabled) { realtime_alpha_blend_of_cpu_usage(loadPercentage, proximity); } #endif #if !defined(ENABLE_CPU) && defined(ENABLE_MEMSCREEN) if (memscreen_enabled) { realtime_alpha_blend_of_cpu_usage(loadPercentage, proximity); } #endif /* Remember where we have been poking around this round */ last_action_min = action_min; } /* bubblemon_update */ #ifdef ENABLE_MEMSCREEN /* draws 4x8 digits for the memory/swap panel */ static void draw_digit(int srcx, int srcy, int destx, int desty) { int i, j; char *from, *to; for (j = 0; j < 8; j++) { from = mem_screen + 56 * 3 * (srcy + j) + srcx * 3; to = bm.mem_buf + 56 * 3 * (desty + j) + destx * 3; i = 12; while (i--) *to++ = *from++; } } /* draws a string using previous function. non-digits and non-K/M = space */ static void draw_string(char *string, int x, int y, int color) { unsigned char c; while ((c = *string++)) { if (c == 'K') { draw_digit(40, (color) ? 69 : 60, x, y); } else if (c == 'M') { draw_digit(44, (color) ? 69 : 60, x, y); } else if (c == ' ') { draw_digit(50, 60, x, y); /* blank space */ } else { c -= '0'; draw_digit(c * 4, (color) ? 69 : 60, x, y); } x += 4; } } static void draw_pixel(unsigned int x, unsigned int y, unsigned char *buf, unsigned char *c) { unsigned char *ptr; ptr = buf + y * 56 * 3 + x * 3 + 6; /* +6 = x + 2 */ *ptr++ = *c++; *ptr++ = *c++; *ptr++ = *c++; } /* draw graph num x size, data taken from history, into rgb buffer buf. * this is called not very often: only 1 time out of 250 */ static void draw_history(int num, int size, unsigned int *history, unsigned char *buf) { int pixels_per_byte; int j, k; int *p; int d; pixels_per_byte = 100; p = history; for (j = 0; j < num; j++) { if (p[0] > pixels_per_byte) pixels_per_byte += 100; p++; } p = history; for (k = 0; k < num; k++) { d = (1.0 * p[0] / pixels_per_byte) * size; for (j = 0; j < size; j++) { if (j < d - 2) draw_pixel(k, size - j - 1, buf, "\x00\x7d\x71"); else if (j < d) draw_pixel(k, size - j - 1, buf, "\x20\xb6\xae"); } p++; } for (j = pixels_per_byte - 100; j > 0; j -= 100) { for (k = 0; k < num; k++) { d = ((float) size / pixels_per_byte) * j; draw_pixel(k, size - d - 1, buf, "\x71\xe3\x71"); } } } /* refreshes memory/swap screen */ static void render_secondary(void) { char percent[4]; char number[8]; int i; /* mem: 2, 24 * mem %: 38, 24 * swap: 2, 43 * 38, 43 * digits: 0, 60 and 0, 69 */ /* make a clean buffer with blank spaces. */ memcpy(bm.mem_buf, bm.screen_type ? load_screen : mem_screen, 56 * 56 * 3); if (bm.screen_type) { for (i = 0; i < 3; i++) { sprintf(number, "%2d", bm.loadavg[i].i); draw_string(number, 19 * i, 8, 0); sprintf(number, "%02d", bm.loadavg[i].f); draw_string(number, 19 * i + 10, 8, 0); } /* copy history graph from previous rollover */ memcpy(bm.mem_buf + 19 * 56 * 3, bm.his_bufb, 56 * 33 * 3); } else { /* draw memory */ if (memscreen_megabytes) snprintf(number, 8, "%6lluM", bm.mem_used >> 20); else snprintf(number, 8, "%6lluK", bm.mem_used >> 10); snprintf(percent, 4, "%03d", bm.mem_percent); draw_string(number, 2, 1, (bm.mem_percent > 90) ? 1 : 0); draw_string(percent, 38, 1, (bm.mem_percent > 90) ? 1 : 0); /* draw swap */ if (memscreen_megabytes) snprintf(number, 8, "%6lluM", bm.swap_used >> 20); else snprintf(number, 8, "%6lluK", bm.swap_used >> 10); snprintf(percent, 4, "%03d", bm.swap_percent); draw_string(number, 2, 10, (bm.swap_percent > 90) ? 1 : 0); draw_string(percent, 38, 10, (bm.swap_percent > 90) ? 1 : 0); /* copy history graph from previous rollover */ memcpy(bm.mem_buf + 21 * 56 * 3, bm.his_bufa, 56 * 31 * 3); } } static void roll_membuffer(void) { static int delay; if (++delay < 30) return; delay = 0; render_secondary(); } static void roll_history(void) { unsigned int yep, j; static int update, doit; if (doit-- <= 0) { doit = ROLLVALUE; if (update-- <= 0) { /* roll history buffers, averaging last 5 samples */ if (bm.history[52]) bm.history[52] /= bm.hisadd; if (bm.memhist[52]) bm.memhist[52] /= bm.memadd; for (j = 1; j < 53; j++) { bm.history[j - 1] = bm.history[j]; bm.memhist[j - 1] = bm.memhist[j]; } bm.history[52] = 0; bm.hisadd = 0; bm.memhist[52] = 0; bm.memadd = 0; /* refresh backgrounds */ memcpy(bm.his_bufa, mem_screen + 21 * 56 * 3, 31 * 56 * 3); memcpy(bm.his_bufb, load_screen + 19 * 56 * 3, 33 * 56 * 3); /* render memory graph */ draw_history(52, 31, bm.memhist, bm.his_bufa); /* render load average graph */ draw_history(52, 33, bm.history, bm.his_bufb); /* reset counter */ update = 5; } /* do load average history update */ yep = bm.loadavg[0].f + (bm.loadavg[0].i * 100); bm.history[52] += yep; bm.hisadd++; /* do memory history update */ yep = bm.mem_percent; bm.memhist[52] += yep; bm.memadd++; } } #endif /* ENABLE_MEMSCREEN */ #ifdef ENABLE_CPU /* draws digits for the CPU meter. This function is very specific * to the meter. Arguments it takes are not what they seem initially */ static void draw_cpudigit(const int what, const int where, unsigned char *kit) { unsigned int len, y; unsigned char *to, *from; for (y = 0; y < 9; y++) { len = 21; to = kit + y * 75 + where; from = digits + y * 285 + what; while (len--) *to++ = *from++; } } #endif /* ENABLE_CPU */ #if defined(ENABLE_CPU) || defined(ENABLE_MEMSCREEN) static void realtime_alpha_blend_of_cpu_usage(int cpu, int proximity) { /* where is the text going to be (now, bottom-center) */ #define POSX 16 #define POSY 46 int bob; /* memory window */ static int blend = MAXBLEND; #ifdef ENABLE_MEMSCREEN static int memblend = 256; static int showmem = 0; #endif /* ENABLE_MEMSCREEN */ #ifdef ENABLE_CPU static int yoh; static int avg; int hibyte, y, pos; /* CPU load buffer */ static unsigned char kit[25 * 3 * 9 + 1]; unsigned char *kitptr; /* the plan is simple. we don't want to redraw the digits every update * because that doesn't look so good. so we average it, and draw only * once every 10 updates. We alpha blend the static buffer so we still * get cool transparency effects. */ avg += cpu; while (++yoh > 10) { cpu = avg / 10; avg ^= avg; /* zero. Haha, I guess these are not */ yoh ^= yoh; /* register vars, optimizer just zeros them */ hibyte = cpu / 10; /* but it's cool to know it understands xor a, a */ if (hibyte == 10) { draw_cpudigit(18, 0, kit); draw_cpudigit(0, 18, kit); draw_cpudigit(0, 36, kit); } else { draw_cpudigit(216, 0, kit); draw_cpudigit(hibyte * 18, 18, kit); draw_cpudigit((cpu % 10) * 18, 36, kit); } /* percent sign is always there */ draw_cpudigit(180, 54, kit); } #endif /* ENABLE_CPU */ /* sexy fade effect */ if (proximity) { blend -= 4; if (blend < MINBLEND) { blend = MINBLEND; #ifdef ENABLE_MEMSCREEN if (memscreen_enabled) { if (!showmem) { /* first time here, update memory stats */ render_secondary(); } showmem = 1; if (!bm.picture_lock) memblend -= 6; if (memblend < 40) { roll_membuffer(); memblend = 40; } } #endif /* ENABLE_MEMSCREEN */ } } else { blend += 4; #ifdef ENABLE_MEMSCREEN if (bm.picture_lock) roll_membuffer(); if (memscreen_enabled && !bm.picture_lock) memblend += 10; #endif /* ENABLE_MEMSCREEN */ if (blend > MAXBLEND) { blend = MAXBLEND; } #ifdef ENABLE_MEMSCREEN if (memscreen_enabled && memblend > 256) { memblend = 256; showmem = 0; } #endif /* ENABLE_MEMSCREEN */ } #ifdef ENABLE_CPU if (cpu_enabled) { /* bit shifts result in smaller and faster code without an extra jns * which appears if we / 128 instead of >> 7. */ kitptr = kit; for (y = 0; y < 9; y++) { unsigned char src; pos = (y + POSY) * 56 * 3 + (POSX * 3); bob = 75; /* 25 * 3 */ while (bob--) { src = bm.rgb_buf[pos]; if (!src) bm.rgb_buf[pos++] = *kitptr++; else bm.rgb_buf[pos++] = (blend * src + (256 - blend) * *kitptr++) >> 8; } } } #endif /* ENABLE_CPU */ #ifdef ENABLE_MEMSCREEN /* we hovered long enough to print some memory info */ if (memscreen_enabled && showmem) { unsigned char *ptr, *ptr2, src; ptr = bm.mem_buf; ptr2 = bm.rgb_buf; bob = 9408; /* 56 * 56 * 3 */ while (bob--) { src = *ptr2; *ptr2++ = (memblend * src + (256 - memblend) * *ptr++) >> 8; } } #endif /* ENABLE_MEMSCREEN */ #undef POSY #undef POSX } #endif #ifdef ENABLE_DUCK static void duck_set(int x, int y, int nr, int rev, int upsidedown) { int w, h; int rw; #ifdef UPSIDE_DOWN_DUCK int rh; #endif int pos; int dw, di, dh, ds; int cmap; /* index into duck colors */ #define _R(idx) ((int)duck_cmap[idx][0]) #define _G(idx) ((int)duck_cmap[idx][1]) #define _B(idx) ((int)duck_cmap[idx][2]) #define GETME(x, y, idx) ((int)duck_data[idx][y * 18 + x]) ds = 0; if (y < 0) ds = -(y); dh = 17; if ((y + 17) > 56) dh = 56 - y; dw = 18; if (x > 38) dw = 18 - (x - 38); di = 0; if (x < 0) di = -(x); for (h = ds; h < dh; h++) { /* calculate this only once */ int ypos = (h + y) * 56; #ifdef UPSIDE_DOWN_DUCK rh = (upsidedown && upside_down_duck_enabled) ? 17 - h : h; #endif for (w = di; w < dw; w++) { rw = (rev) ? 17 - w : w; #ifdef UPSIDE_DOWN_DUCK if ((cmap = GETME(rw, rh, nr)) != 0) { #else if ((cmap = GETME(rw, h, nr)) != 0) { #endif unsigned char r, g, b; pos = (ypos + w + x) * 3; r = _R(cmap); g = _G(cmap); b = _B(cmap); /* and now we'll blend the duck part that in water */ /* if we use integers here we speed up this function about * 40%. */ if (h + y < REALY(bm.waterlevels[w + x])) { bm.rgb_buf[pos++] = r; bm.rgb_buf[pos++] = g; bm.rgb_buf[pos] = b; } else { bm.rgb_buf[pos] = (DUCKBLEND * (int) bm.rgb_buf[pos] + (256 - DUCKBLEND) * (int) r) >> 8; bm.rgb_buf[pos + 1] = (DUCKBLEND * (int) bm.rgb_buf[pos + 1] + (256 - DUCKBLEND) * (int) g) >> 8; bm.rgb_buf[pos + 2] = (DUCKBLEND * (int) bm.rgb_buf[pos + 2] + (256 - DUCKBLEND) * (int) b) >> 8; } } } } #undef _R #undef _G #undef _B #undef GETME } static int animate_correctly(void) { /* returns the correct order of framenumber 0,1,2,1,0,1,2... instead of 0,1,2,0,1,2 <- this way the duck looks really ugly instead of keeping 2 counters we just made it longer */ static int outp[48] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1 }; static int totalcounter = -1; if (++totalcounter > 47) totalcounter = 0; return outp[totalcounter]; } static void duck_swimmer(int posy) { static int tx = -19; static int rp; static int rev = 1; static int upsidedown = 0; #ifdef UPSIDE_DOWN_DUCK /* dive */ if (upside_down_duck_enabled) { if (upsidedown == 0 && posy < 2) upsidedown = 1; else if (upsidedown == 1 && posy > 5) /* jump out */ upsidedown = 0; if (upsidedown) posy += 10; } #endif if (rp++ < 10) { duck_set(tx, posy, animate_correctly(), rev, upsidedown); return; } rp = 0; if (!rev) { if (tx-- < -18) { tx = -18; rev = 1; } } else { if (tx++ > 57) { tx = 57; rev = 0; } } duck_set(tx, posy, animate_correctly(), rev, upsidedown); } #endif /* ENABLE_DUCK */ static void bubblemon_setup_samples(void) { int i; u_int64_t load = 0, total = 0; if (bm.load) { load = bm.load[bm.loadIndex]; free(bm.load); } if (bm.total) { total = bm.total[bm.loadIndex]; free(bm.total); } bm.loadIndex = 0; bm.load = malloc(bm.samples * sizeof(u_int64_t)); bm.total = malloc(bm.samples * sizeof(u_int64_t)); for (i = 0; i < bm.samples; i++) { bm.load[i] = load; bm.total[i] = total; } } static void bubblemon_setup_colors(void) { #define NUM_COLORS 99 int i, j, *col; int r_air_noswap, g_air_noswap, b_air_noswap; int r_liquid_noswap, g_liquid_noswap, b_liquid_noswap; int r_air_maxswap, g_air_maxswap, b_air_maxswap; int r_liquid_maxswap, g_liquid_maxswap, b_liquid_maxswap; int actual_colors = NUM_COLORS / 3; if (!bm.colors) bm.colors = malloc(NUM_COLORS * sizeof(int)); col = bm.colors; r_air_noswap = (bm.air_noswap >> 16) & 0xff; g_air_noswap = (bm.air_noswap >> 8) & 0xff; b_air_noswap = (bm.air_noswap) & 0xff; r_liquid_noswap = (bm.liquid_noswap >> 16) & 0xff; g_liquid_noswap = (bm.liquid_noswap >> 8) & 0xff; b_liquid_noswap = (bm.liquid_noswap) & 0xff; r_air_maxswap = (bm.air_maxswap >> 16) & 0xff; g_air_maxswap = (bm.air_maxswap >> 8) & 0xff; b_air_maxswap = (bm.air_maxswap) & 0xff; r_liquid_maxswap = (bm.liquid_maxswap >> 16) & 0xff; g_liquid_maxswap = (bm.liquid_maxswap >> 8) & 0xff; b_liquid_maxswap = (bm.liquid_maxswap) & 0xff; for (i = 0; i < actual_colors; i++) { int r, g, b; int r_composite, g_composite, b_composite; j = i >> 1; /* Liquid */ r = (r_liquid_maxswap * j + r_liquid_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); g = (g_liquid_maxswap * j + g_liquid_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); b = (b_liquid_maxswap * j + b_liquid_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); r_composite = r; g_composite = g; b_composite = b; col[(i * 3)] = (r << 16) | (g << 8) | b; /* Air */ r = (r_air_maxswap * j + r_air_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); g = (g_air_maxswap * j + g_air_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); b = (b_air_maxswap * j + b_air_noswap * ((actual_colors - 1) - j)) / (actual_colors - 1); r_composite += r; g_composite += g; b_composite += b; col[(i * 3) + 2] = (r << 16) | (g << 8) | b; /* Anti-alias */ r = r_composite >> 1; g = g_composite >> 1; b = b_composite >> 1; col[(i * 3) + 1] = (r << 16) | (g << 8) | b; } #undef NUM_COLORS } static void bubblemon_allocate_buffers(void) { int i; /* storage for bubbles */ bm.bubbles = (Bubble *) malloc(sizeof(Bubble) * bm.maxbubbles); /* Allocate (zeroed) bubble memory */ if (bm.bubblebuf) free(bm.bubblebuf); bm.bubblebuf = calloc(56 * 60, sizeof(char)); /* Allocate water level memory */ if (bm.waterlevels) free(bm.waterlevels); bm.waterlevels = malloc(56 * sizeof(int)); for (i = 0; i < 56; i++) { bm.waterlevels[i] = MAKEY(56); } /* Allocate water level velocity memory */ if (bm.waterlevels_dy) free(bm.waterlevels_dy); bm.waterlevels_dy = calloc(56, sizeof(int)); } static void get_memory_load_percentage(void) { /* system_memory() will return true on initial run so that we get * correct memory info, but may subsequently return 0 if memory * is not changing */ if (system_memory()) { /* new memory/swap data is in, calculate things */ bm.mem_percent = (100 * bm.mem_used) / bm.mem_max; if (bm.swap_max != 0) { bm.swap_percent = (100 * bm.swap_used) / bm.swap_max; } else { bm.swap_percent = 0; } } } /* ex:set ts=8: */