1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.10 1997/12/03 10:56:13 jwz Exp $*/
6 * Copyright (C) 1995-1996 James Macnicol
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any later
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * I got my original inspiration for this by looking at the bottom of a
21 * frying pan while something was cooking and watching the little bubbles
22 * coming off the bottom of the pan as the oil was boiling joining together
23 * to form bigger bubbles and finally to *pop* and disappear. I had some
24 * time on my hands so I wrote this little xscreensaver module to imitate
25 * it. Now that it's done it reminds me more of the bubbles you get in
26 * a glass of fizzy soft drink.....
28 * The problem seemed to be that the position/size etc. of all the bubbles
29 * on the screen had to be remembered and searched through to find when
30 * bubbles hit each other and combined. To do this more efficiently, the
31 * window/screen is divided up into a square mesh of side length mesh_length
32 * and separate lists of bubbles contained in each cell of the mesh are
33 * kept. Only the cells in the immediate vicinity of the bubble in question
34 * are searched. This should make things more efficient although the whole
35 * thing seems to use up too much CPU, but then I'm using an ancient PC so
36 * perhaps it's not surprising .
37 * (Six months after I wrote the above I now have a Pentium with PCI graphics
38 * and things are _much_ nicer.)
40 * Author: James Macnicol
41 * Internet E-mail : J.Macnicol@student.anu.edu.au
45 #include "screenhack.h"
51 # include <sys/types.h>
52 #endif /* BUBBLES_IO */
56 #ifdef SIGNAL_NONSENSE /* what's this crap doing in here? */
58 #endif /* SIGNAL_NONSENSE */
65 # include <sys/wait.h>
67 # if __DECC_VER >= 50200000
68 # include <sys/wait.h>
85 #ifndef NO_DEFAULT_BUBBLE
86 extern int num_default_bubbles;
87 extern char **default_bubbles[];
88 #endif /* NO_DEFAULT_BUBBLE */
90 char *progclass = "Bubbles";
100 "*directory: (default)",
101 #endif /* BUBBLES_IO */
105 "*geometry: 400x300",
109 XrmOptionDescRec options [] = {
110 { "-simple", ".simple", XrmoptionNoArg, "true" },
112 { "-broken", ".broken", XrmoptionNoArg, "true" },
113 #endif /* HAVE_XPM */
114 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
115 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
116 { "-3D", ".3D", XrmoptionNoArg, "true" },
118 { "-file", ".file", XrmoptionSepArg, 0 },
119 { "-directory", ".directory", XrmoptionSepArg, 0 },
120 #endif /* BUBBLES_IO */
121 { "-delay", ".delay", XrmoptionSepArg, 0 },
129 static Bubble **mesh;
130 static int mesh_length;
131 static int mesh_width;
132 static int mesh_height;
133 static int mesh_cells;
135 static int **adjacent_list;
137 static int screen_width;
138 static int screen_height;
139 static int screen_depth;
140 static unsigned int default_fg_pixel, default_bg_pixel;
142 * I know it's not elegant to save this stuff in global variables
143 * but we need it for the signal handler.
145 static Display *defdsp;
146 static Window defwin;
147 static Colormap defcmap;
148 static Visual *defvisual;
150 /* For simple mode only */
151 static int bubble_min_radius;
152 static int bubble_max_radius;
153 static long *bubble_areas;
154 static GC draw_gc, erase_gc;
157 static int num_bubble_pixmaps;
158 static Bubble_Step **step_pixmaps;
160 static char *pixmap_file;
161 #endif /* BUBBLES_IO */
162 static int use_default_bubble;
163 #endif /* HAVE_XPM */
167 static Bool simple = False;
169 static Bool simple = True;
171 static Bool broken = False;
172 static Bool quiet = False;
173 static Bool threed = False;
177 * To prevent forward references, some stuff is up here
181 calc_bubble_area(int r)
182 /* Calculate the area of a bubble of radius r */
186 10.0 * PI * (double)r * (double)r * (double)r);
189 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
191 return (long)(10.0 * PI * (double)r * (double)r);
200 if ((ret = malloc(size)) == NULL) {
201 fprintf(stderr, "%s: out of memory\n", progname);
209 die_bad_bubble(Bubble *bb)
210 /* This is for use with GDB */
212 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
218 null_bubble(Bubble *bb)
219 /* Returns true if the pointer passed is NULL. If not then this checks to
220 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
221 number is set correctly. This only a sanity check for debugging and is
222 turned off if DEBUG isn't set. */
224 if (bb == (Bubble *)NULL)
227 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
228 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
231 if (bb->magic != BUBBLE_MAGIC) {
232 fprintf(stderr, "Magic = %d\n", bb->magic);
236 if ((bb->x < 0) || (bb->x > screen_width) ||
237 (bb->y < 0) || (bb->y > screen_height) ||
238 (bb->radius < bubble_min_radius) || (bb->radius >
239 bubble_max_radius)) {
241 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
242 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
247 if ((bb->x < 0) || (bb->x > screen_width) ||
248 (bb->y < 0) || (bb->y > screen_height) ||
249 (bb->radius < step_pixmaps[0]->radius) ||
250 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
252 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
253 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
256 #endif /* HAVE_XPM */
264 print_bubble_list(Bubble *bb)
265 /* Print list of where all the bubbles are. For debugging purposes only. */
267 if (! null_bubble(bb)) {
268 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
269 print_bubble_list(bb->next);
275 add_bubble_to_list(Bubble **list, Bubble *bb)
276 /* Take a pointer to a list of bubbles and stick bb at the head of the
279 Bubble *head = *list;
281 if (null_bubble(head)) {
282 bb->prev = (Bubble *)NULL;
283 bb->next = (Bubble *)NULL;
286 bb->prev = (Bubble *)NULL;
300 /* Setup the mesh of bubbles */
304 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
305 for (i = 0; i < mesh_cells; i++)
306 mesh[i] = (Bubble *)NULL;
310 cell_to_mesh(int x, int y)
311 /* convert cell coordinates to mesh index */
314 if ((x < 0) || (y < 0)) {
315 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
319 return ((mesh_width * y) + x);
323 mesh_to_cell(int mi, int *cx, int *cy)
324 /* convert mesh index into cell coordinates */
326 *cx = mi % mesh_width;
327 *cy = mi / mesh_width;
331 pixel_to_mesh(int x, int y)
332 /* convert screen coordinates into mesh index */
334 return cell_to_mesh((x / mesh_length), (y / mesh_length));
338 verify_mesh_index(int x, int y)
339 /* check to see if (x,y) is in the mesh */
341 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
343 return (cell_to_mesh(x, y));
348 print_adjacents(int *adj)
349 /* Print a list of the cells calculated above. For debugging only. */
354 for (i = 0; i < 8; i++)
355 printf("%d ", adj[i]);
361 add_to_mesh(Bubble *bb)
362 /* Add the given bubble to the mesh by sticking it on the front of the
363 list. bb is already allocated so no need to malloc() anything, just
367 if (null_bubble(bb)) {
368 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
373 add_bubble_to_list(&mesh[bb->cell_index], bb);
379 /* Print the contents of the mesh */
383 for (i = 0; i < mesh_cells; i++) {
384 if (! null_bubble(mesh[i])) {
385 printf("Mesh cell %d\n", i);
386 print_bubble_list(mesh[i]);
393 /* Check to see if the mesh is Okay. For debugging only. */
398 for (i = 0; i < mesh_cells; i++) {
400 while (! null_bubble(b))
407 /* Count how many bubbles there are in total. For debugging only. */
413 for (i = 0; i < mesh_cells; i++) {
415 while (! null_bubble(b)) {
426 calculate_adjacent_list (void)
427 /* Calculate the list of cells adjacent to a particular cell for use
433 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
434 for (i = 0; i < mesh_cells; i++) {
435 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
436 mesh_to_cell(i, &ix, &iy);
437 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
438 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
439 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
440 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
441 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
442 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
443 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
444 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
445 adjacent_list[i][8] = i;
451 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
460 maxarea = bubble_areas[bubble_max_radius+1];
462 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
464 maxarea = bubble_areas[bubble_max_radius+1];
465 #endif /* HAVE_XPM */
466 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
467 factor = (long)ceil(maxvalue / (double)LONG_MAX);
469 /* Overflow will occur in weighted_mean(). We must divide areas
470 each by factor so it will never do so. */
473 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
474 bubble_areas[i] /= factor;
475 if (bubble_areas[i] == 0)
479 for (i = 0; i <= num_bubble_pixmaps; i++) {
481 printf("area = %ld", step_pixmaps[i]->area);
483 step_pixmaps[i]->area /= factor;
484 if (step_pixmaps[i]->area == 0)
485 step_pixmaps[i]->area = 1;
487 printf("-> %ld\n", step_pixmaps[i]->area);
492 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
493 bubble_areas[i] /= factor;
494 if (bubble_areas[i] == 0)
497 #endif /* HAVE_XPM */
500 printf("maxarea = %ld\n", maxarea);
501 printf("maxvalue = %g\n", maxvalue);
502 printf("LONG_MAX = %ld\n", LONG_MAX);
503 printf("factor = %ld\n", factor);
513 /* Add a new bubble at some random position on the screen of the smallest
516 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
518 /* Can't use null_bubble() here since magic number hasn't been set */
519 if (rv == (Bubble *)NULL) {
520 fprintf(stderr, "Ran out of memory!\n");
525 rv->radius = bubble_min_radius;
526 rv->area = bubble_areas[bubble_min_radius];
530 rv->radius = step_pixmaps[0]->radius;
531 rv->area = step_pixmaps[0]->area;
532 #endif /* HAVE_XPM */
535 rv->magic = BUBBLE_MAGIC;
536 rv->x = ya_random() % screen_width;
537 rv->y = ya_random() % screen_height;
538 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
544 show_bubble(Bubble *bb)
545 /* paint the bubble on the screen */
547 if (null_bubble(bb)) {
548 fprintf(stderr, "NULL bubble passed to show_bubble\n");
556 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
557 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
561 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
562 (bb->x - bb->radius),
563 (bb->y - bb->radius));
565 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
566 step_pixmaps[bb->step]->draw_gc,
567 0, 0, (bb->radius * 2),
569 (bb->x - bb->radius),
570 (bb->y - bb->radius));
571 #endif /* HAVE_XPM */
577 hide_bubble(Bubble *bb)
578 /* erase the bubble */
580 if (null_bubble(bb)) {
581 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
589 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
590 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
595 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
596 (bb->x - bb->radius), (bb->y - bb->radius));
598 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
599 (bb->x - bb->radius),
600 (bb->y - bb->radius),
604 #endif /* HAVE_XPM */
610 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
611 /* Delete an individual bubble, adjusting list of bubbles around it.
612 If keep_bubble is true then the bubble isn't actually deleted. We
613 use this to allow bubbles to change mesh cells without reallocating,
614 (it needs this when two bubbles collide and the centre position is
615 recalculated, and this may stray over a mesh boundary). */
617 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
618 bb->prev->next = bb->next;
619 bb->next->prev = bb->prev;
620 } else if ((!null_bubble(bb->prev)) &&
621 (null_bubble(bb->next))) {
622 bb->prev->next = (Bubble *)NULL;
623 bb->next = mesh[bb->cell_index];
624 } else if ((null_bubble(bb->prev)) &&
625 (!null_bubble(bb->next))) {
626 bb->next->prev = (Bubble *)NULL;
627 mesh[bb->cell_index] = bb->next;
628 bb->next = mesh[bb->cell_index];
630 /* Only item on list */
631 mesh[bb->cell_index] = (Bubble *)NULL;
639 /* Saves ugly inline code */
641 return ((unsigned long)x * (unsigned long)x);
645 get_closest_bubble(Bubble *bb)
646 /* Find the closest bubble touching the this bubble, NULL if none are
649 Bubble *rv = (Bubble *)NULL;
651 unsigned long separation2, touchdist2;
653 unsigned long closest2 = ULONG_MAX;
657 if (null_bubble(bb)) {
658 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
664 for (i = 0; i < 9; i++) {
665 /* There is a bug here where bb->cell_index is negaitve.. */
667 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
668 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
672 /* printf("%d,", bb->cell_index); */
673 if (adjacent_list[bb->cell_index][i] != -1) {
674 tmp = mesh[adjacent_list[bb->cell_index][i]];
675 while (! null_bubble(tmp)) {
679 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
680 /* Add extra leeway so circles _never_ overlap */
681 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
682 if ((separation2 <= touchdist2) && (separation2 <
685 closest2 = separation2;
704 long_div_round(long num, long dem)
709 if ((num < 0) || (dem < 0)) {
710 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
718 if (moddo > (dem / 2))
722 if ((divvie < 0) || (moddo < 0)) {
723 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
733 weighted_mean(int n1, int n2, long w1, long w2)
734 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
737 if ((w1 <= 0) || (w2 <= 0)) {
739 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
744 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
749 bubble_eat(Bubble *diner, Bubble *food)
750 /* The diner eats the food. Returns true (1) if the diner still exists */
756 if ((null_bubble(diner)) || (null_bubble(food))) {
757 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
762 /* We hide the diner even in the case that it doesn't grow so that
763 if the food overlaps its boundary it is replaced. This could
764 probably be solved by letting bubbles eat others which are close
765 but not quite touching. It's probably worth it, too, since we
766 would then not have to redraw bubbles which don't change in
771 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
772 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
773 newmi = pixel_to_mesh(diner->x, diner->y);
774 diner->area += food->area;
775 delete_bubble_in_mesh(food, DELETE_BUBBLE);
777 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
778 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
782 if ((! simple) && (diner->area >
783 step_pixmaps[num_bubble_pixmaps]->area)) {
784 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
787 #endif /* HAVE_XPM */
790 if (diner->area > bubble_areas[diner->radius + 1]) {
791 /* Move the bubble to a new radius */
793 while (diner->area > bubble_areas[i+1])
800 if (diner->area > step_pixmaps[diner->step+1]->area) {
802 while (diner->area > step_pixmaps[i+1]->area)
805 diner->radius = step_pixmaps[diner->step]->radius;
808 #endif /* HAVE_XPM */
811 /* Now adjust locations and cells if need be */
812 if (newmi != diner->cell_index) {
813 delete_bubble_in_mesh(diner, KEEP_BUBBLE);
814 diner->cell_index = newmi;
822 merge_bubbles(Bubble *b1, Bubble *b2)
823 /* These two bubbles merge into one. If the first one wins out return
824 1 else return 2. If there is no winner (it explodes) then return 0 */
832 if ((null_bubble(b1) || null_bubble(b2))) {
833 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
840 delete_bubble_in_mesh(b1, DELETE_BUBBLE);
844 if (b1size > b2size) {
845 switch (bubble_eat(b1, b2)) {
855 } else if (b1size < b2size) {
856 switch (bubble_eat(b2, b1)) {
867 if ((ya_random() % 2) == 0) {
868 switch (bubble_eat(b1, b2)) {
879 switch (bubble_eat(b2, b1)) {
891 fprintf(stderr, "An error occurred in merge_bubbles()\n");
896 insert_new_bubble(Bubble *tmp)
897 /* Calculates which bubbles are eaten when a new bubble tmp is
898 inserted. This is called recursively in case when a bubble grows
899 it eats others. Careful to pick out disappearing bubbles. */
905 if (null_bubble(tmp)) {
906 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
912 touch = get_closest_bubble(nextbub);
913 while (! null_bubble(touch)) {
914 switch (merge_bubbles(nextbub, touch)) {
916 /* touch ate nextbub and survived */
920 /* nextbub ate touch and survived */
923 /* somebody ate someone else but they exploded */
924 nextbub = (Bubble *)NULL;
927 /* something went wrong */
928 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
931 /* Check to see if there are any other bubbles still in the area
932 and if we need to do this all over again for them. */
933 if (! null_bubble(nextbub))
934 touch = get_closest_bubble(nextbub);
936 touch = (Bubble *)NULL;
942 get_length_of_bubble_list(Bubble *bb)
947 while (! null_bubble(tmp)) {
957 * Pixmap stuff used regardless of whether file I/O is available. Must
958 * still check for XPM, though!
965 /* Free resources associated with XPM */
971 fprintf(stderr, "free_pixmaps() called in simple mode\n");
974 printf("free_pixmaps()\n");
977 for(i = 0; i < (num_bubble_pixmaps - 1); i++) {
978 XFreePixmap(defdsp, step_pixmaps[i]->ball);
979 XFreePixmap(defdsp, step_pixmaps[i]->shape_mask);
980 XFreeGC(defdsp, step_pixmaps[i]->draw_gc);
981 XFreeGC(defdsp, step_pixmaps[i]->erase_gc);
982 XFreeColors(defdsp, defcmap, step_pixmaps[i]->xpmattrs.pixels,
983 step_pixmaps[i]->xpmattrs.npixels, 0);
984 XpmFreeAttributes(&step_pixmaps[i]->xpmattrs);
988 #ifdef SIGNAL_NONSENSE
991 /* This gets called when SIGINT or SIGTERM is received */
1000 /* Called when SEGV detected. Hmmmmm.... */
1003 fprintf(stderr, "SEGV detected! : %d\n", a);
1007 #endif /* SIGNAL_NONSENSE */
1011 * Pixmaps without file I/O (but do have XPM)
1015 pixmap_sort(Bubble_Step **head, int numelems)
1016 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1017 the numelems length array with first element at head into order of radius.
1021 Bubble_Step *least = 0;
1022 int minradius = INT_MAX;
1025 for (i = 0; i < numelems; i++) {
1026 if (head[i]->radius < minradius) {
1028 minradius = head[i]->radius;
1031 if (*head != least) {
1032 memcpy(&tmp, least, sizeof(Bubble_Step));
1033 memcpy(least, *head, sizeof(Bubble_Step));
1034 memcpy(*head, &tmp, sizeof(Bubble_Step));
1038 pixmap_sort(&head[1], numelems-1);
1042 extrapolate(int i1, int i2)
1044 return (i2 + (i2 - i1));
1048 make_pixmap_array(Bubble_Step *list)
1049 /* From a linked list of bubbles construct the array step_pixmaps */
1051 Bubble_Step *tmp = list;
1057 if (list == (Bubble_Step *)NULL) {
1058 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1062 num_bubble_pixmaps = 1;
1063 while(tmp->next != (Bubble_Step *)NULL) {
1065 ++num_bubble_pixmaps;
1068 if (num_bubble_pixmaps < 2) {
1069 fprintf(stderr, "Must be at least two bubbles in file\n");
1073 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1074 sizeof(Bubble_Step *));
1076 /* Copy them blindly into the array for sorting. */
1080 step_pixmaps[ind++] = tmp;
1082 } while(tmp != (Bubble_Step *)NULL);
1084 /* We make another bubble beyond the ones with pixmaps so that the final
1085 bubble hangs around and doesn't pop immediately. It's radius and area
1086 are found by extrapolating from the largest two bubbles with pixmaps. */
1088 step_pixmaps[num_bubble_pixmaps] =
1089 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1090 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1092 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1095 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1096 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1100 step_pixmaps[num_bubble_pixmaps]->radius =
1101 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1102 step_pixmaps[num_bubble_pixmaps-1]->radius);
1103 step_pixmaps[num_bubble_pixmaps]->area =
1104 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1108 /* Now check for correct order */
1109 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1111 if (step_pixmaps[ind]->radius < prevrad) {
1112 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1116 prevrad = step_pixmaps[ind]->radius;
1121 #ifndef NO_DEFAULT_BUBBLE
1123 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1124 /* Read pixmap data which has been compiled into the program and a pointer
1125 to which has been passed.
1127 This is virtually copied verbatim from make_pixmap_from_file() above and
1128 changes made to either should be propagated onwards! */
1134 if (pixmap_data == (char **)0) {
1135 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1140 if (bl == (Bubble_Step *)NULL) {
1141 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1145 bl->xpmattrs.valuemask = 0;
1148 bl->xpmattrs.valuemask |= XpmCloseness;
1149 bl->xpmattrs.closeness = 40000;
1152 bl->xpmattrs.valuemask |= XpmVisual;
1153 bl->xpmattrs.visual = defvisual;
1156 bl->xpmattrs.valuemask |= XpmDepth;
1157 bl->xpmattrs.depth = screen_depth;
1160 bl->xpmattrs.valuemask |= XpmColormap;
1161 bl->xpmattrs.colormap = defcmap;
1165 /* This is the only line which is different from make_pixmap_from_file() */
1166 result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
1167 &bl->shape_mask, &bl->xpmattrs);
1171 fprintf(stderr, "xpm: color substitution performed\n");
1174 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1175 bl->area = calc_bubble_area(bl->radius);
1177 case XpmColorFailed:
1178 fprintf(stderr, "xpm: color allocation failed\n");
1181 fprintf(stderr, "xpm: out of memory\n");
1184 fprintf(stderr, "xpm: unknown error code %d\n", result);
1188 gcv.plane_mask = AllPlanes;
1189 gcv.foreground = default_fg_pixel;
1190 gcv.function = GXcopy;
1191 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1192 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1194 gcv.foreground = default_bg_pixel;
1195 gcv.function = GXcopy;
1196 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1197 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1201 default_to_pixmaps (void)
1202 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1205 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1206 Bubble_Step *newpix, *tmppix;
1209 /* Make sure pixmaps are freed when program is terminated */
1210 /* This is when I hit ^C */
1211 #ifdef SIGNAL_NONSENSE
1212 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1213 signal(SIGINT, onintr);
1214 /* xscreensaver sends SIGTERM */
1215 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1216 signal(SIGTERM, onintr);
1218 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
1219 printf("Setting signal handler for SIGSEGV\n");
1220 signal(SIGSEGV, onsegv);
1222 printf("Didn't set signal hanlder for SIGSEGV\n");
1225 #endif /* SIGNAL_NONSENSE */
1227 for (i = 0; i < num_default_bubbles; i++) {
1228 pixpt = default_bubbles[i];
1229 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1230 make_pixmap_from_default(pixpt, newpix);
1231 /* Now add to list */
1232 if (pixmap_list == (Bubble_Step *)NULL) {
1233 pixmap_list = newpix;
1235 tmppix = pixmap_list;
1236 while (tmppix->next != (Bubble_Step *)NULL)
1237 tmppix = tmppix->next;
1238 tmppix->next = newpix;
1240 newpix->next = (Bubble_Step *)NULL;
1243 /* Finally construct step_pixmaps[] */
1244 make_pixmap_array(pixmap_list);
1247 #endif /* NO_DEFAULT_BUBBLE */
1249 #endif /* HAVE_XPM */
1258 my_opendir(char *name)
1259 /* Like opendir() but checks for things so we don't have to do it multiple
1260 times in the code. */
1264 if (name == (char *)NULL) {
1265 fprintf(stderr, "NULL directory name\n");
1269 if ((rv = opendir(name)) == NULL) {
1278 regular_file(char *name)
1279 /* Check to see if we can use the named file. This was broken under Linux
1280 1.3.45 but seems to be okay under 1.3.54. The parameter "name" was being
1281 trashed if the file didn't exist. Yeah, I know 1.3.x are development
1287 if ((fd = open(name, O_RDONLY)) == -1) {
1297 get_random_name(char *dir)
1298 /* Pick an appropriate file at random out of the files in the directory dir */
1305 char buf[PATH_BUF_SIZE];
1308 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1309 return (char *)NULL;
1311 while ((dp = readdir(dfd)) != NULL) {
1312 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1314 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1315 fprintf(stderr, "name %s/%s too long\n", dir, DIRENT_NAME);
1318 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1319 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1322 if (regular_file(buf))
1326 if (numentries == 0) {
1327 fprintf(stderr, "No suitable files found in %s\n", dir);
1328 return (char *)NULL;
1330 entnum = ya_random() % numentries;
1333 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1334 return (char *)NULL;
1335 while ((dp = readdir(dfd)) != NULL) {
1336 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1338 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1339 /* We warned about this previously */
1342 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1343 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1346 if (regular_file(buf)) {
1348 rv = (char *)xmalloc(1024 * sizeof(char));
1356 /* We've screwed up if we reach here - someone must have deleted all the
1357 files while we were counting them... */
1358 fprintf(stderr, "get_random_name(): Oops!\n");
1363 read_line(int fd, char **buf, int bufsize)
1364 /* A line is read from fd until a '\n' is found or EOF is reached. (*buf)
1365 is initially of length bufsize and is extended by bufsize chars if need
1366 be (for as many times as it takes). */
1375 rv = read(fd, &x, 1);
1377 perror("read_line(): ");
1379 } else if (rv == 0) {
1382 } else if (x == '\n') {
1387 if (pos == (size - 1)) {
1388 /* We've come to the end of the space */
1389 newbuf = (char *)xmalloc((size+bufsize) * sizeof(char));
1390 strncpy(newbuf, *buf, (size - 1));
1400 create_temp_file(char **name)
1401 /* Create a temporary file in /tmp and return a filedescriptor to it */
1405 if (*name != (char *)NULL)
1408 if ((*name = tempnam("/tmp", "abxdfes")) == (char *)NULL) {
1409 fprintf(stderr, "Couldn't make new temporary file\n");
1412 /* printf("Temp file created : %s\n", *name); */
1413 if ((rv = creat(*name, 0644)) == -1) {
1414 fprintf(stderr, "Couldn't open temporary file\n");
1424 make_pixmap_from_file(char *fname, Bubble_Step *bl)
1425 /* Read the pixmap in file fname into structure bl which must already
1431 if (bl == (Bubble_Step *)NULL) {
1432 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1436 bl->xpmattrs.closeness = 40000;
1437 bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
1438 bl->xpmattrs.colormap = defcmap;
1440 result = XpmReadFileToPixmap(defdsp, defwin, fname, &bl->ball,
1441 &bl->shape_mask, &bl->xpmattrs);
1445 fprintf(stderr, "xpm: color substitution performed\n");
1448 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1449 bl->area = calc_bubble_area(bl->radius);
1451 case XpmColorFailed:
1452 fprintf(stderr, "xpm: color allocation failed\n");
1455 fprintf(stderr, "xpm: out of memory\n");
1458 fprintf(stderr, "xpm: unknown error code %d\n", result);
1462 gcv.plane_mask = AllPlanes;
1463 gcv.foreground = default_fg_pixel;
1464 gcv.function = GXcopy;
1465 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1466 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1468 gcv.foreground = default_bg_pixel;
1469 gcv.function = GXcopy;
1470 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1471 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1473 #endif /* BUBBLES_IO */
1476 read_file_to_pixmaps(char *fname)
1477 /* Read the pixmaps contained in the file fname into memory. THESE SHOULD
1478 BE UNCOMPRESSED AND READY TO GO! */
1480 int fd, tmpfd=0, rv;
1483 char *buf = (char *)NULL;
1484 char *tmpname = (char *)NULL;
1485 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1486 Bubble_Step *newpix, *tmppix;
1488 /* We first create a linked list of pixmaps before allocating
1489 memory for the array */
1491 if ((fd = open(fname, O_RDONLY)) == -1) {
1492 fprintf(stderr, "Couldn't open %s\n", fname);
1496 #ifdef SIGNAL_NONSENSE
1497 /* Make sure pixmaps are freed when program is terminated */
1498 /* This is when I hit ^C */
1499 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1500 signal(SIGINT, onintr);
1501 /* xscreensaver sends SIGTERM */
1502 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1503 signal(SIGTERM, onintr);
1505 if (signal(SIGSEGV, SIGN_IGN) != SIG_IGN)
1506 signal(SIGSEGV, onsegv);
1508 #endif /* SIGNAL_NONSENSE */
1514 buf = (char *)malloc(READ_LINE_BUF_SIZE * sizeof(char));
1516 switch ((rv = read_line(fd, &buf, READ_LINE_BUF_SIZE))) {
1518 fprintf(stderr, "An I/O error occurred\n");
1522 fprintf(stderr, "EOF occurred inside an XPM block\n");
1529 if (strncmp("};", buf, 2) == 0) {
1531 write(tmpfd, buf, strlen(buf));
1532 write(tmpfd, "\n", 1);
1534 /* Now process the tmpfile */
1535 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1536 make_pixmap_from_file(tmpname, newpix);
1537 /* Now add to list */
1538 if (pixmap_list == (Bubble_Step *)NULL) {
1539 pixmap_list = newpix;
1541 tmppix = pixmap_list;
1542 while (tmppix->next != (Bubble_Step *)NULL)
1543 tmppix = tmppix->next;
1544 tmppix->next = newpix;
1546 newpix->next = (Bubble_Step *)NULL;
1549 write(tmpfd, buf, strlen(buf));
1550 write(tmpfd, "\n", 1);
1553 if (strncmp("/* XPM */", buf, 9) == 0) {
1554 tmpfd = create_temp_file(&tmpname);
1555 /* This proves XPM's performance is kinda pathetic */
1557 printf("New XPM detected : %s, fd=%d\n", tmpname, tmpfd);
1562 write(tmpfd, buf, strlen(buf));
1563 write(tmpfd, "\n", 1);
1567 fprintf(stderr, "read_line returned unknown code %d\n", rv);
1575 if (buf != (char *)NULL)
1577 if (tmpname != (char *)NULL)
1581 fprintf(stderr, "There was no XPM data in the file %s\n", fname);
1585 /* Finally construct step_pixmaps[] */
1586 make_pixmap_array(pixmap_list);
1590 shell_exec(char *command)
1591 /* Forks a shell to execute "command" then waits for command to finish */
1593 int pid, status, wval;
1595 switch(pid=fork()) {
1597 if (execlp(BOURNESH, BOURNESH, "-c", command, (char *)NULL) == -1) {
1598 fprintf(stderr, "Couldn't exec shell %s\n", BOURNESH);
1601 /* fall through if execlp() fails */
1607 while ((wval = wait(&status)) != pid)
1616 uncompress_file(char *current, char *namebuf)
1617 /* If the file current is compressed (i.e. its name ends in .gz or .Z,
1618 no check is made to see if it is actually a compressed file...) then a
1619 new temporary file is created for it and it is decompressed into there,
1620 returning the name of the file to namebuf, else current is returned in
1624 char *tname = (char *)NULL;
1625 char argbuf[COMMAND_BUF_SIZE];
1627 if (((strlen(current) >=4) &&
1628 (strncmp(¤t[strlen(current)-3], ".gz", 3) == 0)) ||
1629 ((strlen(current) >=3) &&
1630 (strncmp(¤t[strlen(current)-2], ".Z", 2) == 0))) {
1631 fd = create_temp_file(&tname);
1632 /* close immediately but don't unlink so we should have a zero length
1633 file in /tmp which we can append to */
1635 if (sprintf(argbuf, "%s -dc %s > %s", GZIP, current, tname) >
1636 (COMMAND_BUF_SIZE-1)) {
1637 fprintf(stderr, "command buffer overflowed in uncompress_file()\n");
1641 strcpy(namebuf, tname);
1643 strcpy(namebuf, current);
1648 #endif /* BUBBLES_IO */
1656 get_resources(Display *dpy, Window window)
1657 /* Get the appropriate X resources and warn about any inconsistencies. */
1665 #endif /* HAVE_XPM */
1666 #endif /* BUBBLES_IO */
1668 XWindowAttributes xgwa;
1670 XGetWindowAttributes (dpy, window, &xgwa);
1671 cmap = xgwa.colormap;
1673 threed = get_boolean_resource("3D", "Boolean");
1674 quiet = get_boolean_resource("quiet", "Boolean");
1675 simple = get_boolean_resource("simple", "Boolean");
1676 /* Forbid rendered bubbles on monochrome displays */
1677 if ((mono_p) && (! simple)) {
1680 "Rendered bubbles not supported on monochrome displays\n");
1683 delay = get_integer_resource("delay", "Integer");
1684 nodelay = get_boolean_resource("nodelay", "Boolean");
1690 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1692 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1697 broken = get_boolean_resource("broken", "Boolean");
1700 fprintf(stderr, "-broken not available in simple mode\n");
1705 broken = get_boolean_resource("broken", "Boolean");
1707 pixmap_file = get_string_resource("file", "File");
1708 dirname = get_string_resource("directory", "Directory");
1709 #ifdef NO_DEFAULT_BUBBLE
1710 /* Must specify -file or -directory if no default bubble compiled in */
1711 if (strcmp(pixmap_file, "(default)") != 0) {
1712 } else if (strcmp(dirname, "(default)") != 0) {
1713 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1714 /* Die if we can't open directory - make it consistent with -file
1715 when it fails, rather than falling back to default. */
1720 "No default bubble compiled in - use -file or -directory\n");
1724 if (strcmp(pixmap_file, "(default)") != 0) {
1725 } else if (strcmp(dirname, "(default)") != 0) {
1726 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1730 /* Use default bubble */
1731 use_default_bubble = 1;
1733 #endif /* NO_DEFAULT_BUBBLE */
1735 use_default_bubble = 1;
1736 #endif /* BUBBLES_IO */
1737 #endif /* HAVE_XPM */
1742 init_bubbles (Display *dpy, Window window)
1745 XWindowAttributes xgwa;
1748 char uncompressed[1024];
1749 #endif /* BUBBLES_IO */
1756 get_resources(dpy, window);
1758 XGetWindowAttributes (dpy, window, &xgwa);
1761 printf("sizof(int) on this platform is %d\n", sizeof(int));
1762 printf("sizof(long) on this platform is %d\n", sizeof(long));
1765 screen_width = xgwa.width;
1766 screen_height = xgwa.height;
1767 screen_depth = xgwa.depth;
1768 defcmap = xgwa.colormap;
1769 defvisual = xgwa.visual;
1772 /* These are pretty much plucked out of the air */
1773 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1775 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1777 /* Some trivial values */
1778 if (bubble_min_radius < 1)
1779 bubble_min_radius = 1;
1780 if (bubble_max_radius <= bubble_min_radius)
1781 bubble_max_radius = bubble_min_radius + 1;
1783 mesh_length = (2 * bubble_max_radius) + 3;
1785 /* store area of each bubble of certain radius as number of 1/10s of
1786 a pixel area. PI is defined in <math.h> */
1787 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1788 for (i = 0; i < bubble_min_radius; i++)
1789 bubble_areas[i] = 0;
1790 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1791 bubble_areas[i] = calc_bubble_area(i);
1793 mesh_length = (2 * bubble_max_radius) + 3;
1797 "Bug: simple mode code not set but HAVE_XPM not defined\n");
1800 /* Make sure all #ifdef sort of things have been taken care of in
1802 if (use_default_bubble) {
1803 #ifdef NO_DEFAULT_BUBBLE
1805 "Bug: use_default_bubble and NO_DEFAULT_BUBBLE both defined\n");
1808 default_to_pixmaps();
1809 #endif /* NO_DEFAULT_BUBBLE */
1811 /* Set mesh length */
1812 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1815 if (! regular_file(pixmap_file)) {
1816 /* perror() in regular_file printed error message */
1819 uncompress_file(pixmap_file, uncompressed);
1820 read_file_to_pixmaps(uncompressed);
1821 if (strcmp(pixmap_file, uncompressed))
1822 unlink(uncompressed);
1824 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1827 "Bug: use_default_bubble is not defined yet I/O is not compiled in\n");
1829 #endif /* BUBBLES_IO */
1831 #endif /* HAVE_XPM */
1833 /* Am I missing something in here??? */
1836 mesh_width = (screen_width / mesh_length) + 1;
1837 mesh_height = (screen_height / mesh_length) + 1;
1838 mesh_cells = mesh_width * mesh_height;
1841 calculate_adjacent_list();
1845 /* Graphics contexts for simple mode */
1847 gcv.foreground = default_fg_pixel;
1848 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1849 gcv.foreground = default_bg_pixel;
1850 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1853 XClearWindow (dpy, window);
1857 bubbles (Display *dpy, Window window)
1863 insert_new_bubble(tmp);
1870 screenhack (Display *dpy, Window window)
1872 init_bubbles (dpy, window);
1874 bubbles (dpy, window);