1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.8 1997/07/26 19:16:33 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 */
61 # include <sys/wait.h>
63 # if __DECC_VER >= 50200000
64 # include <sys/wait.h>
81 #ifndef NO_DEFAULT_BUBBLE
82 extern int num_default_bubbles;
83 extern char **default_bubbles[];
84 #endif /* NO_DEFAULT_BUBBLE */
86 char *progclass = "Bubbles";
96 "*directory: (default)",
97 #endif /* BUBBLES_IO */
101 "*geometry: 400x300",
105 XrmOptionDescRec options [] = {
106 { "-simple", ".simple", XrmoptionNoArg, "true" },
108 { "-broken", ".broken", XrmoptionNoArg, "true" },
109 #endif /* HAVE_XPM */
110 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
111 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
112 { "-3D", ".3D", XrmoptionNoArg, "true" },
114 { "-file", ".file", XrmoptionSepArg, 0 },
115 { "-directory", ".directory", XrmoptionSepArg, 0 },
116 #endif /* BUBBLES_IO */
117 { "-delay", ".delay", XrmoptionSepArg, 0 },
125 static Bubble **mesh;
126 static int mesh_length;
127 static int mesh_width;
128 static int mesh_height;
129 static int mesh_cells;
131 static int **adjacent_list;
133 static int screen_width;
134 static int screen_height;
135 static int screen_depth;
136 static unsigned int default_fg_pixel, default_bg_pixel;
138 * I know it's not elegant to save this stuff in global variables
139 * but we need it for the signal handler.
141 static Display *defdsp;
142 static Window defwin;
143 static Colormap defcmap;
144 static Visual *defvisual;
146 /* For simple mode only */
147 static int bubble_min_radius;
148 static int bubble_max_radius;
149 static long *bubble_areas;
150 static GC draw_gc, erase_gc;
153 static int num_bubble_pixmaps;
154 static Bubble_Step **step_pixmaps;
156 static char *pixmap_file;
157 #endif /* BUBBLES_IO */
158 static int use_default_bubble;
159 #endif /* HAVE_XPM */
163 static Bool simple = False;
165 static Bool simple = True;
167 static Bool broken = False;
168 static Bool quiet = False;
169 static Bool threed = False;
173 * To prevent forward references, some stuff is up here
177 calc_bubble_area(int r)
178 /* Calculate the area of a bubble of radius r */
182 10.0 * PI * (double)r * (double)r * (double)r);
185 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
187 return (long)(10.0 * PI * (double)r * (double)r);
196 if ((ret = malloc(size)) == NULL) {
197 fprintf(stderr, "%s: out of memory\n", progname);
205 die_bad_bubble(Bubble *bb)
206 /* This is for use with GDB */
208 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
214 null_bubble(Bubble *bb)
215 /* Returns true if the pointer passed is NULL. If not then this checks to
216 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
217 number is set correctly. This only a sanity check for debugging and is
218 turned off if DEBUG isn't set. */
220 if (bb == (Bubble *)NULL)
223 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
224 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
227 if (bb->magic != BUBBLE_MAGIC) {
228 fprintf(stderr, "Magic = %d\n", bb->magic);
232 if ((bb->x < 0) || (bb->x > screen_width) ||
233 (bb->y < 0) || (bb->y > screen_height) ||
234 (bb->radius < bubble_min_radius) || (bb->radius >
235 bubble_max_radius)) {
237 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
238 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
243 if ((bb->x < 0) || (bb->x > screen_width) ||
244 (bb->y < 0) || (bb->y > screen_height) ||
245 (bb->radius < step_pixmaps[0]->radius) ||
246 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
248 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
249 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
252 #endif /* HAVE_XPM */
260 print_bubble_list(Bubble *bb)
261 /* Print list of where all the bubbles are. For debugging purposes only. */
263 if (! null_bubble(bb)) {
264 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
265 print_bubble_list(bb->next);
271 add_bubble_to_list(Bubble **list, Bubble *bb)
272 /* Take a pointer to a list of bubbles and stick bb at the head of the
275 Bubble *head = *list;
277 if (null_bubble(head)) {
278 bb->prev = (Bubble *)NULL;
279 bb->next = (Bubble *)NULL;
282 bb->prev = (Bubble *)NULL;
296 /* Setup the mesh of bubbles */
300 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
301 for (i = 0; i < mesh_cells; i++)
302 mesh[i] = (Bubble *)NULL;
306 cell_to_mesh(int x, int y)
307 /* convert cell coordinates to mesh index */
310 if ((x < 0) || (y < 0)) {
311 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
315 return ((mesh_width * y) + x);
319 mesh_to_cell(int mi, int *cx, int *cy)
320 /* convert mesh index into cell coordinates */
322 *cx = mi % mesh_width;
323 *cy = mi / mesh_width;
327 pixel_to_mesh(int x, int y)
328 /* convert screen coordinates into mesh index */
330 return cell_to_mesh((x / mesh_length), (y / mesh_length));
334 verify_mesh_index(int x, int y)
335 /* check to see if (x,y) is in the mesh */
337 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
339 return (cell_to_mesh(x, y));
344 print_adjacents(int *adj)
345 /* Print a list of the cells calculated above. For debugging only. */
350 for (i = 0; i < 8; i++)
351 printf("%d ", adj[i]);
357 add_to_mesh(Bubble *bb)
358 /* Add the given bubble to the mesh by sticking it on the front of the
359 list. bb is already allocated so no need to malloc() anything, just
363 if (null_bubble(bb)) {
364 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
369 add_bubble_to_list(&mesh[bb->cell_index], bb);
375 /* Print the contents of the mesh */
379 for (i = 0; i < mesh_cells; i++) {
380 if (! null_bubble(mesh[i])) {
381 printf("Mesh cell %d\n", i);
382 print_bubble_list(mesh[i]);
389 /* Check to see if the mesh is Okay. For debugging only. */
394 for (i = 0; i < mesh_cells; i++) {
396 while (! null_bubble(b))
403 /* Count how many bubbles there are in total. For debugging only. */
409 for (i = 0; i < mesh_cells; i++) {
411 while (! null_bubble(b)) {
422 calculate_adjacent_list (void)
423 /* Calculate the list of cells adjacent to a particular cell for use
429 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
430 for (i = 0; i < mesh_cells; i++) {
431 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
432 mesh_to_cell(i, &ix, &iy);
433 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
434 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
435 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
436 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
437 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
438 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
439 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
440 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
441 adjacent_list[i][8] = i;
447 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
456 maxarea = bubble_areas[bubble_max_radius+1];
458 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
460 maxarea = bubble_areas[bubble_max_radius+1];
461 #endif /* HAVE_XPM */
462 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
463 factor = (long)ceil(maxvalue / (double)LONG_MAX);
465 /* Overflow will occur in weighted_mean(). We must divide areas
466 each by factor so it will never do so. */
469 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
470 bubble_areas[i] /= factor;
471 if (bubble_areas[i] == 0)
475 for (i = 0; i <= num_bubble_pixmaps; i++) {
477 printf("area = %ld", step_pixmaps[i]->area);
479 step_pixmaps[i]->area /= factor;
480 if (step_pixmaps[i]->area == 0)
481 step_pixmaps[i]->area = 1;
483 printf("-> %ld\n", step_pixmaps[i]->area);
488 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
489 bubble_areas[i] /= factor;
490 if (bubble_areas[i] == 0)
493 #endif /* HAVE_XPM */
496 printf("maxarea = %ld\n", maxarea);
497 printf("maxvalue = %g\n", maxvalue);
498 printf("LONG_MAX = %ld\n", LONG_MAX);
499 printf("factor = %ld\n", factor);
509 /* Add a new bubble at some random position on the screen of the smallest
512 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
514 /* Can't use null_bubble() here since magic number hasn't been set */
515 if (rv == (Bubble *)NULL) {
516 fprintf(stderr, "Ran out of memory!\n");
521 rv->radius = bubble_min_radius;
522 rv->area = bubble_areas[bubble_min_radius];
526 rv->radius = step_pixmaps[0]->radius;
527 rv->area = step_pixmaps[0]->area;
528 #endif /* HAVE_XPM */
531 rv->magic = BUBBLE_MAGIC;
532 rv->x = ya_random() % screen_width;
533 rv->y = ya_random() % screen_height;
534 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
540 show_bubble(Bubble *bb)
541 /* paint the bubble on the screen */
543 if (null_bubble(bb)) {
544 fprintf(stderr, "NULL bubble passed to show_bubble\n");
552 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
553 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
557 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
558 (bb->x - bb->radius),
559 (bb->y - bb->radius));
561 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
562 step_pixmaps[bb->step]->draw_gc,
563 0, 0, (bb->radius * 2),
565 (bb->x - bb->radius),
566 (bb->y - bb->radius));
567 #endif /* HAVE_XPM */
573 hide_bubble(Bubble *bb)
574 /* erase the bubble */
576 if (null_bubble(bb)) {
577 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
585 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
586 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
591 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
592 (bb->x - bb->radius), (bb->y - bb->radius));
594 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
595 (bb->x - bb->radius),
596 (bb->y - bb->radius),
600 #endif /* HAVE_XPM */
606 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
607 /* Delete an individual bubble, adjusting list of bubbles around it.
608 If keep_bubble is true then the bubble isn't actually deleted. We
609 use this to allow bubbles to change mesh cells without reallocating,
610 (it needs this when two bubbles collide and the centre position is
611 recalculated, and this may stray over a mesh boundary). */
613 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
614 bb->prev->next = bb->next;
615 bb->next->prev = bb->prev;
616 } else if ((!null_bubble(bb->prev)) &&
617 (null_bubble(bb->next))) {
618 bb->prev->next = (Bubble *)NULL;
619 bb->next = mesh[bb->cell_index];
620 } else if ((null_bubble(bb->prev)) &&
621 (!null_bubble(bb->next))) {
622 bb->next->prev = (Bubble *)NULL;
623 mesh[bb->cell_index] = bb->next;
624 bb->next = mesh[bb->cell_index];
626 /* Only item on list */
627 mesh[bb->cell_index] = (Bubble *)NULL;
635 /* Saves ugly inline code */
637 return ((unsigned long)x * (unsigned long)x);
641 get_closest_bubble(Bubble *bb)
642 /* Find the closest bubble touching the this bubble, NULL if none are
645 Bubble *rv = (Bubble *)NULL;
647 unsigned long separation2, touchdist2;
649 unsigned long closest2 = ULONG_MAX;
653 if (null_bubble(bb)) {
654 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
660 for (i = 0; i < 9; i++) {
661 /* There is a bug here where bb->cell_index is negaitve.. */
663 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
664 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
668 /* printf("%d,", bb->cell_index); */
669 if (adjacent_list[bb->cell_index][i] != -1) {
670 tmp = mesh[adjacent_list[bb->cell_index][i]];
671 while (! null_bubble(tmp)) {
675 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
676 /* Add extra leeway so circles _never_ overlap */
677 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
678 if ((separation2 <= touchdist2) && (separation2 <
681 closest2 = separation2;
700 long_div_round(long num, long dem)
705 if ((num < 0) || (dem < 0)) {
706 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
714 if (moddo > (dem / 2))
718 if ((divvie < 0) || (moddo < 0)) {
719 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
729 weighted_mean(int n1, int n2, long w1, long w2)
730 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
733 if ((w1 <= 0) || (w2 <= 0)) {
735 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
740 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
745 bubble_eat(Bubble *diner, Bubble *food)
746 /* The diner eats the food. Returns true (1) if the diner still exists */
752 if ((null_bubble(diner)) || (null_bubble(food))) {
753 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
758 /* We hide the diner even in the case that it doesn't grow so that
759 if the food overlaps its boundary it is replaced. This could
760 probably be solved by letting bubbles eat others which are close
761 but not quite touching. It's probably worth it, too, since we
762 would then not have to redraw bubbles which don't change in
767 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
768 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
769 newmi = pixel_to_mesh(diner->x, diner->y);
770 diner->area += food->area;
771 delete_bubble_in_mesh(food, DELETE_BUBBLE);
773 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
774 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
778 if ((! simple) && (diner->area >
779 step_pixmaps[num_bubble_pixmaps]->area)) {
780 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
783 #endif /* HAVE_XPM */
786 if (diner->area > bubble_areas[diner->radius + 1]) {
787 /* Move the bubble to a new radius */
789 while (diner->area > bubble_areas[i+1])
796 if (diner->area > step_pixmaps[diner->step+1]->area) {
798 while (diner->area > step_pixmaps[i+1]->area)
801 diner->radius = step_pixmaps[diner->step]->radius;
804 #endif /* HAVE_XPM */
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 ((ya_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 while (! null_bubble(touch)) {
910 switch (merge_bubbles(nextbub, touch)) {
912 /* touch ate nextbub and survived */
916 /* nextbub ate touch and survived */
919 /* somebody ate someone else but they exploded */
920 nextbub = (Bubble *)NULL;
923 /* something went wrong */
924 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
927 /* Check to see if there are any other bubbles still in the area
928 and if we need to do this all over again for them. */
929 if (! null_bubble(nextbub))
930 touch = get_closest_bubble(nextbub);
932 touch = (Bubble *)NULL;
938 get_length_of_bubble_list(Bubble *bb)
943 while (! null_bubble(tmp)) {
953 * Pixmap stuff used regardless of whether file I/O is available. Must
954 * still check for XPM, though!
961 /* Free resources associated with XPM */
967 fprintf(stderr, "free_pixmaps() called in simple mode\n");
970 printf("free_pixmaps()\n");
973 for(i = 0; i < (num_bubble_pixmaps - 1); i++) {
974 XFreePixmap(defdsp, step_pixmaps[i]->ball);
975 XFreePixmap(defdsp, step_pixmaps[i]->shape_mask);
976 XFreeGC(defdsp, step_pixmaps[i]->draw_gc);
977 XFreeGC(defdsp, step_pixmaps[i]->erase_gc);
978 XFreeColors(defdsp, defcmap, step_pixmaps[i]->xpmattrs.pixels,
979 step_pixmaps[i]->xpmattrs.npixels, 0);
980 XpmFreeAttributes(&step_pixmaps[i]->xpmattrs);
986 /* This gets called when SIGINT or SIGTERM is received */
995 /* Called when SEGV detected. Hmmmmm.... */
998 fprintf(stderr, "SEGV detected! : %d\n", a);
1005 * Pixmaps without file I/O (but do have XPM)
1009 pixmap_sort(Bubble_Step **head, int numelems)
1010 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1011 the numelems length array with first element at head into order of radius.
1015 Bubble_Step *least = 0;
1016 int minradius = INT_MAX;
1019 for (i = 0; i < numelems; i++) {
1020 if (head[i]->radius < minradius) {
1022 minradius = head[i]->radius;
1025 if (*head != least) {
1026 memcpy(&tmp, least, sizeof(Bubble_Step));
1027 memcpy(least, *head, sizeof(Bubble_Step));
1028 memcpy(*head, &tmp, sizeof(Bubble_Step));
1032 pixmap_sort(&head[1], numelems-1);
1036 extrapolate(int i1, int i2)
1038 return (i2 + (i2 - i1));
1042 make_pixmap_array(Bubble_Step *list)
1043 /* From a linked list of bubbles construct the array step_pixmaps */
1045 Bubble_Step *tmp = list;
1051 if (list == (Bubble_Step *)NULL) {
1052 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1056 num_bubble_pixmaps = 1;
1057 while(tmp->next != (Bubble_Step *)NULL) {
1059 ++num_bubble_pixmaps;
1062 if (num_bubble_pixmaps < 2) {
1063 fprintf(stderr, "Must be at least two bubbles in file\n");
1067 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1068 sizeof(Bubble_Step *));
1070 /* Copy them blindly into the array for sorting. */
1074 step_pixmaps[ind++] = tmp;
1076 } while(tmp != (Bubble_Step *)NULL);
1078 /* We make another bubble beyond the ones with pixmaps so that the final
1079 bubble hangs around and doesn't pop immediately. It's radius and area
1080 are found by extrapolating from the largest two bubbles with pixmaps. */
1082 step_pixmaps[num_bubble_pixmaps] =
1083 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1084 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1086 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1089 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1090 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1094 step_pixmaps[num_bubble_pixmaps]->radius =
1095 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1096 step_pixmaps[num_bubble_pixmaps-1]->radius);
1097 step_pixmaps[num_bubble_pixmaps]->area =
1098 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1102 /* Now check for correct order */
1103 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1105 if (step_pixmaps[ind]->radius < prevrad) {
1106 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1110 prevrad = step_pixmaps[ind]->radius;
1115 #ifndef NO_DEFAULT_BUBBLE
1117 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1118 /* Read pixmap data which has been compiled into the program and a pointer
1119 to which has been passed.
1121 This is virtually copied verbatim from make_pixmap_from_file() above and
1122 changes made to either should be propagated onwards! */
1128 if (pixmap_data == (char **)0) {
1129 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1134 if (bl == (Bubble_Step *)NULL) {
1135 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1139 bl->xpmattrs.valuemask = 0;
1142 bl->xpmattrs.valuemask |= XpmCloseness;
1143 bl->xpmattrs.closeness = 40000;
1146 bl->xpmattrs.valuemask |= XpmVisual;
1147 bl->xpmattrs.visual = defvisual;
1150 bl->xpmattrs.valuemask |= XpmDepth;
1151 bl->xpmattrs.depth = screen_depth;
1154 bl->xpmattrs.valuemask |= XpmColormap;
1155 bl->xpmattrs.colormap = defcmap;
1159 /* This is the only line which is different from make_pixmap_from_file() */
1160 result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
1161 &bl->shape_mask, &bl->xpmattrs);
1165 fprintf(stderr, "xpm: color substitution performed\n");
1168 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1169 bl->area = calc_bubble_area(bl->radius);
1171 case XpmColorFailed:
1172 fprintf(stderr, "xpm: color allocation failed\n");
1175 fprintf(stderr, "xpm: out of memory\n");
1178 fprintf(stderr, "xpm: unknown error code %d\n", result);
1182 gcv.plane_mask = AllPlanes;
1183 gcv.foreground = default_fg_pixel;
1184 gcv.function = GXcopy;
1185 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1186 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1188 gcv.foreground = default_bg_pixel;
1189 gcv.function = GXcopy;
1190 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1191 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1195 default_to_pixmaps (void)
1196 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1199 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1200 Bubble_Step *newpix, *tmppix;
1203 /* Make sure pixmaps are freed when program is terminated */
1204 /* This is when I hit ^C */
1205 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1206 signal(SIGINT, onintr);
1207 /* xscreensaver sends SIGTERM */
1208 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1209 signal(SIGTERM, onintr);
1211 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
1212 printf("Setting signal handler for SIGSEGV\n");
1213 signal(SIGSEGV, onsegv);
1215 printf("Didn't set signal hanlder for SIGSEGV\n");
1219 for (i = 0; i < num_default_bubbles; i++) {
1220 pixpt = default_bubbles[i];
1221 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1222 make_pixmap_from_default(pixpt, newpix);
1223 /* Now add to list */
1224 if (pixmap_list == (Bubble_Step *)NULL) {
1225 pixmap_list = newpix;
1227 tmppix = pixmap_list;
1228 while (tmppix->next != (Bubble_Step *)NULL)
1229 tmppix = tmppix->next;
1230 tmppix->next = newpix;
1232 newpix->next = (Bubble_Step *)NULL;
1235 /* Finally construct step_pixmaps[] */
1236 make_pixmap_array(pixmap_list);
1239 #endif /* NO_DEFAULT_BUBBLE */
1241 #endif /* HAVE_XPM */
1250 my_opendir(char *name)
1251 /* Like opendir() but checks for things so we don't have to do it multiple
1252 times in the code. */
1256 if (name == (char *)NULL) {
1257 fprintf(stderr, "NULL directory name\n");
1261 if ((rv = opendir(name)) == NULL) {
1270 regular_file(char *name)
1271 /* Check to see if we can use the named file. This was broken under Linux
1272 1.3.45 but seems to be okay under 1.3.54. The parameter "name" was being
1273 trashed if the file didn't exist. Yeah, I know 1.3.x are development
1279 if ((fd = open(name, O_RDONLY)) == -1) {
1289 get_random_name(char *dir)
1290 /* Pick an appropriate file at random out of the files in the directory dir */
1297 char buf[PATH_BUF_SIZE];
1300 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1301 return (char *)NULL;
1303 while ((dp = readdir(dfd)) != NULL) {
1304 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1306 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1307 fprintf(stderr, "name %s/%s too long\n", dir, DIRENT_NAME);
1310 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1311 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1314 if (regular_file(buf))
1318 if (numentries == 0) {
1319 fprintf(stderr, "No suitable files found in %s\n", dir);
1320 return (char *)NULL;
1322 entnum = ya_random() % numentries;
1325 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1326 return (char *)NULL;
1327 while ((dp = readdir(dfd)) != NULL) {
1328 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1330 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1331 /* We warned about this previously */
1334 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1335 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1338 if (regular_file(buf)) {
1340 rv = (char *)xmalloc(1024 * sizeof(char));
1348 /* We've screwed up if we reach here - someone must have deleted all the
1349 files while we were counting them... */
1350 fprintf(stderr, "get_random_name(): Oops!\n");
1355 read_line(int fd, char **buf, int bufsize)
1356 /* A line is read from fd until a '\n' is found or EOF is reached. (*buf)
1357 is initially of length bufsize and is extended by bufsize chars if need
1358 be (for as many times as it takes). */
1367 rv = read(fd, &x, 1);
1369 perror("read_line(): ");
1371 } else if (rv == 0) {
1374 } else if (x == '\n') {
1379 if (pos == (size - 1)) {
1380 /* We've come to the end of the space */
1381 newbuf = (char *)xmalloc((size+bufsize) * sizeof(char));
1382 strncpy(newbuf, *buf, (size - 1));
1392 create_temp_file(char **name)
1393 /* Create a temporary file in /tmp and return a filedescriptor to it */
1397 if (*name != (char *)NULL)
1400 if ((*name = tempnam("/tmp", "abxdfes")) == (char *)NULL) {
1401 fprintf(stderr, "Couldn't make new temporary file\n");
1404 /* printf("Temp file created : %s\n", *name); */
1405 if ((rv = creat(*name, 0644)) == -1) {
1406 fprintf(stderr, "Couldn't open temporary file\n");
1416 make_pixmap_from_file(char *fname, Bubble_Step *bl)
1417 /* Read the pixmap in file fname into structure bl which must already
1423 if (bl == (Bubble_Step *)NULL) {
1424 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1428 bl->xpmattrs.closeness = 40000;
1429 bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
1430 bl->xpmattrs.colormap = defcmap;
1432 result = XpmReadFileToPixmap(defdsp, defwin, fname, &bl->ball,
1433 &bl->shape_mask, &bl->xpmattrs);
1437 fprintf(stderr, "xpm: color substitution performed\n");
1440 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1441 bl->area = calc_bubble_area(bl->radius);
1443 case XpmColorFailed:
1444 fprintf(stderr, "xpm: color allocation failed\n");
1447 fprintf(stderr, "xpm: out of memory\n");
1450 fprintf(stderr, "xpm: unknown error code %d\n", result);
1454 gcv.plane_mask = AllPlanes;
1455 gcv.foreground = default_fg_pixel;
1456 gcv.function = GXcopy;
1457 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1458 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1460 gcv.foreground = default_bg_pixel;
1461 gcv.function = GXcopy;
1462 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1463 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1465 #endif /* BUBBLES_IO */
1468 read_file_to_pixmaps(char *fname)
1469 /* Read the pixmaps contained in the file fname into memory. THESE SHOULD
1470 BE UNCOMPRESSED AND READY TO GO! */
1472 int fd, tmpfd=0, rv;
1475 char *buf = (char *)NULL;
1476 char *tmpname = (char *)NULL;
1477 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1478 Bubble_Step *newpix, *tmppix;
1480 /* We first create a linked list of pixmaps before allocating
1481 memory for the array */
1483 if ((fd = open(fname, O_RDONLY)) == -1) {
1484 fprintf(stderr, "Couldn't open %s\n", fname);
1488 /* Make sure pixmaps are freed when program is terminated */
1489 /* This is when I hit ^C */
1490 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1491 signal(SIGINT, onintr);
1492 /* xscreensaver sends SIGTERM */
1493 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1494 signal(SIGTERM, onintr);
1496 if (signal(SIGSEGV, SIGN_IGN) != SIG_IGN)
1497 signal(SIGSEGV, onsegv);
1504 buf = (char *)malloc(READ_LINE_BUF_SIZE * sizeof(char));
1506 switch ((rv = read_line(fd, &buf, READ_LINE_BUF_SIZE))) {
1508 fprintf(stderr, "An I/O error occurred\n");
1512 fprintf(stderr, "EOF occurred inside an XPM block\n");
1519 if (strncmp("};", buf, 2) == 0) {
1521 write(tmpfd, buf, strlen(buf));
1522 write(tmpfd, "\n", 1);
1524 /* Now process the tmpfile */
1525 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1526 make_pixmap_from_file(tmpname, newpix);
1527 /* Now add to list */
1528 if (pixmap_list == (Bubble_Step *)NULL) {
1529 pixmap_list = newpix;
1531 tmppix = pixmap_list;
1532 while (tmppix->next != (Bubble_Step *)NULL)
1533 tmppix = tmppix->next;
1534 tmppix->next = newpix;
1536 newpix->next = (Bubble_Step *)NULL;
1539 write(tmpfd, buf, strlen(buf));
1540 write(tmpfd, "\n", 1);
1543 if (strncmp("/* XPM */", buf, 9) == 0) {
1544 tmpfd = create_temp_file(&tmpname);
1545 /* This proves XPM's performance is kinda pathetic */
1547 printf("New XPM detected : %s, fd=%d\n", tmpname, tmpfd);
1552 write(tmpfd, buf, strlen(buf));
1553 write(tmpfd, "\n", 1);
1557 fprintf(stderr, "read_line returned unknown code %d\n", rv);
1565 if (buf != (char *)NULL)
1567 if (tmpname != (char *)NULL)
1571 fprintf(stderr, "There was no XPM data in the file %s\n", fname);
1575 /* Finally construct step_pixmaps[] */
1576 make_pixmap_array(pixmap_list);
1580 shell_exec(char *command)
1581 /* Forks a shell to execute "command" then waits for command to finish */
1583 int pid, status, wval;
1585 switch(pid=fork()) {
1587 if (execlp(BOURNESH, BOURNESH, "-c", command, (char *)NULL) == -1) {
1588 fprintf(stderr, "Couldn't exec shell %s\n", BOURNESH);
1591 /* fall through if execlp() fails */
1597 while ((wval = wait(&status)) != pid)
1606 uncompress_file(char *current, char *namebuf)
1607 /* If the file current is compressed (i.e. its name ends in .gz or .Z,
1608 no check is made to see if it is actually a compressed file...) then a
1609 new temporary file is created for it and it is decompressed into there,
1610 returning the name of the file to namebuf, else current is returned in
1614 char *tname = (char *)NULL;
1615 char argbuf[COMMAND_BUF_SIZE];
1617 if (((strlen(current) >=4) &&
1618 (strncmp(¤t[strlen(current)-3], ".gz", 3) == 0)) ||
1619 ((strlen(current) >=3) &&
1620 (strncmp(¤t[strlen(current)-2], ".Z", 2) == 0))) {
1621 fd = create_temp_file(&tname);
1622 /* close immediately but don't unlink so we should have a zero length
1623 file in /tmp which we can append to */
1625 if (sprintf(argbuf, "%s -dc %s > %s", GZIP, current, tname) >
1626 (COMMAND_BUF_SIZE-1)) {
1627 fprintf(stderr, "command buffer overflowed in uncompress_file()\n");
1631 strcpy(namebuf, tname);
1633 strcpy(namebuf, current);
1638 #endif /* BUBBLES_IO */
1646 get_resources(Display *dpy, Window window)
1647 /* Get the appropriate X resources and warn about any inconsistencies. */
1655 #endif /* HAVE_XPM */
1656 #endif /* BUBBLES_IO */
1658 XWindowAttributes xgwa;
1660 XGetWindowAttributes (dpy, window, &xgwa);
1661 cmap = xgwa.colormap;
1663 threed = get_boolean_resource("3D", "Boolean");
1664 quiet = get_boolean_resource("quiet", "Boolean");
1665 simple = get_boolean_resource("simple", "Boolean");
1666 /* Forbid rendered bubbles on monochrome displays */
1667 if ((mono_p) && (! simple)) {
1670 "Rendered bubbles not supported on monochrome displays\n");
1673 delay = get_integer_resource("delay", "Integer");
1674 nodelay = get_boolean_resource("nodelay", "Boolean");
1680 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1682 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1687 broken = get_boolean_resource("broken", "Boolean");
1690 fprintf(stderr, "-broken not available in simple mode\n");
1695 broken = get_boolean_resource("broken", "Boolean");
1697 pixmap_file = get_string_resource("file", "File");
1698 dirname = get_string_resource("directory", "Directory");
1699 #ifdef NO_DEFAULT_BUBBLE
1700 /* Must specify -file or -directory if no default bubble compiled in */
1701 if (strcmp(pixmap_file, "(default)") != 0) {
1702 } else if (strcmp(dirname, "(default)") != 0) {
1703 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1704 /* Die if we can't open directory - make it consistent with -file
1705 when it fails, rather than falling back to default. */
1710 "No default bubble compiled in - use -file or -directory\n");
1714 if (strcmp(pixmap_file, "(default)") != 0) {
1715 } else if (strcmp(dirname, "(default)") != 0) {
1716 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1720 /* Use default bubble */
1721 use_default_bubble = 1;
1723 #endif /* NO_DEFAULT_BUBBLE */
1725 use_default_bubble = 1;
1726 #endif /* BUBBLES_IO */
1727 #endif /* HAVE_XPM */
1732 init_bubbles (Display *dpy, Window window)
1735 XWindowAttributes xgwa;
1738 char uncompressed[1024];
1739 #endif /* BUBBLES_IO */
1746 get_resources(dpy, window);
1748 XGetWindowAttributes (dpy, window, &xgwa);
1751 printf("sizof(int) on this platform is %d\n", sizeof(int));
1752 printf("sizof(long) on this platform is %d\n", sizeof(long));
1755 screen_width = xgwa.width;
1756 screen_height = xgwa.height;
1757 screen_depth = xgwa.depth;
1758 defcmap = xgwa.colormap;
1759 defvisual = xgwa.visual;
1762 /* These are pretty much plucked out of the air */
1763 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1765 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1767 /* Some trivial values */
1768 if (bubble_min_radius < 1)
1769 bubble_min_radius = 1;
1770 if (bubble_max_radius <= bubble_min_radius)
1771 bubble_max_radius = bubble_min_radius + 1;
1773 mesh_length = (2 * bubble_max_radius) + 3;
1775 /* store area of each bubble of certain radius as number of 1/10s of
1776 a pixel area. PI is defined in <math.h> */
1777 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1778 for (i = 0; i < bubble_min_radius; i++)
1779 bubble_areas[i] = 0;
1780 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1781 bubble_areas[i] = calc_bubble_area(i);
1783 mesh_length = (2 * bubble_max_radius) + 3;
1787 "Bug: simple mode code not set but HAVE_XPM not defined\n");
1790 /* Make sure all #ifdef sort of things have been taken care of in
1792 if (use_default_bubble) {
1793 #ifdef NO_DEFAULT_BUBBLE
1795 "Bug: use_default_bubble and NO_DEFAULT_BUBBLE both defined\n");
1798 default_to_pixmaps();
1799 #endif /* NO_DEFAULT_BUBBLE */
1801 /* Set mesh length */
1802 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1805 if (! regular_file(pixmap_file)) {
1806 /* perror() in regular_file printed error message */
1809 uncompress_file(pixmap_file, uncompressed);
1810 read_file_to_pixmaps(uncompressed);
1811 if (strcmp(pixmap_file, uncompressed))
1812 unlink(uncompressed);
1814 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1817 "Bug: use_default_bubble is not defined yet I/O is not compiled in\n");
1819 #endif /* BUBBLES_IO */
1821 #endif /* HAVE_XPM */
1823 /* Am I missing something in here??? */
1826 mesh_width = (screen_width / mesh_length) + 1;
1827 mesh_height = (screen_height / mesh_length) + 1;
1828 mesh_cells = mesh_width * mesh_height;
1831 calculate_adjacent_list();
1835 /* Graphics contexts for simple mode */
1837 gcv.foreground = default_fg_pixel;
1838 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1839 gcv.foreground = default_bg_pixel;
1840 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1843 XClearWindow (dpy, window);
1847 bubbles (Display *dpy, Window window)
1853 insert_new_bubble(tmp);
1860 screenhack (Display *dpy, Window window)
1862 init_bubbles (dpy, window);
1864 bubbles (dpy, window);