1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* bubble --- simple exploding bubbles */
3 
4 #if 0
5 static const char sccsid[] = "@(#)bubble.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1998 by Charles Vidal <cvidal@ivsweb.com>
11  *         http://www.chez.com/vidalc
12  *         and David Bagley
13  *
14  * Permission to use, copy, modify, and distribute this software and its
15  * documentation for any purpose and without fee is hereby granted,
16  * provided that the above copyright notice appear in all copies and that
17  * both that copyright notice and this permission notice appear in
18  * supporting documentation.
19  *
20  * This file is provided AS IS with no warranties of any kind.  The author
21  * shall have no liability with respect to the infringement of copyrights,
22  * trade secrets or any patents by this file or any part thereof.  In no
23  * event will the author be liable for any lost revenue or profits or
24  * other special, indirect and consequential damages.
25  *
26  * Revision History:
27  * 01-Nov-2000: Allocation checks
28  * 10-Jan-1998: Written.
29  */
30 
31 #ifdef STANDALONE
32 # define MODE_bubble
33 # define DEFAULTS	"*delay: 100000 \n" \
34 			"*count: 25 \n" \
35 			"*size: 100 \n" \
36 			"*ncolors: 200 \n" \
37 			"*fullrandom: True \n" \
38 
39 # define reshape_bubble 0
40 # define bubble_handle_event 0
41 # define UNIFORM_COLORS
42 # include "xlockmore.h"		/* in xscreensaver distribution */
43 #else /* STANDALONE */
44 # include "xlock.h"		/* in xlockmore distribution */
45 #endif /* STANDALONE */
46 
47 #ifdef MODE_bubble
48 
49 #define DEF_BOIL  "False"
50 static Bool boil;
51 
52 static XrmOptionDescRec opts[] =
53 {
54 	{(char *) "-boil", (char *) ".bubble.boil", XrmoptionNoArg, (caddr_t) "on"},
55 	{(char *) "+boil", (char *) ".bubble.boil", XrmoptionNoArg, (caddr_t) "off"}
56 };
57 static argtype vars[] =
58 {
59 	{(void *) & boil, (char *) "boil", (char *) "Boil", (char *) DEF_BOIL, t_Bool}
60 };
61 static OptionStruct desc[] =
62 {
63 	{(char *) "-/+boil", (char *) "turn on/off boil"}
64 };
65 
66 ENTRYPOINT ModeSpecOpt bubble_opts =
67 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
68 
69 #ifdef USE_MODULES
70 ModStruct   bubble_description =
71 {"bubble", "init_bubble", "draw_bubble", "release_bubble",
72  (char *) NULL, "init_bubble", "free_bubble", &bubble_opts,
73  100000, 25, 1, 100, 64, 0.3, "",
74  "Shows popping bubbles", 0, NULL};
75 
76 #endif
77 
78 typedef struct {
79 	int         x, y, life;
80 } bubbletype;
81 
82 #define MINSIZE 20
83 #define MINBUBBLES 1
84 
85 typedef struct {
86 	int         direction;
87 	int         color;
88 	int         width, height;
89 	int         nbubbles;
90 	Bool        boil;
91 	bubbletype *bubble;
92 	int         d;
93 	Pixmap      dbuf;
94 	GC          dbuf_gc;
95 } bubblestruct;
96 
97 static bubblestruct *bubbles = (bubblestruct *) NULL;
98 
99 static void
updateBubble(ModeInfo * mi,int i)100 updateBubble(ModeInfo * mi, int i)
101 {
102 	bubblestruct *bp = &bubbles[MI_SCREEN(mi)];
103 	int         x, diameter, x4, y4, diameter4;
104 
105 	if (bp->bubble[i].life + 1 > bp->d - NRAND(16) ||
106 	    bp->bubble[i].y < 0) {
107 		bp->bubble[i].life = 0;
108 		return;
109 	}
110 	++bp->bubble[i].life;
111 	diameter = bp->bubble[i].life;
112 	x = bp->bubble[i].x;
113 	if (bp->boil) {
114 		bp->bubble[i].y -= diameter / 2;
115 		x += (int) (cos((double) (diameter +
116 		       bp->bubble[i].x) / (M_PI / 5.0)) * (double) diameter);
117 	}
118 	/* SunOS 4.1.X xnews server may crash without this */
119 	if (diameter < 4) {
120 		XFillRectangle(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc,
121 			    x - diameter / 2, bp->bubble[i].y - diameter / 2,
122 			       diameter, diameter);
123 	} else {
124 		XDrawArc(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc,
125 			 x - diameter / 2, bp->bubble[i].y - diameter / 2,
126 			 diameter, diameter, 0, 23040);
127 	}
128 	diameter4 = diameter / 4;
129 	if (diameter4 > 0) {
130 		x4 = x - diameter4 / 2 +
131 			((bp->direction / 2) ? (-1) : 1) * diameter / 6;
132 		y4 = bp->bubble[i].y - diameter4 / 2 +
133 			((bp->direction % 2) ? 1 : (-1)) * diameter / 6;
134 		/* SunOS 4.1.X xnews server may crash without this */
135 		if (diameter4 < 4) {
136 			XFillRectangle(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc,
137 				       x4, y4, diameter4, diameter4);
138 		} else {
139 			XFillArc(MI_DISPLAY(mi), bp->dbuf, bp->dbuf_gc,
140 				 x4, y4, diameter4, diameter4, 0, 23040);
141 		}
142 	}
143 }
144 
145 static void
changeBubble(ModeInfo * mi)146 changeBubble(ModeInfo * mi)
147 {
148 	bubblestruct *bp = &bubbles[MI_SCREEN(mi)];
149 	int         i;
150 
151 	for (i = 0; i < bp->nbubbles; i++) {
152 		if (bp->bubble[i].life != 0)
153 			updateBubble(mi, i);
154 	}
155 	i = NRAND(bp->nbubbles);
156 	if (bp->bubble[i].life == 0) {
157 		bp->bubble[i].x = NRAND(bp->width);
158 		if (bp->boil)
159 			bp->bubble[i].y = bp->height -
160 				((bp->height >= 16) ? NRAND(bp->height / 16) : 0);
161 		else
162 			bp->bubble[i].y = NRAND(bp->height);
163 		updateBubble(mi, i);
164 	}
165 }
166 
167 static void
free_bubble_screen(Display * display,bubblestruct * bp)168 free_bubble_screen(Display *display, bubblestruct *bp)
169 {
170 	if (bp == NULL) {
171 		return;
172 	}
173 	if (bp->dbuf != None) {
174 		XFreePixmap(display, bp->dbuf);
175 		bp->dbuf = None;
176 	}
177 	if (bp->dbuf_gc != None) {
178 		XFreeGC(display, bp->dbuf_gc);
179 		bp->dbuf_gc = None;
180 	}
181 	if (bp->bubble != NULL) {
182 		free(bp->bubble);
183 		bp->bubble = (bubbletype *) NULL;
184 	}
185 	bp = NULL;
186 }
187 
188 ENTRYPOINT void
free_bubble(ModeInfo * mi)189 free_bubble(ModeInfo * mi)
190 {
191 	free_bubble_screen(MI_DISPLAY(mi), &bubbles[MI_SCREEN(mi)]);
192 }
193 
194 ENTRYPOINT void
init_bubble(ModeInfo * mi)195 init_bubble(ModeInfo * mi)
196 {
197 	bubblestruct *bp;
198 	int         size = MI_SIZE(mi);
199 	Display    *display = MI_DISPLAY(mi);
200 	Window      window = MI_WINDOW(mi);
201 	XGCValues   gcv;
202 
203 	MI_INIT(mi, bubbles);
204 	bp = &bubbles[MI_SCREEN(mi)];
205 
206 	bp->width = MI_WIDTH(mi);
207 	bp->height = MI_HEIGHT(mi);
208 	bp->direction = NRAND(4);
209 	if (MI_IS_FULLRANDOM(mi))
210 		bp->boil = (Bool) (LRAND() & 1);
211 	else
212 		bp->boil = boil;
213 
214 	if (size < -MINSIZE)
215 		bp->d = NRAND(MIN(-size, MAX(MINSIZE,
216 		   MIN(bp->width, bp->height) / 2)) - MINSIZE + 1) + MINSIZE;
217 	else if (size < MINSIZE) {
218 		if (!size)
219 			bp->d = MAX(MINSIZE, MIN(bp->width, bp->height) / 2);
220 		else
221 			bp->d = MINSIZE;
222 	} else
223 		bp->d = MIN(size, MAX(MINSIZE,
224 				      MIN(bp->width, bp->height) / 2));
225 	bp->nbubbles = MI_COUNT(mi);
226 	if (bp->nbubbles < -MINBUBBLES) {
227 		bp->nbubbles = NRAND(-bp->nbubbles - MINBUBBLES + 1) + MINBUBBLES;
228 	} else if (bp->nbubbles < MINBUBBLES)
229 		bp->nbubbles = MINBUBBLES;
230 	if (bp->bubble != NULL)
231 		free(bp->bubble);
232 	if ((bp->bubble = (bubbletype *) calloc(bp->nbubbles,
233 			sizeof (bubbletype))) == NULL) {
234 		free_bubble_screen(display, bp);
235 		return;
236 	}
237 	if (MI_NPIXELS(mi) > 2)
238 		bp->color = NRAND(MI_NPIXELS(mi));
239 
240 	if (bp->dbuf != None)
241 		XFreePixmap(display, bp->dbuf);
242 	if ((bp->dbuf = XCreatePixmap(display, window, bp->width, bp->height,
243 			1)) == None) {
244 		free_bubble_screen(display, bp);
245 		return;
246 	}
247 	/* Do not want any exposure events from XCopyPlane */
248 	XSetGraphicsExposures(display, MI_GC(mi), False);
249 
250 	gcv.foreground = 1;
251 	gcv.background = 0;
252 	gcv.function = GXcopy;
253 	gcv.graphics_exposures = False;
254 	gcv.line_width = 2;
255 	if (bp->dbuf_gc)
256 		XFreeGC(display, bp->dbuf_gc);
257 	if ((bp->dbuf_gc = XCreateGC(display, bp->dbuf,
258 	  		GCForeground | GCBackground | GCLineWidth | GCFunction,
259 			&gcv)) == None) {
260 		free_bubble_screen(display, bp);
261 		return;
262 	}
263 	MI_CLEARWINDOW(mi);
264 }
265 
266 ENTRYPOINT void
draw_bubble(ModeInfo * mi)267 draw_bubble(ModeInfo * mi)
268 {
269 	Display    *display = MI_DISPLAY(mi);
270 	Window      window = MI_WINDOW(mi);
271 	GC          gc = MI_GC(mi);
272 	bubblestruct *bp;
273 
274 	if (bubbles == NULL)
275 		return;
276 	bp = &bubbles[MI_SCREEN(mi)];
277 	if (bp->bubble == NULL)
278 		return;
279 
280 	MI_IS_DRAWN(mi) = True;
281 	if (MI_NPIXELS(mi) <= 2)
282 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
283 	else {
284 		bp->color = (bp->color + 1) % MI_NPIXELS(mi);
285 		XSetForeground(display, gc, MI_PIXEL(mi, bp->color));
286 	}
287 	if (bp->dbuf) {
288 		XSetForeground(display, bp->dbuf_gc, 0);
289 		XFillRectangle(display, bp->dbuf, bp->dbuf_gc,
290 			       0, 0, bp->width, bp->height);
291 		XSetForeground(display, bp->dbuf_gc, 1);
292 		changeBubble(mi);
293 		XCopyPlane(display, bp->dbuf, window, gc,
294 			   0, 0, bp->width, bp->height, 0, 0, 1);
295 	}
296 }
297 
298 ENTRYPOINT void
release_bubble(ModeInfo * mi)299 release_bubble(ModeInfo * mi)
300 {
301 	if (bubbles != NULL) {
302 		int         screen;
303 
304 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
305 			free_bubble_screen(MI_DISPLAY(mi), &bubbles[screen]);
306 		free(bubbles);
307 		bubbles = (bubblestruct *) NULL;
308 	}
309 }
310 
311 XSCREENSAVER_MODULE ("Bubble", bubble)
312 
313 #endif /* MODE_bubble */
314