1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.17 2000/07/19 06:38:42 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"
52 # include <sys/wait.h>
54 # if __DECC_VER >= 50200000
55 # include <sys/wait.h>
72 extern void init_default_bubbles(void);
73 extern int num_default_bubbles;
74 extern char **default_bubbles[];
75 static int drop_bubble( Bubble *bb );
77 char *progclass = "Bubbles";
93 XrmOptionDescRec options [] = {
94 { "-simple", ".simple", XrmoptionNoArg, "true" },
96 { "-broken", ".broken", XrmoptionNoArg, "true" },
98 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
99 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
100 { "-3D", ".3D", XrmoptionNoArg, "true" },
101 { "-delay", ".delay", XrmoptionSepArg, 0 },
102 { "-drop", ".drop", XrmoptionNoArg, "true" },
103 { "-rise", ".rise", XrmoptionNoArg, "true" },
104 { "-trails", ".trails", XrmoptionNoArg, "true" },
112 static Bubble **mesh;
113 static int mesh_length;
114 static int mesh_width;
115 static int mesh_height;
116 static int mesh_cells;
118 static int **adjacent_list;
120 static int screen_width;
121 static int screen_height;
122 static int screen_depth;
123 static unsigned int default_fg_pixel, default_bg_pixel;
125 * I know it's not elegant to save this stuff in global variables
126 * but we need it for the signal handler.
128 static Display *defdsp;
129 static Window defwin;
130 static Colormap defcmap;
131 static Visual *defvisual;
133 /* For simple mode only */
134 static int bubble_min_radius;
135 static int bubble_max_radius;
136 static long *bubble_areas;
137 static int *bubble_droppages;
138 static GC draw_gc, erase_gc;
141 static int num_bubble_pixmaps;
142 static Bubble_Step **step_pixmaps;
143 #endif /* HAVE_XPM */
147 static Bool simple = False;
149 static Bool simple = True;
151 static Bool broken = False;
152 static Bool quiet = False;
153 static Bool threed = False;
154 static Bool drop = False;
155 static Bool trails = False;
160 * To prevent forward references, some stuff is up here
164 calc_bubble_area(int r)
165 /* Calculate the area of a bubble of radius r */
169 10.0 * PI * (double)r * (double)r * (double)r);
172 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
174 return (long)(10.0 * PI * (double)r * (double)r);
183 if ((ret = malloc(size)) == NULL) {
184 fprintf(stderr, "%s: out of memory\n", progname);
192 die_bad_bubble(Bubble *bb)
193 /* This is for use with GDB */
195 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
201 null_bubble(Bubble *bb)
202 /* Returns true if the pointer passed is NULL. If not then this checks to
203 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
204 number is set correctly. This only a sanity check for debugging and is
205 turned off if DEBUG isn't set. */
207 if (bb == (Bubble *)NULL)
210 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
211 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
214 if (bb->magic != BUBBLE_MAGIC) {
215 fprintf(stderr, "Magic = %d\n", bb->magic);
219 if ((bb->x < 0) || (bb->x > screen_width) ||
220 (bb->y < 0) || (bb->y > screen_height) ||
221 (bb->radius < bubble_min_radius) || (bb->radius >
222 bubble_max_radius)) {
224 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
225 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
230 if ((bb->x < 0) || (bb->x > screen_width) ||
231 (bb->y < 0) || (bb->y > screen_height) ||
232 (bb->radius < step_pixmaps[0]->radius) ||
233 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
235 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
236 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
239 #endif /* HAVE_XPM */
247 print_bubble_list(Bubble *bb)
248 /* Print list of where all the bubbles are. For debugging purposes only. */
250 if (! null_bubble(bb)) {
251 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
252 print_bubble_list(bb->next);
258 add_bubble_to_list(Bubble **list, Bubble *bb)
259 /* Take a pointer to a list of bubbles and stick bb at the head of the
262 Bubble *head = *list;
264 if (null_bubble(head)) {
265 bb->prev = (Bubble *)NULL;
266 bb->next = (Bubble *)NULL;
269 bb->prev = (Bubble *)NULL;
283 /* Setup the mesh of bubbles */
287 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
288 for (i = 0; i < mesh_cells; i++)
289 mesh[i] = (Bubble *)NULL;
293 cell_to_mesh(int x, int y)
294 /* convert cell coordinates to mesh index */
297 if ((x < 0) || (y < 0)) {
298 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
302 return ((mesh_width * y) + x);
306 mesh_to_cell(int mi, int *cx, int *cy)
307 /* convert mesh index into cell coordinates */
309 *cx = mi % mesh_width;
310 *cy = mi / mesh_width;
314 pixel_to_mesh(int x, int y)
315 /* convert screen coordinates into mesh index */
317 return cell_to_mesh((x / mesh_length), (y / mesh_length));
321 verify_mesh_index(int x, int y)
322 /* check to see if (x,y) is in the mesh */
324 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
326 return (cell_to_mesh(x, y));
331 print_adjacents(int *adj)
332 /* Print a list of the cells calculated above. For debugging only. */
337 for (i = 0; i < 8; i++)
338 printf("%d ", adj[i]);
344 add_to_mesh(Bubble *bb)
345 /* Add the given bubble to the mesh by sticking it on the front of the
346 list. bb is already allocated so no need to malloc() anything, just
350 if (null_bubble(bb)) {
351 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
356 add_bubble_to_list(&mesh[bb->cell_index], bb);
362 /* Print the contents of the mesh */
366 for (i = 0; i < mesh_cells; i++) {
367 if (! null_bubble(mesh[i])) {
368 printf("Mesh cell %d\n", i);
369 print_bubble_list(mesh[i]);
376 /* Check to see if the mesh is Okay. For debugging only. */
381 for (i = 0; i < mesh_cells; i++) {
383 while (! null_bubble(b))
390 /* Count how many bubbles there are in total. For debugging only. */
396 for (i = 0; i < mesh_cells; i++) {
398 while (! null_bubble(b)) {
409 calculate_adjacent_list (void)
410 /* Calculate the list of cells adjacent to a particular cell for use
416 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
417 for (i = 0; i < mesh_cells; i++) {
418 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
419 mesh_to_cell(i, &ix, &iy);
420 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
421 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
422 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
423 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
424 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
425 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
426 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
427 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
428 adjacent_list[i][8] = i;
434 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
443 maxarea = bubble_areas[bubble_max_radius+1];
445 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
447 maxarea = bubble_areas[bubble_max_radius+1];
448 #endif /* HAVE_XPM */
449 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
450 factor = (long)ceil(maxvalue / (double)LONG_MAX);
452 /* Overflow will occur in weighted_mean(). We must divide areas
453 each by factor so it will never do so. */
456 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
457 bubble_areas[i] /= factor;
458 if (bubble_areas[i] == 0)
462 for (i = 0; i <= num_bubble_pixmaps; i++) {
464 printf("area = %ld", step_pixmaps[i]->area);
466 step_pixmaps[i]->area /= factor;
467 if (step_pixmaps[i]->area == 0)
468 step_pixmaps[i]->area = 1;
470 printf("-> %ld\n", step_pixmaps[i]->area);
475 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
476 bubble_areas[i] /= factor;
477 if (bubble_areas[i] == 0)
480 #endif /* HAVE_XPM */
483 printf("maxarea = %ld\n", maxarea);
484 printf("maxvalue = %g\n", maxvalue);
485 printf("LONG_MAX = %ld\n", LONG_MAX);
486 printf("factor = %ld\n", factor);
496 /* Add a new bubble at some random position on the screen of the smallest
499 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
501 /* Can't use null_bubble() here since magic number hasn't been set */
502 if (rv == (Bubble *)NULL) {
503 fprintf(stderr, "Ran out of memory!\n");
508 rv->radius = bubble_min_radius;
509 rv->area = bubble_areas[bubble_min_radius];
513 rv->radius = step_pixmaps[0]->radius;
514 rv->area = step_pixmaps[0]->area;
515 #endif /* HAVE_XPM */
518 rv->magic = BUBBLE_MAGIC;
519 rv->x = random() % screen_width;
520 rv->y = random() % screen_height;
521 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
527 show_bubble(Bubble *bb)
528 /* paint the bubble on the screen */
530 if (null_bubble(bb)) {
531 fprintf(stderr, "NULL bubble passed to show_bubble\n");
539 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
540 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
544 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
545 (bb->x - bb->radius),
546 (bb->y - bb->radius));
548 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
549 step_pixmaps[bb->step]->draw_gc,
550 0, 0, (bb->radius * 2),
552 (bb->x - bb->radius),
553 (bb->y - bb->radius));
554 #endif /* HAVE_XPM */
560 hide_bubble(Bubble *bb)
561 /* erase the bubble */
563 if (null_bubble(bb)) {
564 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
572 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
573 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
578 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
579 (bb->x - bb->radius), (bb->y - bb->radius));
581 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
582 (bb->x - bb->radius),
583 (bb->y - bb->radius),
587 #endif /* HAVE_XPM */
593 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
594 /* Delete an individual bubble, adjusting list of bubbles around it.
595 If keep_bubble is true then the bubble isn't actually deleted. We
596 use this to allow bubbles to change mesh cells without reallocating,
597 (it needs this when two bubbles collide and the centre position is
598 recalculated, and this may stray over a mesh boundary). */
600 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
601 bb->prev->next = bb->next;
602 bb->next->prev = bb->prev;
603 } else if ((!null_bubble(bb->prev)) &&
604 (null_bubble(bb->next))) {
605 bb->prev->next = (Bubble *)NULL;
606 bb->next = mesh[bb->cell_index];
607 } else if ((null_bubble(bb->prev)) &&
608 (!null_bubble(bb->next))) {
609 bb->next->prev = (Bubble *)NULL;
610 mesh[bb->cell_index] = bb->next;
611 bb->next = mesh[bb->cell_index];
613 /* Only item on list */
614 mesh[bb->cell_index] = (Bubble *)NULL;
622 /* Saves ugly inline code */
624 return ((unsigned long)x * (unsigned long)x);
628 get_closest_bubble(Bubble *bb)
629 /* Find the closest bubble touching the this bubble, NULL if none are
632 Bubble *rv = (Bubble *)NULL;
634 unsigned long separation2, touchdist2;
636 unsigned long closest2 = ULONG_MAX;
640 if (null_bubble(bb)) {
641 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
647 for (i = 0; i < 9; i++) {
648 /* There is a bug here where bb->cell_index is negaitve.. */
650 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
651 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
655 /* printf("%d,", bb->cell_index); */
656 if (adjacent_list[bb->cell_index][i] != -1) {
657 tmp = mesh[adjacent_list[bb->cell_index][i]];
658 while (! null_bubble(tmp)) {
662 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
663 /* Add extra leeway so circles _never_ overlap */
664 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
665 if ((separation2 <= touchdist2) && (separation2 <
668 closest2 = separation2;
687 long_div_round(long num, long dem)
692 if ((num < 0) || (dem < 0)) {
693 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
701 if (moddo > (dem / 2))
705 if ((divvie < 0) || (moddo < 0)) {
706 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
716 weighted_mean(int n1, int n2, long w1, long w2)
717 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
720 if ((w1 <= 0) || (w2 <= 0)) {
722 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
727 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
732 bubble_eat(Bubble *diner, Bubble *food)
733 /* The diner eats the food. Returns true (1) if the diner still exists */
739 if ((null_bubble(diner)) || (null_bubble(food))) {
740 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
745 /* We hide the diner even in the case that it doesn't grow so that
746 if the food overlaps its boundary it is replaced. This could
747 probably be solved by letting bubbles eat others which are close
748 but not quite touching. It's probably worth it, too, since we
749 would then not have to redraw bubbles which don't change in
754 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
755 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
756 newmi = pixel_to_mesh(diner->x, diner->y);
757 diner->area += food->area;
758 delete_bubble_in_mesh(food, DELETE_BUBBLE);
761 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
762 diner->area = bubble_areas[bubble_max_radius];
765 if ((! simple) && (diner->area > step_pixmaps[num_bubble_pixmaps]->area)) {
766 diner->area = step_pixmaps[num_bubble_pixmaps]->area;
768 #endif /* HAVE_XPM */
771 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
772 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
776 if ((! simple) && (diner->area >
777 step_pixmaps[num_bubble_pixmaps]->area)) {
778 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
781 #endif /* HAVE_XPM */
785 if (diner->area > bubble_areas[diner->radius + 1]) {
786 /* Move the bubble to a new radius */
788 while ((i < bubble_max_radius - 1) && (diner->area > bubble_areas[i+1]))
795 if (diner->area > step_pixmaps[diner->step+1]->area) {
797 while ((i < num_bubble_pixmaps - 1) && (diner->area > step_pixmaps[i+1]->area))
800 diner->radius = step_pixmaps[diner->step]->radius;
803 #endif /* HAVE_XPM */
806 /* Now adjust locations and cells if need be */
807 if (newmi != diner->cell_index) {
808 delete_bubble_in_mesh(diner, KEEP_BUBBLE);
809 diner->cell_index = newmi;
817 merge_bubbles(Bubble *b1, Bubble *b2)
818 /* These two bubbles merge into one. If the first one wins out return
819 1 else return 2. If there is no winner (it explodes) then return 0 */
827 if ((null_bubble(b1) || null_bubble(b2))) {
828 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
835 delete_bubble_in_mesh(b1, DELETE_BUBBLE);
839 if (b1size > b2size) {
840 switch (bubble_eat(b1, b2)) {
850 } else if (b1size < b2size) {
851 switch (bubble_eat(b2, b1)) {
862 if ((random() % 2) == 0) {
863 switch (bubble_eat(b1, b2)) {
874 switch (bubble_eat(b2, b1)) {
886 fprintf(stderr, "An error occurred in merge_bubbles()\n");
891 insert_new_bubble(Bubble *tmp)
892 /* Calculates which bubbles are eaten when a new bubble tmp is
893 inserted. This is called recursively in case when a bubble grows
894 it eats others. Careful to pick out disappearing bubbles. */
900 if (null_bubble(tmp)) {
901 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
907 touch = get_closest_bubble(nextbub);
908 if (null_bubble(touch))
913 /* Merge all touching bubbles */
914 while (! null_bubble(touch)) {
915 switch (merge_bubbles(nextbub, touch)) {
917 /* touch ate nextbub and survived */
921 /* nextbub ate touch and survived */
924 /* somebody ate someone else but they exploded */
925 nextbub = (Bubble *)NULL;
928 /* something went wrong */
929 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
933 /* Check to see if any bubble survived. */
934 if (null_bubble(nextbub))
937 /* Check to see if there are any other bubbles still in the area
938 and if we need to do this all over again for them. */
939 touch = get_closest_bubble(nextbub);
942 if (null_bubble(nextbub))
945 /* Shift bubble down. Break if we run off the screen. */
947 if (drop_bubble( nextbub ) == -1)
951 /* Check to see if there are any other bubbles still in the area
952 and if we need to do this all over again for them. */
953 touch = get_closest_bubble(nextbub);
954 if (null_bubble(touch)) {
955 /* We also continue every so often if we're dropping and the bubble is at max size */
958 if ((nextbub->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
963 if ((nextbub->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
966 #endif /* HAVE_XPM */
976 leave_trail( Bubble *bb )
982 tmp->y = bb->y - ((bb->radius + 10) * drop_dir);
983 tmp->cell_index = pixel_to_mesh(tmp->x, tmp->y);
985 insert_new_bubble(tmp);
991 drop_bubble( Bubble *bb )
998 (bb->y) += (bubble_droppages[bb->radius] * drop_dir);
1001 (bb->y) += (step_pixmaps[bb->step]->droppage * drop_dir);
1002 #endif /* HAVE_XPM */
1003 if ((bb->y < 0) || (bb->y > screen_height)) {
1004 delete_bubble_in_mesh( bb, DELETE_BUBBLE );
1010 /* Now adjust locations and cells if need be */
1011 newmi = pixel_to_mesh(bb->x, bb->y);
1012 if (newmi != bb->cell_index) {
1013 delete_bubble_in_mesh(bb, KEEP_BUBBLE);
1014 bb->cell_index = newmi;
1020 if ((bb->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
1025 if ((bb->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
1028 #endif /* HAVE_XPM */
1037 get_length_of_bubble_list(Bubble *bb)
1042 while (! null_bubble(tmp)) {
1052 * Pixmap stuff used regardless of whether file I/O is available. Must
1053 * still check for XPM, though!
1059 * Pixmaps without file I/O (but do have XPM)
1063 pixmap_sort(Bubble_Step **head, int numelems)
1064 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1065 the numelems length array with first element at head into order of radius.
1069 Bubble_Step *least = 0;
1070 int minradius = INT_MAX;
1073 for (i = 0; i < numelems; i++) {
1074 if (head[i]->radius < minradius) {
1076 minradius = head[i]->radius;
1079 if (*head != least) {
1080 memcpy(&tmp, least, sizeof(Bubble_Step));
1081 memcpy(least, *head, sizeof(Bubble_Step));
1082 memcpy(*head, &tmp, sizeof(Bubble_Step));
1086 pixmap_sort(&head[1], numelems-1);
1090 extrapolate(int i1, int i2)
1092 return (i2 + (i2 - i1));
1096 make_pixmap_array(Bubble_Step *list)
1097 /* From a linked list of bubbles construct the array step_pixmaps */
1099 Bubble_Step *tmp = list;
1105 if (list == (Bubble_Step *)NULL) {
1106 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1110 num_bubble_pixmaps = 1;
1111 while(tmp->next != (Bubble_Step *)NULL) {
1113 ++num_bubble_pixmaps;
1116 if (num_bubble_pixmaps < 2) {
1117 fprintf(stderr, "Must be at least two bubbles in file\n");
1121 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1122 sizeof(Bubble_Step *));
1124 /* Copy them blindly into the array for sorting. */
1128 step_pixmaps[ind++] = tmp;
1130 } while(tmp != (Bubble_Step *)NULL);
1132 /* We make another bubble beyond the ones with pixmaps so that the final
1133 bubble hangs around and doesn't pop immediately. It's radius and area
1134 are found by extrapolating from the largest two bubbles with pixmaps. */
1136 step_pixmaps[num_bubble_pixmaps] =
1137 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1138 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1140 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1143 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1144 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1148 step_pixmaps[num_bubble_pixmaps]->radius =
1149 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1150 step_pixmaps[num_bubble_pixmaps-1]->radius);
1151 step_pixmaps[num_bubble_pixmaps]->area =
1152 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1156 /* Now check for correct order */
1157 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1159 if (step_pixmaps[ind]->radius < prevrad) {
1160 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1164 prevrad = step_pixmaps[ind]->radius;
1168 /* Now populate the droppage values */
1169 for (ind = 0; ind < num_bubble_pixmaps; ind++)
1170 step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / num_bubble_pixmaps;
1174 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1175 /* Read pixmap data which has been compiled into the program and a pointer
1176 to which has been passed.
1178 This is virtually copied verbatim from make_pixmap_from_file() above and
1179 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 bl->xpmattrs.valuemask = 0;
1199 bl->xpmattrs.valuemask |= XpmCloseness;
1200 bl->xpmattrs.closeness = 40000;
1203 bl->xpmattrs.valuemask |= XpmVisual;
1204 bl->xpmattrs.visual = defvisual;
1207 bl->xpmattrs.valuemask |= XpmDepth;
1208 bl->xpmattrs.depth = screen_depth;
1211 bl->xpmattrs.valuemask |= XpmColormap;
1212 bl->xpmattrs.colormap = defcmap;
1216 /* This is the only line which is different from make_pixmap_from_file() */
1217 result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
1218 &bl->shape_mask, &bl->xpmattrs);
1222 fprintf(stderr, "xpm: color substitution performed\n");
1225 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1226 bl->area = calc_bubble_area(bl->radius);
1228 case XpmColorFailed:
1229 fprintf(stderr, "xpm: color allocation failed\n");
1232 fprintf(stderr, "xpm: out of memory\n");
1235 fprintf(stderr, "xpm: unknown error code %d\n", result);
1239 gcv.plane_mask = AllPlanes;
1240 gcv.foreground = default_fg_pixel;
1241 gcv.function = GXcopy;
1242 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1243 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1245 gcv.foreground = default_bg_pixel;
1246 gcv.function = GXcopy;
1247 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1248 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1252 default_to_pixmaps (void)
1253 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1256 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1257 Bubble_Step *newpix, *tmppix;
1260 init_default_bubbles();
1262 for (i = 0; i < num_default_bubbles; i++) {
1263 pixpt = default_bubbles[i];
1264 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1265 make_pixmap_from_default(pixpt, newpix);
1266 /* Now add to list */
1267 if (pixmap_list == (Bubble_Step *)NULL) {
1268 pixmap_list = newpix;
1270 tmppix = pixmap_list;
1271 while (tmppix->next != (Bubble_Step *)NULL)
1272 tmppix = tmppix->next;
1273 tmppix->next = newpix;
1275 newpix->next = (Bubble_Step *)NULL;
1278 /* Finally construct step_pixmaps[] */
1279 make_pixmap_array(pixmap_list);
1282 #endif /* HAVE_XPM */
1291 get_resources(Display *dpy, Window window)
1292 /* Get the appropriate X resources and warn about any inconsistencies. */
1295 XWindowAttributes xgwa;
1297 XGetWindowAttributes (dpy, window, &xgwa);
1298 cmap = xgwa.colormap;
1300 threed = get_boolean_resource("3D", "Boolean");
1301 quiet = get_boolean_resource("quiet", "Boolean");
1302 simple = get_boolean_resource("simple", "Boolean");
1303 /* Forbid rendered bubbles on monochrome displays */
1304 if ((mono_p) && (! simple)) {
1307 "Rendered bubbles not supported on monochrome displays\n");
1310 delay = get_integer_resource("delay", "Integer");
1311 nodelay = get_boolean_resource("nodelay", "Boolean");
1317 drop = get_boolean_resource("drop", "Boolean");
1318 rise = get_boolean_resource("rise", "Boolean");
1319 trails = get_boolean_resource("trails", "Boolean");
1321 fprintf( stderr, "Sorry, bubbles can't both drop and rise\n" );
1324 drop_dir = (drop ? 1 : -1);
1328 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1330 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1335 broken = get_boolean_resource("broken", "Boolean");
1338 fprintf(stderr, "-broken not available in simple mode\n");
1343 broken = get_boolean_resource("broken", "Boolean");
1344 #endif /* HAVE_XPM */
1349 init_bubbles (Display *dpy, Window window)
1352 XWindowAttributes xgwa;
1358 get_resources(dpy, window);
1360 XGetWindowAttributes (dpy, window, &xgwa);
1363 printf("sizof(int) on this platform is %d\n", sizeof(int));
1364 printf("sizof(long) on this platform is %d\n", sizeof(long));
1367 screen_width = xgwa.width;
1368 screen_height = xgwa.height;
1369 screen_depth = xgwa.depth;
1370 defcmap = xgwa.colormap;
1371 defvisual = xgwa.visual;
1374 /* These are pretty much plucked out of the air */
1375 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1377 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1379 /* Some trivial values */
1380 if (bubble_min_radius < 1)
1381 bubble_min_radius = 1;
1382 if (bubble_max_radius <= bubble_min_radius)
1383 bubble_max_radius = bubble_min_radius + 1;
1385 mesh_length = (2 * bubble_max_radius) + 3;
1387 /* store area of each bubble of certain radius as number of 1/10s of
1388 a pixel area. PI is defined in <math.h> */
1389 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1390 for (i = 0; i < bubble_min_radius; i++)
1391 bubble_areas[i] = 0;
1392 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1393 bubble_areas[i] = calc_bubble_area(i);
1395 /* Now populate the droppage values */
1396 bubble_droppages = (int *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1397 for (i = 0; i < bubble_min_radius; i++)
1398 bubble_droppages[i] = 0;
1399 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1400 bubble_droppages[i] = MAX_DROPPAGE * (i - bubble_min_radius) / (bubble_max_radius - bubble_min_radius);
1402 mesh_length = (2 * bubble_max_radius) + 3;
1406 "Bug: simple mode code not set but HAVE_XPM not defined\n");
1409 /* Make sure all #ifdef sort of things have been taken care of in
1411 default_to_pixmaps();
1413 /* Set mesh length */
1414 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1415 #endif /* HAVE_XPM */
1417 /* Am I missing something in here??? */
1420 mesh_width = (screen_width / mesh_length) + 1;
1421 mesh_height = (screen_height / mesh_length) + 1;
1422 mesh_cells = mesh_width * mesh_height;
1425 calculate_adjacent_list();
1429 /* Graphics contexts for simple mode */
1431 gcv.foreground = default_fg_pixel;
1432 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1433 gcv.foreground = default_bg_pixel;
1434 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1437 XClearWindow (dpy, window);
1441 bubbles (Display *dpy, Window window)
1447 insert_new_bubble(tmp);
1454 screenhack (Display *dpy, Window window)
1456 init_bubbles (dpy, window);
1458 bubbles (dpy, window);
1459 screenhack_handle_events (dpy);