1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.18 2002/01/17 02:16:04 jwz Exp $*/
6 * Copyright (C) 1995-1996 James Macnicol
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
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.....
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.)
38 * Author: James Macnicol
39 * Internet E-mail : j-macnicol@adfa.edu.au
43 #include "screenhack.h"
51 # include <sys/wait.h>
53 # if __DECC_VER >= 50200000
54 # include <sys/wait.h>
63 #include "xpm-pixmap.h"
65 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
66 # define FANCY_BUBBLES
73 extern void init_default_bubbles(void);
74 extern int num_default_bubbles;
75 extern char **default_bubbles[];
76 static int drop_bubble( Bubble *bb );
78 char *progclass = "Bubbles";
94 XrmOptionDescRec options [] = {
95 { "-simple", ".simple", XrmoptionNoArg, "true" },
97 { "-broken", ".broken", XrmoptionNoArg, "true" },
99 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
100 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
101 { "-3D", ".3D", XrmoptionNoArg, "true" },
102 { "-delay", ".delay", XrmoptionSepArg, 0 },
103 { "-drop", ".drop", XrmoptionNoArg, "true" },
104 { "-rise", ".rise", XrmoptionNoArg, "true" },
105 { "-trails", ".trails", XrmoptionNoArg, "true" },
113 static Bubble **mesh;
114 static int mesh_length;
115 static int mesh_width;
116 static int mesh_height;
117 static int mesh_cells;
119 static int **adjacent_list;
121 static int screen_width;
122 static int screen_height;
123 static int screen_depth;
124 static unsigned int default_fg_pixel, default_bg_pixel;
126 * I know it's not elegant to save this stuff in global variables
127 * but we need it for the signal handler.
129 static Display *defdsp;
130 static Window defwin;
131 static Colormap defcmap;
132 static Visual *defvisual;
134 /* For simple mode only */
135 static int bubble_min_radius;
136 static int bubble_max_radius;
137 static long *bubble_areas;
138 static int *bubble_droppages;
139 static GC draw_gc, erase_gc;
142 static int num_bubble_pixmaps;
143 static Bubble_Step **step_pixmaps;
148 static Bool simple = False;
150 static Bool simple = True;
152 static Bool broken = False;
153 static Bool quiet = False;
154 static Bool threed = False;
155 static Bool drop = False;
156 static Bool trails = False;
161 * To prevent forward references, some stuff is up here
165 calc_bubble_area(int r)
166 /* Calculate the area of a bubble of radius r */
170 10.0 * PI * (double)r * (double)r * (double)r);
173 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
175 return (long)(10.0 * PI * (double)r * (double)r);
184 if ((ret = malloc(size)) == NULL) {
185 fprintf(stderr, "%s: out of memory\n", progname);
193 die_bad_bubble(Bubble *bb)
194 /* This is for use with GDB */
196 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
202 null_bubble(Bubble *bb)
203 /* Returns true if the pointer passed is NULL. If not then this checks to
204 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
205 number is set correctly. This only a sanity check for debugging and is
206 turned off if DEBUG isn't set. */
208 if (bb == (Bubble *)NULL)
211 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
212 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
215 if (bb->magic != BUBBLE_MAGIC) {
216 fprintf(stderr, "Magic = %d\n", bb->magic);
220 if ((bb->x < 0) || (bb->x > screen_width) ||
221 (bb->y < 0) || (bb->y > screen_height) ||
222 (bb->radius < bubble_min_radius) || (bb->radius >
223 bubble_max_radius)) {
225 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
226 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
231 if ((bb->x < 0) || (bb->x > screen_width) ||
232 (bb->y < 0) || (bb->y > screen_height) ||
233 (bb->radius < step_pixmaps[0]->radius) ||
234 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
236 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
237 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
248 print_bubble_list(Bubble *bb)
249 /* Print list of where all the bubbles are. For debugging purposes only. */
251 if (! null_bubble(bb)) {
252 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
253 print_bubble_list(bb->next);
259 add_bubble_to_list(Bubble **list, Bubble *bb)
260 /* Take a pointer to a list of bubbles and stick bb at the head of the
263 Bubble *head = *list;
265 if (null_bubble(head)) {
266 bb->prev = (Bubble *)NULL;
267 bb->next = (Bubble *)NULL;
270 bb->prev = (Bubble *)NULL;
284 /* Setup the mesh of bubbles */
288 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
289 for (i = 0; i < mesh_cells; i++)
290 mesh[i] = (Bubble *)NULL;
294 cell_to_mesh(int x, int y)
295 /* convert cell coordinates to mesh index */
298 if ((x < 0) || (y < 0)) {
299 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
303 return ((mesh_width * y) + x);
307 mesh_to_cell(int mi, int *cx, int *cy)
308 /* convert mesh index into cell coordinates */
310 *cx = mi % mesh_width;
311 *cy = mi / mesh_width;
315 pixel_to_mesh(int x, int y)
316 /* convert screen coordinates into mesh index */
318 return cell_to_mesh((x / mesh_length), (y / mesh_length));
322 verify_mesh_index(int x, int y)
323 /* check to see if (x,y) is in the mesh */
325 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
327 return (cell_to_mesh(x, y));
332 print_adjacents(int *adj)
333 /* Print a list of the cells calculated above. For debugging only. */
338 for (i = 0; i < 8; i++)
339 printf("%d ", adj[i]);
345 add_to_mesh(Bubble *bb)
346 /* Add the given bubble to the mesh by sticking it on the front of the
347 list. bb is already allocated so no need to malloc() anything, just
351 if (null_bubble(bb)) {
352 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
357 add_bubble_to_list(&mesh[bb->cell_index], bb);
363 /* Print the contents of the mesh */
367 for (i = 0; i < mesh_cells; i++) {
368 if (! null_bubble(mesh[i])) {
369 printf("Mesh cell %d\n", i);
370 print_bubble_list(mesh[i]);
377 /* Check to see if the mesh is Okay. For debugging only. */
382 for (i = 0; i < mesh_cells; i++) {
384 while (! null_bubble(b))
391 /* Count how many bubbles there are in total. For debugging only. */
397 for (i = 0; i < mesh_cells; i++) {
399 while (! null_bubble(b)) {
410 calculate_adjacent_list (void)
411 /* Calculate the list of cells adjacent to a particular cell for use
417 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
418 for (i = 0; i < mesh_cells; i++) {
419 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
420 mesh_to_cell(i, &ix, &iy);
421 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
422 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
423 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
424 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
425 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
426 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
427 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
428 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
429 adjacent_list[i][8] = i;
435 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
444 maxarea = bubble_areas[bubble_max_radius+1];
446 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
447 #else /* !FANCY_BUBBLES */
448 maxarea = bubble_areas[bubble_max_radius+1];
449 #endif /* !FANCY_BUBBLES */
450 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
451 factor = (long)ceil(maxvalue / (double)LONG_MAX);
453 /* Overflow will occur in weighted_mean(). We must divide areas
454 each by factor so it will never do so. */
457 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
458 bubble_areas[i] /= factor;
459 if (bubble_areas[i] == 0)
463 for (i = 0; i <= num_bubble_pixmaps; i++) {
465 printf("area = %ld", step_pixmaps[i]->area);
467 step_pixmaps[i]->area /= factor;
468 if (step_pixmaps[i]->area == 0)
469 step_pixmaps[i]->area = 1;
471 printf("-> %ld\n", step_pixmaps[i]->area);
475 #else /* !FANCY_BUBBLES */
476 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
477 bubble_areas[i] /= factor;
478 if (bubble_areas[i] == 0)
481 #endif /* !FANCY_BUBBLES */
484 printf("maxarea = %ld\n", maxarea);
485 printf("maxvalue = %g\n", maxvalue);
486 printf("LONG_MAX = %ld\n", LONG_MAX);
487 printf("factor = %ld\n", factor);
497 /* Add a new bubble at some random position on the screen of the smallest
500 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
502 /* Can't use null_bubble() here since magic number hasn't been set */
503 if (rv == (Bubble *)NULL) {
504 fprintf(stderr, "Ran out of memory!\n");
509 rv->radius = bubble_min_radius;
510 rv->area = bubble_areas[bubble_min_radius];
514 rv->radius = step_pixmaps[0]->radius;
515 rv->area = step_pixmaps[0]->area;
516 #endif /* FANCY_BUBBLES */
519 rv->magic = BUBBLE_MAGIC;
520 rv->x = random() % screen_width;
521 rv->y = random() % screen_height;
522 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
528 show_bubble(Bubble *bb)
529 /* paint the bubble on the screen */
531 if (null_bubble(bb)) {
532 fprintf(stderr, "NULL bubble passed to show_bubble\n");
540 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
541 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
545 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
546 (bb->x - bb->radius),
547 (bb->y - bb->radius));
549 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
550 step_pixmaps[bb->step]->draw_gc,
551 0, 0, (bb->radius * 2),
553 (bb->x - bb->radius),
554 (bb->y - bb->radius));
555 #endif /* FANCY_BUBBLES */
561 hide_bubble(Bubble *bb)
562 /* erase the bubble */
564 if (null_bubble(bb)) {
565 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
573 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
574 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
579 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
580 (bb->x - bb->radius), (bb->y - bb->radius));
582 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
583 (bb->x - bb->radius),
584 (bb->y - bb->radius),
588 #endif /* FANCY_BUBBLES */
594 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
595 /* Delete an individual bubble, adjusting list of bubbles around it.
596 If keep_bubble is true then the bubble isn't actually deleted. We
597 use this to allow bubbles to change mesh cells without reallocating,
598 (it needs this when two bubbles collide and the centre position is
599 recalculated, and this may stray over a mesh boundary). */
601 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
602 bb->prev->next = bb->next;
603 bb->next->prev = bb->prev;
604 } else if ((!null_bubble(bb->prev)) &&
605 (null_bubble(bb->next))) {
606 bb->prev->next = (Bubble *)NULL;
607 bb->next = mesh[bb->cell_index];
608 } else if ((null_bubble(bb->prev)) &&
609 (!null_bubble(bb->next))) {
610 bb->next->prev = (Bubble *)NULL;
611 mesh[bb->cell_index] = bb->next;
612 bb->next = mesh[bb->cell_index];
614 /* Only item on list */
615 mesh[bb->cell_index] = (Bubble *)NULL;
623 /* Saves ugly inline code */
625 return ((unsigned long)x * (unsigned long)x);
629 get_closest_bubble(Bubble *bb)
630 /* Find the closest bubble touching the this bubble, NULL if none are
633 Bubble *rv = (Bubble *)NULL;
635 unsigned long separation2, touchdist2;
637 unsigned long closest2 = ULONG_MAX;
641 if (null_bubble(bb)) {
642 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
648 for (i = 0; i < 9; i++) {
649 /* There is a bug here where bb->cell_index is negaitve.. */
651 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
652 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
656 /* printf("%d,", bb->cell_index); */
657 if (adjacent_list[bb->cell_index][i] != -1) {
658 tmp = mesh[adjacent_list[bb->cell_index][i]];
659 while (! null_bubble(tmp)) {
663 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
664 /* Add extra leeway so circles _never_ overlap */
665 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
666 if ((separation2 <= touchdist2) && (separation2 <
669 closest2 = separation2;
688 long_div_round(long num, long dem)
693 if ((num < 0) || (dem < 0)) {
694 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
702 if (moddo > (dem / 2))
706 if ((divvie < 0) || (moddo < 0)) {
707 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
717 weighted_mean(int n1, int n2, long w1, long w2)
718 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
721 if ((w1 <= 0) || (w2 <= 0)) {
723 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
728 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
733 bubble_eat(Bubble *diner, Bubble *food)
734 /* The diner eats the food. Returns true (1) if the diner still exists */
740 if ((null_bubble(diner)) || (null_bubble(food))) {
741 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
746 /* We hide the diner even in the case that it doesn't grow so that
747 if the food overlaps its boundary it is replaced. This could
748 probably be solved by letting bubbles eat others which are close
749 but not quite touching. It's probably worth it, too, since we
750 would then not have to redraw bubbles which don't change in
755 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
756 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
757 newmi = pixel_to_mesh(diner->x, diner->y);
758 diner->area += food->area;
759 delete_bubble_in_mesh(food, DELETE_BUBBLE);
762 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
763 diner->area = bubble_areas[bubble_max_radius];
766 if ((! simple) && (diner->area > step_pixmaps[num_bubble_pixmaps]->area)) {
767 diner->area = step_pixmaps[num_bubble_pixmaps]->area;
769 #endif /* FANCY_BUBBLES */
772 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
773 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
777 if ((! simple) && (diner->area >
778 step_pixmaps[num_bubble_pixmaps]->area)) {
779 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
782 #endif /* FANCY_BUBBLES */
786 if (diner->area > bubble_areas[diner->radius + 1]) {
787 /* Move the bubble to a new radius */
789 while ((i < bubble_max_radius - 1) && (diner->area > bubble_areas[i+1]))
796 if (diner->area > step_pixmaps[diner->step+1]->area) {
798 while ((i < num_bubble_pixmaps - 1) && (diner->area > step_pixmaps[i+1]->area))
801 diner->radius = step_pixmaps[diner->step]->radius;
804 #endif /* FANCY_BUBBLES */
807 /* Now adjust locations and cells if need be */
808 if (newmi != diner->cell_index) {
809 delete_bubble_in_mesh(diner, KEEP_BUBBLE);
810 diner->cell_index = newmi;
818 merge_bubbles(Bubble *b1, Bubble *b2)
819 /* These two bubbles merge into one. If the first one wins out return
820 1 else return 2. If there is no winner (it explodes) then return 0 */
828 if ((null_bubble(b1) || null_bubble(b2))) {
829 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
836 delete_bubble_in_mesh(b1, DELETE_BUBBLE);
840 if (b1size > b2size) {
841 switch (bubble_eat(b1, b2)) {
851 } else if (b1size < b2size) {
852 switch (bubble_eat(b2, b1)) {
863 if ((random() % 2) == 0) {
864 switch (bubble_eat(b1, b2)) {
875 switch (bubble_eat(b2, b1)) {
887 fprintf(stderr, "An error occurred in merge_bubbles()\n");
892 insert_new_bubble(Bubble *tmp)
893 /* Calculates which bubbles are eaten when a new bubble tmp is
894 inserted. This is called recursively in case when a bubble grows
895 it eats others. Careful to pick out disappearing bubbles. */
901 if (null_bubble(tmp)) {
902 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
908 touch = get_closest_bubble(nextbub);
909 if (null_bubble(touch))
914 /* Merge all touching bubbles */
915 while (! null_bubble(touch)) {
916 switch (merge_bubbles(nextbub, touch)) {
918 /* touch ate nextbub and survived */
922 /* nextbub ate touch and survived */
925 /* somebody ate someone else but they exploded */
926 nextbub = (Bubble *)NULL;
929 /* something went wrong */
930 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
934 /* Check to see if any bubble survived. */
935 if (null_bubble(nextbub))
938 /* Check to see if there are any other bubbles still in the area
939 and if we need to do this all over again for them. */
940 touch = get_closest_bubble(nextbub);
943 if (null_bubble(nextbub))
946 /* Shift bubble down. Break if we run off the screen. */
948 if (drop_bubble( nextbub ) == -1)
952 /* Check to see if there are any other bubbles still in the area
953 and if we need to do this all over again for them. */
954 touch = get_closest_bubble(nextbub);
955 if (null_bubble(touch)) {
956 /* We also continue every so often if we're dropping and the bubble is at max size */
959 if ((nextbub->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
964 if ((nextbub->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
967 #endif /* FANCY_BUBBLES */
977 leave_trail( Bubble *bb )
983 tmp->y = bb->y - ((bb->radius + 10) * drop_dir);
984 tmp->cell_index = pixel_to_mesh(tmp->x, tmp->y);
986 insert_new_bubble(tmp);
992 drop_bubble( Bubble *bb )
999 (bb->y) += (bubble_droppages[bb->radius] * drop_dir);
1000 #ifdef FANCY_BUBBLES
1002 (bb->y) += (step_pixmaps[bb->step]->droppage * drop_dir);
1003 #endif /* FANCY_BUBBLES */
1004 if ((bb->y < 0) || (bb->y > screen_height)) {
1005 delete_bubble_in_mesh( bb, DELETE_BUBBLE );
1011 /* Now adjust locations and cells if need be */
1012 newmi = pixel_to_mesh(bb->x, bb->y);
1013 if (newmi != bb->cell_index) {
1014 delete_bubble_in_mesh(bb, KEEP_BUBBLE);
1015 bb->cell_index = newmi;
1021 if ((bb->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
1024 #ifdef FANCY_BUBBLES
1026 if ((bb->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
1029 #endif /* FANCY_BUBBLES */
1038 get_length_of_bubble_list(Bubble *bb)
1043 while (! null_bubble(tmp)) {
1053 * Pixmap stuff used regardless of whether file I/O is available. Must
1054 * still check for XPM, though!
1057 #ifdef FANCY_BUBBLES
1060 * Pixmaps without file I/O (but do have XPM)
1064 pixmap_sort(Bubble_Step **head, int numelems)
1065 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1066 the numelems length array with first element at head into order of radius.
1070 Bubble_Step *least = 0;
1071 int minradius = INT_MAX;
1074 for (i = 0; i < numelems; i++) {
1075 if (head[i]->radius < minradius) {
1077 minradius = head[i]->radius;
1080 if (*head != least) {
1081 memcpy(&tmp, least, sizeof(Bubble_Step));
1082 memcpy(least, *head, sizeof(Bubble_Step));
1083 memcpy(*head, &tmp, sizeof(Bubble_Step));
1087 pixmap_sort(&head[1], numelems-1);
1091 extrapolate(int i1, int i2)
1093 return (i2 + (i2 - i1));
1097 make_pixmap_array(Bubble_Step *list)
1098 /* From a linked list of bubbles construct the array step_pixmaps */
1100 Bubble_Step *tmp = list;
1106 if (list == (Bubble_Step *)NULL) {
1107 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1111 num_bubble_pixmaps = 1;
1112 while(tmp->next != (Bubble_Step *)NULL) {
1114 ++num_bubble_pixmaps;
1117 if (num_bubble_pixmaps < 2) {
1118 fprintf(stderr, "Must be at least two bubbles in file\n");
1122 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1123 sizeof(Bubble_Step *));
1125 /* Copy them blindly into the array for sorting. */
1129 step_pixmaps[ind++] = tmp;
1131 } while(tmp != (Bubble_Step *)NULL);
1133 /* We make another bubble beyond the ones with pixmaps so that the final
1134 bubble hangs around and doesn't pop immediately. It's radius and area
1135 are found by extrapolating from the largest two bubbles with pixmaps. */
1137 step_pixmaps[num_bubble_pixmaps] =
1138 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1139 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1141 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1144 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1145 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1149 step_pixmaps[num_bubble_pixmaps]->radius =
1150 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1151 step_pixmaps[num_bubble_pixmaps-1]->radius);
1152 step_pixmaps[num_bubble_pixmaps]->area =
1153 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1157 /* Now check for correct order */
1158 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1160 if (step_pixmaps[ind]->radius < prevrad) {
1161 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1165 prevrad = step_pixmaps[ind]->radius;
1169 /* Now populate the droppage values */
1170 for (ind = 0; ind < num_bubble_pixmaps; ind++)
1171 step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / num_bubble_pixmaps;
1175 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1176 /* Read pixmap data which has been compiled into the program and a pointer
1177 to which has been passed.
1179 This is virtually copied verbatim from make_pixmap_from_file() above and
1180 changes made to either should be propagated onwards! */
1185 if (pixmap_data == (char **)0) {
1186 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1191 if (bl == (Bubble_Step *)NULL) {
1192 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1196 #ifdef FANCY_BUBBLES
1199 bl->ball = xpm_data_to_pixmap (defdsp, defwin, (char **) pixmap_data,
1200 &w, &h, &bl->shape_mask);
1201 bl->radius = MAX(w, h) / 2;
1202 bl->area = calc_bubble_area(bl->radius);
1204 #endif /* FANCY_BUBBLES */
1206 gcv.plane_mask = AllPlanes;
1207 gcv.foreground = default_fg_pixel;
1208 gcv.function = GXcopy;
1209 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1210 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1212 gcv.foreground = default_bg_pixel;
1213 gcv.function = GXcopy;
1214 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1215 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1219 default_to_pixmaps (void)
1220 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1223 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1224 Bubble_Step *newpix, *tmppix;
1227 init_default_bubbles();
1229 for (i = 0; i < num_default_bubbles; i++) {
1230 pixpt = default_bubbles[i];
1231 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1232 make_pixmap_from_default(pixpt, newpix);
1233 /* Now add to list */
1234 if (pixmap_list == (Bubble_Step *)NULL) {
1235 pixmap_list = newpix;
1237 tmppix = pixmap_list;
1238 while (tmppix->next != (Bubble_Step *)NULL)
1239 tmppix = tmppix->next;
1240 tmppix->next = newpix;
1242 newpix->next = (Bubble_Step *)NULL;
1245 /* Finally construct step_pixmaps[] */
1246 make_pixmap_array(pixmap_list);
1249 #endif /* FANCY_BUBBLES */
1258 get_resources(Display *dpy, Window window)
1259 /* Get the appropriate X resources and warn about any inconsistencies. */
1262 XWindowAttributes xgwa;
1264 XGetWindowAttributes (dpy, window, &xgwa);
1265 cmap = xgwa.colormap;
1267 threed = get_boolean_resource("3D", "Boolean");
1268 quiet = get_boolean_resource("quiet", "Boolean");
1269 simple = get_boolean_resource("simple", "Boolean");
1270 /* Forbid rendered bubbles on monochrome displays */
1271 if ((mono_p) && (! simple)) {
1274 "Rendered bubbles not supported on monochrome displays\n");
1277 delay = get_integer_resource("delay", "Integer");
1278 nodelay = get_boolean_resource("nodelay", "Boolean");
1284 drop = get_boolean_resource("drop", "Boolean");
1285 rise = get_boolean_resource("rise", "Boolean");
1286 trails = get_boolean_resource("trails", "Boolean");
1288 fprintf( stderr, "Sorry, bubbles can't both drop and rise\n" );
1291 drop_dir = (drop ? 1 : -1);
1295 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1297 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1302 broken = get_boolean_resource("broken", "Boolean");
1305 fprintf(stderr, "-broken not available in simple mode\n");
1307 #ifndef FANCY_BUBBLES
1309 #else /* FANCY_BUBBLES */
1310 broken = get_boolean_resource("broken", "Boolean");
1311 #endif /* FANCY_BUBBLES */
1316 init_bubbles (Display *dpy, Window window)
1319 XWindowAttributes xgwa;
1325 get_resources(dpy, window);
1327 XGetWindowAttributes (dpy, window, &xgwa);
1330 printf("sizof(int) on this platform is %d\n", sizeof(int));
1331 printf("sizof(long) on this platform is %d\n", sizeof(long));
1334 screen_width = xgwa.width;
1335 screen_height = xgwa.height;
1336 screen_depth = xgwa.depth;
1337 defcmap = xgwa.colormap;
1338 defvisual = xgwa.visual;
1341 /* These are pretty much plucked out of the air */
1342 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1344 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1346 /* Some trivial values */
1347 if (bubble_min_radius < 1)
1348 bubble_min_radius = 1;
1349 if (bubble_max_radius <= bubble_min_radius)
1350 bubble_max_radius = bubble_min_radius + 1;
1352 mesh_length = (2 * bubble_max_radius) + 3;
1354 /* store area of each bubble of certain radius as number of 1/10s of
1355 a pixel area. PI is defined in <math.h> */
1356 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1357 for (i = 0; i < bubble_min_radius; i++)
1358 bubble_areas[i] = 0;
1359 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1360 bubble_areas[i] = calc_bubble_area(i);
1362 /* Now populate the droppage values */
1363 bubble_droppages = (int *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1364 for (i = 0; i < bubble_min_radius; i++)
1365 bubble_droppages[i] = 0;
1366 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1367 bubble_droppages[i] = MAX_DROPPAGE * (i - bubble_min_radius) / (bubble_max_radius - bubble_min_radius);
1369 mesh_length = (2 * bubble_max_radius) + 3;
1371 #ifndef FANCY_BUBBLES
1373 "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1375 #else /* FANCY_BUBBLES */
1376 /* Make sure all #ifdef sort of things have been taken care of in
1378 default_to_pixmaps();
1380 /* Set mesh length */
1381 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1382 #endif /* FANCY_BUBBLES */
1384 /* Am I missing something in here??? */
1387 mesh_width = (screen_width / mesh_length) + 1;
1388 mesh_height = (screen_height / mesh_length) + 1;
1389 mesh_cells = mesh_width * mesh_height;
1392 calculate_adjacent_list();
1396 /* Graphics contexts for simple mode */
1398 gcv.foreground = default_fg_pixel;
1399 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1400 gcv.foreground = default_bg_pixel;
1401 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1404 XClearWindow (dpy, window);
1408 bubbles (Display *dpy, Window window)
1414 insert_new_bubble(tmp);
1421 screenhack (Display *dpy, Window window)
1423 init_bubbles (dpy, window);
1425 bubbles (dpy, window);
1426 screenhack_handle_events (dpy);