/* -*- Mode: C; tab-width: 4 -*- */ /* bubble --- simple exploding bubbles */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)bubble.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1998 by Charles Vidal * http://www.chez.com/vidalc * and David Bagley * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 01-Nov-2000: Allocation checks * 10-Jan-1998: Written. */ #ifdef STANDALONE #define MODE_bubble #define PROGCLASS "Bubble" #define HACK_INIT init_bubble #define HACK_DRAW draw_bubble #define bubble_opts xlockmore_opts #define DEFAULTS "*delay: 100000 \n" \ "*count: 25 \n" \ "*size: 100 \n" \ "*ncolors: 200 \n" \ "*fullrandom: True \n" #define UNIFORM_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_bubble #define DEF_BOIL "False" static Bool boil; static XrmOptionDescRec opts[] = { {(char *) "-boil", (char *) ".bubble.boil", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+boil", (char *) ".bubble.boil", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & boil, (char *) "boil", (char *) "Boil", (char *) DEF_BOIL, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+boil", (char *) "turn on/off boil"} }; ModeSpecOpt bubble_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct bubble_description = {"bubble", "init_bubble", "draw_bubble", "release_bubble", "refresh_bubble", "init_bubble", (char *) NULL, &bubble_opts, 100000, 25, 1, 100, 64, 0.6, "", "Shows popping bubbles", 0, NULL}; #endif typedef struct { int x, y, life; } bubbletype; #define MINSIZE 20 #define MINBUBBLES 1 typedef struct { int direction; int color; int width, height; int nbubbles; Bool boil; bubbletype *bubble; int d; Pixmap dbuf; GC dbuf_gc; } bubblestruct; static bubblestruct *bubbles = (bubblestruct *) NULL; static void updateBubble(ModeInfo * mi, int i) { bubblestruct *bp = &bubbles[MI_SCREEN(mi)]; int x, diameter, x4, y4, diameter4; if (bp->bubble[i].life + 1 > bp->d - NRAND(16) || bp->bubble[i].y < 0) { bp->bubble[i].life = 0; return; } ++bp->bubble[i].life; diameter = bp->bubble[i].life; x = bp->bubble[i].x; if (bp->boil) { bp->bubble[i].y -= diameter / 2; x += (int) (cos((double) (diameter + bp->bubble[i].x) / (M_PI / 5.0)) * (double) diameter); } /* SunOS 4.1.X xnews server may crash without this */ if (diameter < 4) { XFillRectangle(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc, x - diameter / 2, bp->bubble[i].y - diameter / 2, diameter, diameter); } else { XDrawArc(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc, x - diameter / 2, bp->bubble[i].y - diameter / 2, diameter, diameter, 0, 23040); } diameter4 = diameter / 4; if (diameter4 > 0) { x4 = x - diameter4 / 2 + ((bp->direction / 2) ? (-1) : 1) * diameter / 6; y4 = bp->bubble[i].y - diameter4 / 2 + ((bp->direction % 2) ? 1 : (-1)) * diameter / 6; /* SunOS 4.1.X xnews server may crash without this */ if (diameter4 < 4) { XFillRectangle(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc, x4, y4, diameter4, diameter4); } else { XFillArc(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc, x4, y4, diameter4, diameter4, 0, 23040); } } } static void changeBubble(ModeInfo * mi) { bubblestruct *bp = &bubbles[MI_SCREEN(mi)]; int i; for (i = 0; i < bp->nbubbles; i++) { if (bp->bubble[i].life != 0) updateBubble(mi, i); } i = NRAND(bp->nbubbles); if (bp->bubble[i].life == 0) { bp->bubble[i].x = NRAND(bp->width); if (bp->boil) bp->bubble[i].y = bp->height - ((bp->height >= 16) ? NRAND(bp->height / 16) : 0); else bp->bubble[i].y = NRAND(bp->height); updateBubble(mi, i); } } static void free_bubble(Display *display, bubblestruct *bp) { if (bp->dbuf != None) { XFreePixmap(display, bp->dbuf); bp->dbuf = None; } if (bp->dbuf_gc != None) { XFreeGC(display, bp->dbuf_gc); bp->dbuf_gc = None; } if (bp->bubble != NULL) { free(bp->bubble); bp->bubble = (bubbletype *) NULL; } } void init_bubble(ModeInfo * mi) { bubblestruct *bp; int size = MI_SIZE(mi); Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); XGCValues gcv; if (bubbles == NULL) { if ((bubbles = (bubblestruct *) calloc(MI_NUM_SCREENS(mi), sizeof (bubblestruct))) == NULL) return; } bp = &bubbles[MI_SCREEN(mi)]; bp->width = MI_WIDTH(mi); bp->height = MI_HEIGHT(mi); bp->direction = NRAND(4); if (MI_IS_FULLRANDOM(mi)) bp->boil = (Bool) (LRAND() & 1); else bp->boil = boil; if (size < -MINSIZE) bp->d = NRAND(MIN(-size, MAX(MINSIZE, MIN(bp->width, bp->height) / 2)) - MINSIZE + 1) + MINSIZE; else if (size < MINSIZE) { if (!size) bp->d = MAX(MINSIZE, MIN(bp->width, bp->height) / 2); else bp->d = MINSIZE; } else bp->d = MIN(size, MAX(MINSIZE, MIN(bp->width, bp->height) / 2)); bp->nbubbles = MI_COUNT(mi); if (bp->nbubbles < -MINBUBBLES) { bp->nbubbles = NRAND(-bp->nbubbles - MINBUBBLES + 1) + MINBUBBLES; } else if (bp->nbubbles < MINBUBBLES) bp->nbubbles = MINBUBBLES; if (bp->bubble != NULL) free(bp->bubble); if ((bp->bubble = (bubbletype *) calloc(bp->nbubbles, sizeof (bubbletype))) == NULL) { free_bubble(display, bp); return; } if (MI_NPIXELS(mi) > 2) bp->color = NRAND(MI_NPIXELS(mi)); if (bp->dbuf != None) XFreePixmap(display, bp->dbuf); if ((bp->dbuf = XCreatePixmap(display, window, bp->width, bp->height, 1)) == None) { free_bubble(display, bp); return; } /* Do not want any exposure events from XCopyPlane */ XSetGraphicsExposures(display, MI_GC(mi), False); gcv.foreground = 1; gcv.background = 0; gcv.function = GXcopy; gcv.graphics_exposures = False; gcv.line_width = 2; if (bp->dbuf_gc) XFreeGC(display, bp->dbuf_gc); if ((bp->dbuf_gc = XCreateGC(display, bp->dbuf, GCForeground | GCBackground | GCLineWidth | GCFunction, &gcv)) == None) { free_bubble(display, bp); return; } MI_CLEARWINDOW(mi); } void draw_bubble(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); bubblestruct *bp; if (bubbles == NULL) return; bp = &bubbles[MI_SCREEN(mi)]; if (bp->bubble == NULL) return; MI_IS_DRAWN(mi) = True; if (MI_NPIXELS(mi) <= 2) XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); else { bp->color = (bp->color + 1) % MI_NPIXELS(mi); XSetForeground(display, gc, MI_PIXEL(mi, bp->color)); } if (bp->dbuf) { XSetForeground(display, bp->dbuf_gc, 0); XFillRectangle(display, bp->dbuf, bp->dbuf_gc, 0, 0, bp->width, bp->height); XSetForeground(display, bp->dbuf_gc, 1); changeBubble(mi); XCopyPlane(display, bp->dbuf, window, gc, 0, 0, bp->width, bp->height, 0, 0, 1); } } void release_bubble(ModeInfo * mi) { if (bubbles != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_bubble(MI_DISPLAY(mi), &bubbles[screen]); free(bubbles); bubbles = (bubblestruct *) NULL; } } void refresh_bubble(ModeInfo * mi) { /* Do nothing, it will refresh by itself */ } #endif /* MODE_bubble */