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