1 /* bubbles.c - frying pan / soft drink in a glass simulation */
2
3 /*$Id: bubbles.c,v 1.30 2008/07/31 19:27:48 jwz Exp $*/
4
5 /*
6 * Copyright (C) 1995-1996 James Macnicol
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation. No representations are made about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 */
16
17 /*
18 * I got my original inspiration for this by looking at the bottom of a
19 * frying pan while something was cooking and watching the little bubbles
20 * coming off the bottom of the pan as the oil was boiling joining together
21 * to form bigger bubbles and finally to *pop* and disappear. I had some
22 * time on my hands so I wrote this little xscreensaver module to imitate
23 * it. Now that it's done it reminds me more of the bubbles you get in
24 * a glass of fizzy soft drink.....
25 *
26 * The problem seemed to be that the position/size etc. of all the bubbles
27 * on the screen had to be remembered and searched through to find when
28 * bubbles hit each other and combined. To do this more efficiently, the
29 * window/screen is divided up into a square mesh of side length mesh_length
30 * and separate lists of bubbles contained in each cell of the mesh are
31 * kept. Only the cells in the immediate vicinity of the bubble in question
32 * are searched. This should make things more efficient although the whole
33 * thing seems to use up too much CPU, but then I'm using an ancient PC so
34 * perhaps it's not surprising .
35 * (Six months after I wrote the above I now have a Pentium with PCI graphics
36 * and things are _much_ nicer.)
37 *
38 * Author: James Macnicol
39 * Internet E-mail : j-macnicol@adfa.edu.au
40 */
41
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45
46 #undef DEBUG /* doesn't compile */
47
48 #include <math.h>
49 #include <limits.h>
50
51 #ifndef VMS
52 # include <sys/wait.h>
53 #else /* VMS */
54 # if __DECC_VER >= 50200000
55 # include <sys/wait.h>
56 # endif
57 #endif /* VMS */
58
59 #ifdef HAVE_UNISTD_H
60 # include <unistd.h>
61 #endif
62
63 #include "screenhack.h"
64 #include "yarandom.h"
65 #include "bubbles.h"
66 #include "ximage-loader.h"
67
68 #define FANCY_BUBBLES
69
70 /*
71 * Public variables
72 */
73
74 static const char *bubbles_defaults [] = {
75 ".background: black",
76 ".foreground: white",
77 "*fpsSolid: true",
78 "*simple: false",
79 "*broken: false",
80 "*delay: 10000",
81 "*quiet: false",
82 "*mode: float",
83 "*trails: false",
84 "*3D: false",
85 0
86 };
87
88 static XrmOptionDescRec bubbles_options [] = {
89 { "-simple", ".simple", XrmoptionNoArg, "true" },
90 #ifdef FANCY_BUBBLES
91 { "-broken", ".broken", XrmoptionNoArg, "true" },
92 #endif
93 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
94 { "-3D", ".3D", XrmoptionNoArg, "true" },
95 { "-delay", ".delay", XrmoptionSepArg, 0 },
96 { "-mode", ".mode", XrmoptionSepArg, 0 },
97 { "-drop", ".mode", XrmoptionNoArg, "drop" },
98 { "-rise", ".mode", XrmoptionNoArg, "rise" },
99 { "-trails", ".trails", XrmoptionNoArg, "true" },
100 { 0, 0, 0, 0 }
101 };
102
103 /*
104 * Private variables
105 */
106
107 struct state {
108 Display *dpy;
109 Window window;
110
111 Bubble **mesh;
112 int mesh_length;
113 int mesh_width;
114 int mesh_height;
115 int mesh_cells;
116
117 int **adjacent_list;
118
119 int screen_width;
120 int screen_height;
121 int screen_depth;
122 unsigned int default_fg_pixel, default_bg_pixel;
123
124 int bubble_min_radius; /* For simple mode only */
125 int bubble_max_radius;
126 long *bubble_areas;
127 int *bubble_droppages;
128 GC draw_gc, erase_gc;
129
130 #ifdef FANCY_BUBBLES
131 int num_bubble_pixmaps;
132 Bubble_Step **step_pixmaps, **def_list;
133 #endif
134
135 Bool simple;
136 Bool broken;
137 Bool quiet;
138 Bool threed;
139 Bool drop;
140 Bool trails;
141 int drop_dir;
142 int delay;
143 };
144
145 static int drop_bubble( struct state *st, Bubble *bb );
146
147 /*
148 * To prevent forward references, some stuff is up here
149 */
150
151 static long
calc_bubble_area(struct state * st,int r)152 calc_bubble_area(struct state *st, int r)
153 /* Calculate the area of a bubble of radius r */
154 {
155 #ifdef DEBUG
156 printf("%d %g\n", r,
157 10.0 * PI * (double)r * (double)r * (double)r);
158 #endif /* DEBUG */
159 if (st->threed)
160 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
161 else
162 return (long)(10.0 * PI * (double)r * (double)r);
163 }
164
165 static void *
xmalloc(size_t size)166 xmalloc(size_t size)
167 /* Safe malloc */
168 {
169 void *ret;
170
171 if ((ret = malloc(size)) == NULL) {
172 fprintf(stderr, "%s: out of memory\n", progname);
173 exit(1);
174 }
175 return ret;
176 }
177
178 #ifdef DEBUG
179 static void
die_bad_bubble(Bubble * bb)180 die_bad_bubble(Bubble *bb)
181 /* This is for use with GDB */
182 {
183 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
184 exit(1);
185 }
186 #endif
187
188 static int
null_bubble(Bubble * bb)189 null_bubble(Bubble *bb)
190 /* Returns true if the pointer passed is NULL. If not then this checks to
191 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
192 number is set correctly. This only a sanity check for debugging and is
193 turned off if DEBUG isn't set. */
194 {
195 if (bb == (Bubble *)NULL)
196 return 1;
197 #ifdef DEBUG
198 if ((bb->cell_index < 0) || (bb->cell_index > st->mesh_cells)) {
199 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
200 die_bad_bubble(bb);
201 }
202 if (bb->magic != BUBBLE_MAGIC) {
203 fprintf(stderr, "Magic = %d\n", bb->magic);
204 die_bad_bubble(bb);
205 }
206 if (st->simple) {
207 if ((bb->x < 0) || (bb->x > st->screen_width) ||
208 (bb->y < 0) || (bb->y > st->screen_height) ||
209 (bb->radius < st->bubble_min_radius) || (bb->radius >
210 st->bubble_max_radius)) {
211 fprintf(stderr,
212 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
213 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
214 die_bad_bubble(bb);
215 }
216 #ifdef FANCY_BUBBLES
217 } else {
218 if ((bb->x < 0) || (bb->x > st->screen_width) ||
219 (bb->y < 0) || (bb->y > st->screen_height) ||
220 (bb->radius < st->step_pixmaps[0]->radius) ||
221 (bb->radius > st->step_pixmaps[st->num_bubble_pixmaps-1]->radius)) {
222 fprintf(stderr,
223 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
224 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
225 die_bad_bubble(bb);
226 }
227 #endif
228 }
229 #endif /* DEBUG */
230 return 0;
231 }
232
233 #ifdef DEBUG
234 static void
print_bubble_list(Bubble * bb)235 print_bubble_list(Bubble *bb)
236 /* Print list of where all the bubbles are. For debugging purposes only. */
237 {
238 if (! null_bubble(bb)) {
239 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
240 print_bubble_list(bb->next);
241 }
242 }
243 #endif /* DEBUG */
244
245 static void
add_bubble_to_list(Bubble ** list,Bubble * bb)246 add_bubble_to_list(Bubble **list, Bubble *bb)
247 /* Take a pointer to a list of bubbles and stick bb at the head of the
248 list. */
249 {
250 Bubble *head = *list;
251
252 if (null_bubble(head)) {
253 bb->prev = (Bubble *)NULL;
254 bb->next = (Bubble *)NULL;
255 } else {
256 bb->next = head;
257 bb->prev = (Bubble *)NULL;
258 head->prev = bb;
259 }
260 *list = bb;
261 }
262
263
264 /*
265 * Mesh stuff
266 */
267
268
269 static void
init_mesh(struct state * st)270 init_mesh (struct state *st)
271 /* Setup the mesh of bubbles */
272 {
273 int i;
274
275 st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
276 for (i = 0; i < st->mesh_cells; i++)
277 st->mesh[i] = (Bubble *)NULL;
278 }
279
280 static int
cell_to_mesh(struct state * st,int x,int y)281 cell_to_mesh(struct state *st, int x, int y)
282 /* convert cell coordinates to mesh index */
283 {
284 #ifdef DEBUG
285 if ((x < 0) || (y < 0)) {
286 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
287 exit(1);
288 }
289 #endif
290 return ((st->mesh_width * y) + x);
291 }
292
293 static void
mesh_to_cell(struct state * st,int mi,int * cx,int * cy)294 mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
295 /* convert mesh index into cell coordinates */
296 {
297 *cx = mi % st->mesh_width;
298 *cy = mi / st->mesh_width;
299 }
300
301 static int
pixel_to_mesh(struct state * st,int x,int y)302 pixel_to_mesh(struct state *st, int x, int y)
303 /* convert screen coordinates into mesh index */
304 {
305 return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
306 }
307
308 static int
verify_mesh_index(struct state * st,int x,int y)309 verify_mesh_index(struct state *st, int x, int y)
310 /* check to see if (x,y) is in the mesh */
311 {
312 if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
313 return (-1);
314 return (cell_to_mesh(st, x, y));
315 }
316
317 #ifdef DEBUG
318 static void
print_adjacents(int * adj)319 print_adjacents(int *adj)
320 /* Print a list of the cells calculated above. For debugging only. */
321 {
322 int i;
323
324 printf("(");
325 for (i = 0; i < 8; i++)
326 printf("%d ", adj[i]);
327 printf(")\n");
328 }
329 #endif /* DEBUG */
330
331 static void
add_to_mesh(struct state * st,Bubble * bb)332 add_to_mesh(struct state *st, Bubble *bb)
333 /* Add the given bubble to the mesh by sticking it on the front of the
334 list. bb is already allocated so no need to malloc() anything, just
335 adjust pointers. */
336 {
337 #ifdef DEBUG
338 if (null_bubble(bb)) {
339 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
340 exit(1);
341 }
342 #endif /* DEBUG */
343
344 add_bubble_to_list(&st->mesh[bb->cell_index], bb);
345 }
346
347 #ifdef DEBUG
348 static void
print_mesh(struct state * st)349 print_mesh (struct state *st)
350 /* Print the contents of the mesh */
351 {
352 int i;
353
354 for (i = 0; i < st->mesh_cells; i++) {
355 if (! null_bubble(st->mesh[i])) {
356 printf("Mesh cell %d\n", i);
357 print_bubble_list(st->mesh[i]);
358 }
359 }
360 }
361
362 static void
valid_mesh(struct state * st)363 valid_mesh (struct state *st)
364 /* Check to see if the mesh is Okay. For debugging only. */
365 {
366 int i;
367 Bubble *b;
368
369 for (i = 0; i < st->mesh_cells; i++) {
370 b = st->mesh[i];
371 while (! null_bubble(b))
372 b = b->next;
373 }
374 }
375
376 static int
total_bubbles(struct state * st)377 total_bubbles (struct state *st)
378 /* Count how many bubbles there are in total. For debugging only. */
379 {
380 int rv = 0;
381 int i;
382 Bubble *b;
383
384 for (i = 0; i < st->mesh_cells; i++) {
385 b = st->mesh[i];
386 while (! null_bubble(b)) {
387 rv++;
388 b = b->next;
389 }
390 }
391
392 return rv;
393 }
394 #endif /* DEBUG */
395
396 static void
calculate_adjacent_list(struct state * st)397 calculate_adjacent_list (struct state *st)
398 /* Calculate the list of cells adjacent to a particular cell for use
399 later. */
400 {
401 int i;
402 int ix, iy;
403
404 st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
405 for (i = 0; i < st->mesh_cells; i++) {
406 st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
407 mesh_to_cell(st, i, &ix, &iy);
408 st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
409 st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
410 st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
411 st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
412 st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
413 st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
414 st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
415 st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
416 st->adjacent_list[i][8] = i;
417 }
418 }
419
420 static void
adjust_areas(struct state * st)421 adjust_areas (struct state *st)
422 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
423 {
424 double maxvalue;
425 long maxarea;
426 long factor;
427 int i;
428
429 #ifdef FANCY_BUBBLES
430 if (st->simple)
431 maxarea = st->bubble_areas[st->bubble_max_radius+1];
432 else
433 maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
434 #else /* !FANCY_BUBBLES */
435 maxarea = st->bubble_areas[st->bubble_max_radius+1];
436 #endif /* !FANCY_BUBBLES */
437 maxvalue = (double)st->screen_width * 2.0 * (double)maxarea;
438 factor = (long)ceil(maxvalue / (double)LONG_MAX);
439 if (factor > 1) {
440 /* Overflow will occur in weighted_mean(). We must divide areas
441 each by factor so it will never do so. */
442 #ifdef FANCY_BUBBLES
443 if (st->simple) {
444 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
445 st->bubble_areas[i] /= factor;
446 if (st->bubble_areas[i] == 0)
447 st->bubble_areas[i] = 1;
448 }
449 } else {
450 for (i = 0; i <= st->num_bubble_pixmaps; i++) {
451 #ifdef DEBUG
452 printf("area = %ld", st->step_pixmaps[i]->area);
453 #endif /* DEBUG */
454 st->step_pixmaps[i]->area /= factor;
455 if (st->step_pixmaps[i]->area == 0)
456 st->step_pixmaps[i]->area = 1;
457 #ifdef DEBUG
458 printf("-> %ld\n", st->step_pixmaps[i]->area);
459 #endif /* DEBUG */
460 }
461 }
462 #else /* !FANCY_BUBBLES */
463 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
464 st->bubble_areas[i] /= factor;
465 if (st->bubble_areas[i] == 0)
466 st->bubble_areas[i] = 1;
467 }
468 #endif /* !FANCY_BUBBLES */
469 }
470 #ifdef DEBUG
471 printf("maxarea = %ld\n", maxarea);
472 printf("maxvalue = %g\n", maxvalue);
473 printf("LONG_MAX = %ld\n", LONG_MAX);
474 printf("factor = %ld\n", factor);
475 #endif /* DEBUG */
476 }
477
478 /*
479 * Bubbles stuff
480 */
481
482 static Bubble *
new_bubble(struct state * st)483 new_bubble (struct state *st)
484 /* Add a new bubble at some random position on the screen of the smallest
485 size. */
486 {
487 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
488
489 /* Can't use null_bubble() here since magic number hasn't been set */
490 if (rv == (Bubble *)NULL) {
491 fprintf(stderr, "Ran out of memory!\n");
492 exit(1);
493 }
494
495 if (st->simple) {
496 rv->radius = st->bubble_min_radius;
497 rv->area = st->bubble_areas[st->bubble_min_radius];
498 #ifdef FANCY_BUBBLES
499 } else {
500 rv->step = 0;
501 rv->radius = st->step_pixmaps[0]->radius;
502 rv->area = st->step_pixmaps[0]->area;
503 #endif /* FANCY_BUBBLES */
504 }
505 rv->visible = 0;
506 rv->magic = BUBBLE_MAGIC;
507 rv->x = random() % st->screen_width;
508 rv->y = random() % st->screen_height;
509 rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
510
511 return rv;
512 }
513
514 static void
show_bubble(struct state * st,Bubble * bb)515 show_bubble(struct state *st, Bubble *bb)
516 /* paint the bubble on the screen */
517 {
518 if (null_bubble(bb)) {
519 fprintf(stderr, "NULL bubble passed to show_bubble\n");
520 exit(1);
521 }
522
523 if (! bb->visible) {
524 bb->visible = 1;
525
526 if (st->simple) {
527 XDrawArc(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
528 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
529 360*64);
530 } else {
531 #ifdef FANCY_BUBBLES
532 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc,
533 (bb->x - bb->radius),
534 (bb->y - bb->radius));
535
536 XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window,
537 st->step_pixmaps[bb->step]->draw_gc,
538 0, 0, (bb->radius * 2),
539 (bb->radius * 2),
540 (bb->x - bb->radius),
541 (bb->y - bb->radius));
542 #endif /* FANCY_BUBBLES */
543 }
544 }
545 }
546
547 static void
hide_bubble(struct state * st,Bubble * bb)548 hide_bubble(struct state *st, Bubble *bb)
549 /* erase the bubble */
550 {
551 if (null_bubble(bb)) {
552 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
553 exit(1);
554 }
555
556 if (bb->visible) {
557 bb->visible = 0;
558
559 if (st->simple) {
560 XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
561 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
562 360*64);
563 } else {
564 #ifdef FANCY_BUBBLES
565 if (! st->broken) {
566 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc,
567 (bb->x - bb->radius), (bb->y - bb->radius));
568
569 XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
570 (bb->x - bb->radius),
571 (bb->y - bb->radius),
572 (bb->radius * 2),
573 (bb->radius * 2));
574 }
575 #endif /* FANCY_BUBBLES */
576 }
577 }
578 }
579
580 static void
delete_bubble_in_mesh(struct state * st,Bubble * bb,int keep_bubble)581 delete_bubble_in_mesh(struct state *st, Bubble *bb, int keep_bubble)
582 /* Delete an individual bubble, adjusting list of bubbles around it.
583 If keep_bubble is true then the bubble isn't actually deleted. We
584 use this to allow bubbles to change mesh cells without reallocating,
585 (it needs this when two bubbles collide and the centre position is
586 recalculated, and this may stray over a mesh boundary). */
587 {
588 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
589 bb->prev->next = bb->next;
590 bb->next->prev = bb->prev;
591 } else if ((!null_bubble(bb->prev)) &&
592 (null_bubble(bb->next))) {
593 bb->prev->next = (Bubble *)NULL;
594 bb->next = st->mesh[bb->cell_index];
595 } else if ((null_bubble(bb->prev)) &&
596 (!null_bubble(bb->next))) {
597 bb->next->prev = (Bubble *)NULL;
598 st->mesh[bb->cell_index] = bb->next;
599 bb->next = st->mesh[bb->cell_index];
600 } else {
601 /* Only item on list */
602 st->mesh[bb->cell_index] = (Bubble *)NULL;
603 }
604 if (! keep_bubble)
605 free(bb);
606 }
607
608 static unsigned long
ulongsqrint(int x)609 ulongsqrint(int x)
610 /* Saves ugly inline code */
611 {
612 return ((unsigned long)x * (unsigned long)x);
613 }
614
615 static Bubble *
get_closest_bubble(struct state * st,Bubble * bb)616 get_closest_bubble(struct state *st, Bubble *bb)
617 /* Find the closest bubble touching the this bubble, NULL if none are
618 touching. */
619 {
620 Bubble *rv = (Bubble *)NULL;
621 Bubble *tmp;
622 unsigned long separation2, touchdist2;
623 int dx, dy;
624 unsigned long closest2 = ULONG_MAX;
625 int i;
626
627 #ifdef DEBUG
628 if (null_bubble(bb)) {
629 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
630 (int)bb);
631 exit(1);
632 }
633 #endif /* DEBUG */
634
635 for (i = 0; i < 9; i++) {
636 /* There is a bug here where bb->cell_index is negaitve.. */
637 #ifdef DEBUG
638 if ((bb->cell_index < 0) || (bb->cell_index >= st->mesh_cells)) {
639 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
640 exit(1);
641 }
642 #endif /* DEBUG */
643 /* printf("%d,", bb->cell_index); */
644 if (st->adjacent_list[bb->cell_index][i] != -1) {
645 tmp = st->mesh[st->adjacent_list[bb->cell_index][i]];
646 while (! null_bubble(tmp)) {
647 if (tmp != bb) {
648 dx = tmp->x - bb->x;
649 dy = tmp->y - bb->y;
650 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
651 /* Add extra leeway so circles _never_ overlap */
652 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
653 if ((separation2 <= touchdist2) && (separation2 <
654 closest2)) {
655 rv = tmp;
656 closest2 = separation2;
657 }
658 }
659 tmp = tmp->next;
660 }
661 }
662 }
663
664 return rv;
665 }
666
667 #ifdef DEBUG
668 static void
ldr_barf(struct state * st)669 ldr_barf (struct state *st)
670 {
671 }
672 #endif /* DEBUG */
673
674 static long
long_div_round(long num,long dem)675 long_div_round(long num, long dem)
676 {
677 long divvie, moddo;
678
679 #ifdef DEBUG
680 if ((num < 0) || (dem < 0)) {
681 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
682 ldr_barf();
683 exit(1);
684 }
685 #endif /* DEBUG */
686
687 divvie = num / dem;
688 moddo = num % dem;
689 if (moddo > (dem / 2))
690 ++divvie;
691
692 #ifdef DEBUG
693 if ((divvie < 0) || (moddo < 0)) {
694 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
695 ldr_barf();
696 exit(1);
697 }
698 #endif /* DEBUG */
699
700 return divvie;
701 }
702
703 static int
weighted_mean(int n1,int n2,long w1,long w2)704 weighted_mean(int n1, int n2, long w1, long w2)
705 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
706 {
707 #ifdef DEBUG
708 if ((w1 <= 0) || (w2 <= 0)) {
709 fprintf(stderr,
710 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
711 n1, n2, w1, w2);
712 exit(1);
713 }
714 #endif /* DEBUG */
715 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
716 w1 + w2));
717 }
718
719 static int
bubble_eat(struct state * st,Bubble * diner,Bubble * food)720 bubble_eat(struct state *st, Bubble *diner, Bubble *food)
721 /* The diner eats the food. Returns true (1) if the diner still exists */
722 {
723 int i;
724 int newmi;
725
726 #ifdef DEBUG
727 if ((null_bubble(diner)) || (null_bubble(food))) {
728 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
729 exit(1);
730 }
731 #endif /* DEBUG */
732
733 /* We hide the diner even in the case that it doesn't grow so that
734 if the food overlaps its boundary it is replaced. This could
735 probably be solved by letting bubbles eat others which are close
736 but not quite touching. It's probably worth it, too, since we
737 would then not have to redraw bubbles which don't change in
738 size. */
739
740 hide_bubble(st, diner);
741 hide_bubble(st, food);
742 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
743 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
744 newmi = pixel_to_mesh(st, diner->x, diner->y);
745 diner->area += food->area;
746 delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
747
748 if (st->drop) {
749 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
750 diner->area = st->bubble_areas[st->bubble_max_radius];
751 }
752 #ifdef FANCY_BUBBLES
753 if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
754 diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
755 }
756 #endif /* FANCY_BUBBLES */
757 }
758 else {
759 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
760 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
761 return 0;
762 }
763 #ifdef FANCY_BUBBLES
764 if ((! st->simple) && (diner->area >
765 st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
766 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
767 return 0;
768 }
769 #endif /* FANCY_BUBBLES */
770 }
771
772 if (st->simple) {
773 if (diner->area > st->bubble_areas[diner->radius + 1]) {
774 /* Move the bubble to a new radius */
775 i = diner->radius;
776 while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
777 ++i;
778 diner->radius = i;
779 }
780 show_bubble(st, diner);
781 #ifdef FANCY_BUBBLES
782 } else {
783 if (diner->area > st->step_pixmaps[diner->step+1]->area) {
784 i = diner->step;
785 while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
786 ++i;
787 diner->step = i;
788 diner->radius = st->step_pixmaps[diner->step]->radius;
789 }
790 show_bubble(st, diner);
791 #endif /* FANCY_BUBBLES */
792 }
793
794 /* Now adjust locations and cells if need be */
795 if (newmi != diner->cell_index) {
796 delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
797 diner->cell_index = newmi;
798 add_to_mesh(st, diner);
799 }
800
801 return 1;
802 }
803
804 static int
merge_bubbles(struct state * st,Bubble * b1,Bubble * b2)805 merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
806 /* These two bubbles merge into one. If the first one wins out return
807 1 else return 2. If there is no winner (it explodes) then return 0 */
808 {
809 int b1size, b2size;
810
811 b1size = b1->area;
812 b2size = b2->area;
813
814 #ifdef DEBUG
815 if ((null_bubble(b1) || null_bubble(b2))) {
816 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
817 exit(1);
818 }
819 #endif /* DEBUG */
820
821 if (b1 == b2) {
822 hide_bubble(st, b1);
823 delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
824 return 0;
825 }
826
827 if (b1size > b2size) {
828 switch (bubble_eat(st, b1, b2)) {
829 case 0:
830 return 0;
831 break;
832 case 1:
833 return 1;
834 break;
835 default:
836 break;
837 }
838 } else if (b1size < b2size) {
839 switch (bubble_eat(st, b2, b1)) {
840 case 0:
841 return 0;
842 break;
843 case 1:
844 return 2;
845 break;
846 default:
847 break;
848 }
849 } else {
850 if ((random() % 2) == 0) {
851 switch (bubble_eat(st, b1, b2)) {
852 case 0:
853 return 0;
854 break;
855 case 1:
856 return 1;
857 break;
858 default:
859 break;
860 }
861 } else {
862 switch (bubble_eat(st, b2, b1)) {
863 case 0:
864 return 0;
865 break;
866 case 1:
867 return 2;
868 break;
869 default:
870 break;
871 }
872 }
873 }
874 fprintf(stderr, "An error occurred in merge_bubbles()\n");
875 exit(1);
876 }
877
878 static void
insert_new_bubble(struct state * st,Bubble * tmp)879 insert_new_bubble(struct state *st, Bubble *tmp)
880 /* Calculates which bubbles are eaten when a new bubble tmp is
881 inserted. This is called recursively in case when a bubble grows
882 it eats others. Careful to pick out disappearing bubbles. */
883 {
884 Bubble *nextbub;
885 Bubble *touch;
886
887 #ifdef DEBUG
888 if (null_bubble(tmp)) {
889 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
890 exit(1);
891 }
892 #endif /* DEBUG */
893
894 nextbub = tmp;
895 touch = get_closest_bubble(st, nextbub);
896 if (null_bubble(touch))
897 return;
898
899 while (1) {
900
901 /* Merge all touching bubbles */
902 while (! null_bubble(touch)) {
903 switch (merge_bubbles(st, nextbub, touch)) {
904 case 2:
905 /* touch ate nextbub and survived */
906 nextbub = touch;
907 break;
908 case 1:
909 /* nextbub ate touch and survived */
910 break;
911 case 0:
912 /* somebody ate someone else but they exploded */
913 nextbub = (Bubble *)NULL;
914 break;
915 default:
916 /* something went wrong */
917 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
918 exit(1);
919 }
920
921 /* Check to see if any bubble survived. */
922 if (null_bubble(nextbub))
923 break;
924
925 /* Check to see if there are any other bubbles still in the area
926 and if we need to do this all over again for them. */
927 touch = get_closest_bubble(st, nextbub);
928 }
929
930 if (null_bubble(nextbub))
931 break;
932
933 /* Shift bubble down. Break if we run off the screen. */
934 if (st->drop) {
935 if (drop_bubble( st, nextbub ) == -1)
936 break;
937 }
938
939 /* Check to see if there are any other bubbles still in the area
940 and if we need to do this all over again for them. */
941 touch = get_closest_bubble(st, nextbub);
942 if (null_bubble(touch)) {
943 /* We also continue every so often if we're dropping and the bubble is at max size */
944 if (st->drop) {
945 if (st->simple) {
946 if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
947 continue;
948 }
949 #ifdef FANCY_BUBBLES
950 else {
951 if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
952 continue;
953 }
954 #endif /* FANCY_BUBBLES */
955 }
956 break;
957 }
958
959 }
960 }
961
962
963 static void
leave_trail(struct state * st,Bubble * bb)964 leave_trail(struct state *st, Bubble *bb )
965 {
966 Bubble *tmp;
967
968 tmp = new_bubble(st);
969 tmp->x = bb->x;
970 tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
971 tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
972 add_to_mesh(st, tmp);
973 insert_new_bubble(st, tmp);
974 show_bubble( st, tmp );
975 }
976
977
978 static int
drop_bubble(struct state * st,Bubble * bb)979 drop_bubble( struct state *st, Bubble *bb )
980 {
981 int newmi;
982
983 hide_bubble( st, bb );
984
985 if (st->simple)
986 (bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
987 #ifdef FANCY_BUBBLES
988 else
989 (bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
990 #endif /* FANCY_BUBBLES */
991 if ((bb->y < 0) || (bb->y > st->screen_height)) {
992 delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
993 return -1;
994 }
995
996 show_bubble( st, bb );
997
998 /* Now adjust locations and cells if need be */
999 newmi = pixel_to_mesh(st, bb->x, bb->y);
1000 if (newmi != bb->cell_index) {
1001 delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
1002 bb->cell_index = newmi;
1003 add_to_mesh(st, bb);
1004 }
1005
1006 if (st->trails) {
1007 if (st->simple) {
1008 if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
1009 leave_trail( st, bb );
1010 }
1011 #ifdef FANCY_BUBBLES
1012 else {
1013 if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
1014 leave_trail( st, bb );
1015 }
1016 #endif /* FANCY_BUBBLES */
1017 }
1018
1019 return 0;
1020 }
1021
1022
1023 #ifdef DEBUG
1024 static int
get_length_of_bubble_list(Bubble * bb)1025 get_length_of_bubble_list(Bubble *bb)
1026 {
1027 Bubble *tmp = bb;
1028 int rv = 0;
1029
1030 while (! null_bubble(tmp)) {
1031 rv++;
1032 tmp = tmp->next;
1033 }
1034
1035 return rv;
1036 }
1037 #endif /* DEBUG */
1038
1039 /*
1040 * Pixmap stuff used regardless of whether file I/O is available. Must
1041 * still check for XPM, though!
1042 */
1043
1044 #ifdef FANCY_BUBBLES
1045
1046 /*
1047 * Pixmaps without file I/O (but do have XPM)
1048 */
1049
1050 static void
pixmap_sort(Bubble_Step ** head,int numelems)1051 pixmap_sort(Bubble_Step **head, int numelems)
1052 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1053 the numelems length array with first element at head into order of radius.
1054 */
1055 {
1056 Bubble_Step tmp;
1057 Bubble_Step *least = 0;
1058 int minradius = INT_MAX;
1059 int i;
1060
1061 for (i = 0; i < numelems; i++) {
1062 if (head[i]->radius < minradius) {
1063 least = head[i];
1064 minradius = head[i]->radius;
1065 }
1066 }
1067 if (*head != least) {
1068 memcpy(&tmp, least, sizeof(Bubble_Step));
1069 memcpy(least, *head, sizeof(Bubble_Step));
1070 memcpy(*head, &tmp, sizeof(Bubble_Step));
1071 }
1072
1073 if (numelems > 2)
1074 pixmap_sort(&head[1], numelems-1);
1075 }
1076
1077 static int
extrapolate(int i1,int i2)1078 extrapolate(int i1, int i2)
1079 {
1080 return (i2 + (i2 - i1));
1081 }
1082
1083 static void
make_pixmap_array(struct state * st,Bubble_Step * list)1084 make_pixmap_array(struct state *st, Bubble_Step *list)
1085 /* From a linked list of bubbles construct the array step_pixmaps */
1086 {
1087 Bubble_Step *tmp = list;
1088 int ind;
1089 #ifdef DEBUG
1090 int prevrad = -1;
1091 #endif
1092
1093 if (list == (Bubble_Step *)NULL) {
1094 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1095 exit(1);
1096 }
1097
1098 st->num_bubble_pixmaps = 1;
1099 while(tmp->next != (Bubble_Step *)NULL) {
1100 tmp = tmp->next;
1101 ++st->num_bubble_pixmaps;
1102 }
1103
1104 if (st->num_bubble_pixmaps < 2) {
1105 fprintf(stderr, "Must be at least two bubbles in file\n");
1106 exit(1);
1107 }
1108
1109 st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) *
1110 sizeof(Bubble_Step *));
1111
1112 /* Copy them blindly into the array for sorting. */
1113 ind = 0;
1114 tmp = list;
1115 do {
1116 st->step_pixmaps[ind++] = tmp;
1117 tmp = tmp->next;
1118 } while(tmp != (Bubble_Step *)NULL);
1119
1120 /* We make another bubble beyond the ones with pixmaps so that the final
1121 bubble hangs around and doesn't pop immediately. It's radius and area
1122 are found by extrapolating from the largest two bubbles with pixmaps. */
1123
1124 st->step_pixmaps[st->num_bubble_pixmaps] =
1125 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1126 st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
1127
1128 pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
1129
1130 #ifdef DEBUG
1131 if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
1132 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1133 }
1134 #endif /* DEBUG */
1135
1136 st->step_pixmaps[st->num_bubble_pixmaps]->radius =
1137 extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
1138 st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
1139 st->step_pixmaps[st->num_bubble_pixmaps]->area =
1140 calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
1141
1142
1143 #ifdef DEBUG
1144 /* Now check for correct order */
1145 for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
1146 if (prevrad > 0) {
1147 if (st->step_pixmaps[ind]->radius < prevrad) {
1148 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1149 exit(1);
1150 }
1151 }
1152 prevrad = st->step_pixmaps[ind]->radius;
1153 }
1154 #endif /* DEBUG */
1155
1156 /* Now populate the droppage values */
1157 for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
1158 st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
1159 }
1160
1161 static void
make_pixmap_from_default(struct state * st,const unsigned char * png_data,unsigned long data_size,Bubble_Step * bl)1162 make_pixmap_from_default(struct state *st,
1163 const unsigned char *png_data,
1164 unsigned long data_size,
1165 Bubble_Step *bl)
1166 /* Read pixmap data which has been compiled into the program and a pointer
1167 to which has been passed.
1168
1169 This is virtually copied verbatim from make_pixmap_from_file() above and
1170 changes made to either should be propagated onwards! */
1171 {
1172 XGCValues gcv;
1173
1174 #ifdef DEBUG
1175 if (pixmap_data == (char **)0) {
1176 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1177 exit(1);
1178 }
1179 #endif
1180
1181 if (bl == (Bubble_Step *)NULL) {
1182 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1183 exit(1);
1184 }
1185
1186 #ifdef FANCY_BUBBLES
1187 {
1188 int w, h;
1189 bl->ball = image_data_to_pixmap (st->dpy, st->window, png_data, data_size,
1190 &w, &h, &bl->shape_mask);
1191 bl->radius = MAX(w, h) / 2;
1192 bl->area = calc_bubble_area(st, bl->radius);
1193 }
1194 #endif /* FANCY_BUBBLES */
1195
1196 gcv.foreground = st->default_fg_pixel;
1197 gcv.function = GXcopy;
1198 bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1199 XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
1200
1201 gcv.foreground = st->default_bg_pixel;
1202 gcv.function = GXcopy;
1203 bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1204 XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
1205 }
1206
1207 static void
default_to_pixmaps(struct state * st)1208 default_to_pixmaps (struct state *st)
1209 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1210 {
1211 int i;
1212 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1213 Bubble_Step *newpix, *tmppix;
1214
1215 init_default_bubbles();
1216
1217 st->def_list = (Bubble_Step **)xmalloc((num_default_bubbles + 1) *
1218 sizeof(Bubble_Step *));
1219 for (i = 0; i < num_default_bubbles; i++) {
1220 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1221 make_pixmap_from_default(st,
1222 default_bubbles[i].png,
1223 default_bubbles[i].size,
1224 newpix);
1225 /* Now add to list */
1226 st->def_list[i] = newpix;
1227 if (pixmap_list == (Bubble_Step *)NULL) {
1228 pixmap_list = newpix;
1229 } else {
1230 tmppix = pixmap_list;
1231 while (tmppix->next != (Bubble_Step *)NULL)
1232 tmppix = tmppix->next;
1233 tmppix->next = newpix;
1234 }
1235 newpix->next = (Bubble_Step *)NULL;
1236 }
1237
1238 /* Finally construct step_pixmaps[] */
1239 make_pixmap_array(st, pixmap_list);
1240 }
1241
1242 #endif /* FANCY_BUBBLES */
1243
1244
1245 /*
1246 * Main stuff
1247 */
1248
1249
1250 static void
get_resources(struct state * st)1251 get_resources(struct state *st)
1252 /* Get the appropriate X resources and warn about any inconsistencies. */
1253 {
1254 Bool rise;
1255 XWindowAttributes xgwa;
1256 Colormap cmap;
1257 char *s;
1258 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1259 cmap = xgwa.colormap;
1260
1261 st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1262 st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1263 st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1264 /* Forbid rendered bubbles on monochrome displays */
1265 if ((mono_p) && (! st->simple)) {
1266 if (! st->quiet)
1267 fprintf(stderr,
1268 "Rendered bubbles not supported on monochrome displays\n");
1269 st->simple = True;
1270 }
1271 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1272
1273 s = get_string_resource (st->dpy, "mode", "Mode");
1274 rise = False;
1275 if (!s || !*s || !strcasecmp (s, "float"))
1276 ;
1277 else if (!strcasecmp (s, "rise"))
1278 rise = True;
1279 else if (!strcasecmp (s, "drop"))
1280 st->drop = True;
1281 else
1282 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1283
1284 if (s) free (s);
1285
1286 st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1287 st->drop_dir = (st->drop ? 1 : -1);
1288 if (st->drop || rise)
1289 st->drop = 1;
1290
1291 st->default_fg_pixel = get_pixel_resource (st->dpy,
1292 cmap, "foreground", "Foreground");
1293 st->default_bg_pixel = get_pixel_resource (st->dpy,
1294 cmap, "background", "Background");
1295
1296 if (st->simple) {
1297 /* This is easy */
1298 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1299 if (st->broken)
1300 if (! st->quiet)
1301 fprintf(stderr, "-broken not available in simple mode\n");
1302 } else {
1303 #ifndef FANCY_BUBBLES
1304 st->simple = 1;
1305 #else /* FANCY_BUBBLES */
1306 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1307 #endif /* FANCY_BUBBLES */
1308 }
1309 }
1310
1311 static void *
bubbles_init(Display * dpy,Window window)1312 bubbles_init (Display *dpy, Window window)
1313 {
1314 struct state *st = (struct state *) calloc (1, sizeof(*st));
1315 XGCValues gcv;
1316 XWindowAttributes xgwa;
1317 int i;
1318
1319 st->dpy = dpy;
1320 st->window = window;
1321
1322 get_resources(st);
1323
1324 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1325
1326 #ifdef DEBUG
1327 printf("sizof(int) on this platform is %d\n", sizeof(int));
1328 printf("sizof(long) on this platform is %d\n", sizeof(long));
1329 #endif /* DEBUG */
1330
1331 st->screen_width = xgwa.width;
1332 st->screen_height = xgwa.height;
1333 st->screen_depth = xgwa.depth;
1334
1335 if (st->simple) {
1336 /* These are pretty much plucked out of the air */
1337 st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width,
1338 st->screen_height)));
1339 st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1340 st->screen_height)));
1341 /* Some trivial values */
1342 if (st->bubble_min_radius < 1)
1343 st->bubble_min_radius = 1;
1344 if (st->bubble_max_radius <= st->bubble_min_radius)
1345 st->bubble_max_radius = st->bubble_min_radius + 1;
1346
1347 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1348
1349 /* store area of each bubble of certain radius as number of 1/10s of
1350 a pixel area. PI is defined in <math.h> */
1351 st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1352 for (i = 0; i < st->bubble_min_radius; i++)
1353 st->bubble_areas[i] = 0;
1354 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1355 st->bubble_areas[i] = calc_bubble_area(st, i);
1356
1357 /* Now populate the droppage values */
1358 st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1359 for (i = 0; i < st->bubble_min_radius; i++)
1360 st->bubble_droppages[i] = 0;
1361 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1362 st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1363
1364 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1365 } else {
1366 #ifndef FANCY_BUBBLES
1367 fprintf(stderr,
1368 "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1369 exit(1);
1370 #else /* FANCY_BUBBLES */
1371 /* Make sure all #ifdef sort of things have been taken care of in
1372 get_resources(). */
1373 default_to_pixmaps(st);
1374
1375 /* Set mesh length */
1376 st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1377 #endif /* FANCY_BUBBLES */
1378
1379 /* Am I missing something in here??? */
1380 }
1381
1382 st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1383 st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1384 st->mesh_cells = st->mesh_width * st->mesh_height;
1385 init_mesh(st);
1386
1387 calculate_adjacent_list(st);
1388
1389 adjust_areas(st);
1390
1391 /* Graphics contexts for simple mode */
1392 if (st->simple) {
1393 gcv.foreground = st->default_fg_pixel;
1394 st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1395 gcv.foreground = st->default_bg_pixel;
1396 st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1397 }
1398
1399 XClearWindow (st->dpy, st->window);
1400
1401 # ifndef FANCY_BUBBLES
1402 st->simple = True;
1403 # endif
1404
1405 return st;
1406 }
1407
1408 static unsigned long
bubbles_draw(Display * dpy,Window window,void * closure)1409 bubbles_draw (Display *dpy, Window window, void *closure)
1410 {
1411 struct state *st = (struct state *) closure;
1412 int i;
1413 for (i = 0; i < 5; i++)
1414 {
1415 Bubble *tmp = new_bubble(st);
1416 add_to_mesh(st, tmp);
1417 insert_new_bubble(st, tmp);
1418 }
1419 return st->delay;
1420 }
1421
1422
1423 static void
bubbles_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)1424 bubbles_reshape (Display *dpy, Window window, void *closure,
1425 unsigned int w, unsigned int h)
1426 {
1427 }
1428
1429 static Bool
bubbles_event(Display * dpy,Window window,void * closure,XEvent * event)1430 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1431 {
1432 return False;
1433 }
1434
1435 static void
free_bubble(Display * dpy,Bubble_Step * b)1436 free_bubble (Display *dpy, Bubble_Step *b)
1437 {
1438 if (b->ball) XFreePixmap (dpy, b->ball);
1439 if (b->shape_mask) XFreePixmap (dpy, b->shape_mask);
1440 if (b->draw_gc) XFreeGC (dpy, b->draw_gc);
1441 if (b->erase_gc) XFreeGC (dpy, b->erase_gc);
1442 free (b);
1443 }
1444
1445 static void
bubbles_free(Display * dpy,Window window,void * closure)1446 bubbles_free (Display *dpy, Window window, void *closure)
1447 {
1448 struct state *st = (struct state *) closure;
1449 int i;
1450
1451 /* #### The lifetime of objects in this program is fucking incomprehensible.
1452 I give up. */
1453
1454 /* for (i = 0; i < num_default_bubbles; i++)
1455 free_bubble (dpy, st->def_list[i]); */
1456 for (i = 0; i < st->num_bubble_pixmaps; i++)
1457 free_bubble (dpy, st->step_pixmaps[i]);
1458 for (i = 0; i < st->mesh_cells; i++)
1459 free (st->adjacent_list[i]);
1460 /* for (i = 0; i < st->mesh_cells; i++)
1461 free_bubble (dpy, st->mesh[i]); */
1462 if (st->draw_gc) XFreeGC (dpy, st->draw_gc);
1463 if (st->erase_gc) XFreeGC (dpy, st->erase_gc);
1464 free (st->adjacent_list);
1465 free (st->def_list);
1466 free (st->step_pixmaps);
1467 free (st->mesh);
1468 free (st);
1469 }
1470
1471 XSCREENSAVER_MODULE ("Bubbles", bubbles)
1472