1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.1 1996/09/08 01:35:40 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
49 #include <sys/types.h>
50 #endif /* BUBBLES_IO */
60 #include "screenhack.h"
61 #include "../utils/yarandom.h"
71 #ifndef NO_DEFAULT_BUBBLE
72 extern int num_default_bubbles;
73 extern char **default_bubbles[];
74 #endif /* NO_DEFAULT_BUBBLE */
76 char *progclass = "Bubbles";
86 "*directory: (default)",
87 #endif /* BUBBLES_IO */
95 XrmOptionDescRec options [] = {
96 { "-simple", ".simple", XrmoptionNoArg, "true" },
98 { "-broken", ".broken", XrmoptionNoArg, "true" },
100 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
101 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
102 { "-3D", ".3D", XrmoptionNoArg, "true" },
104 { "-file", ".file", XrmoptionSepArg, 0 },
105 { "-directory", ".directory", XrmoptionSepArg, 0 },
106 #endif /* BUBBLES_IO */
107 { "-delay", ".delay", XrmoptionSepArg, 0 }
109 int options_size = (sizeof (options) / sizeof (options[0]));
115 static Bubble **mesh;
116 static int mesh_length;
117 static int mesh_width;
118 static int mesh_height;
119 static int mesh_cells;
121 static int **adjacent_list;
123 static int screen_width;
124 static int screen_height;
125 static int screen_depth;
126 static unsigned int default_fg_pixel, default_bg_pixel;
128 * I know it's not elegant to save this stuff in global variables
129 * but we need it for the signal handler.
131 static Display *defdsp;
132 static Window defwin;
133 static Colormap defcmap;
135 /* For simple mode only */
136 static int bubble_min_radius;
137 static int bubble_max_radius;
138 static long *bubble_areas;
139 static GC draw_gc, erase_gc;
142 static int num_bubble_pixmaps;
143 static Bubble_Step **step_pixmaps;
145 static char *pixmap_file;
146 #endif /* BUBBLES_IO */
147 static int use_default_bubble;
148 #endif /* HAVE_XPM */
152 static Bool simple = False;
154 static Bool simple = True;
156 static Bool broken = False;
157 static Bool quiet = False;
158 static Bool threed = False;
162 * To prevent forward references, some stuff is up here
168 /* Calculate the area of a bubble of radius r */
172 10.0 * PI * (double)r * (double)r * (double)r);
175 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
177 return (long)(10.0 * PI * (double)r * (double)r);
187 if ((ret = malloc(size)) == NULL) {
188 fprintf(stderr, "%s: out of memory\n", progname);
198 /* This is for use with GDB */
200 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
208 /* Returns true if the pointer passed is NULL. If not then this checks to
209 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
210 number is set correctly. This only a sanity check for debugging and is
211 turned off if DEBUG isn't set. */
213 if (bb == (Bubble *)NULL)
216 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
217 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
220 if (bb->magic != BUBBLE_MAGIC) {
221 fprintf(stderr, "Magic = %d\n", bb->magic);
225 if ((bb->x < 0) || (bb->x > screen_width) ||
226 (bb->y < 0) || (bb->y > screen_height) ||
227 (bb->radius < bubble_min_radius) || (bb->radius >
228 bubble_max_radius)) {
230 "radius = %d, x = %d, y = %d, magic = %d, \
231 cell index = %d\n", bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
236 if ((bb->x < 0) || (bb->x > screen_width) ||
237 (bb->y < 0) || (bb->y > screen_height) ||
238 (bb->radius < step_pixmaps[0]->radius) ||
239 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
241 "radius = %d, x = %d, y = %d, magic = %d, \
242 cell index = %d\n", bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
245 #endif /* HAVE_XPM */
253 print_bubble_list(bb)
255 /* Print list of where all the bubbles are. For debugging purposes only. */
257 if (! null_bubble(bb)) {
258 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
259 print_bubble_list(bb->next);
265 add_bubble_to_list(list, bb)
268 /* Take a pointer to a list of bubbles and stick bb at the head of the
271 Bubble *head = *list;
273 if (null_bubble(head)) {
274 bb->prev = (Bubble *)NULL;
275 bb->next = (Bubble *)NULL;
278 bb->prev = (Bubble *)NULL;
292 /* Setup the mesh of bubbles */
296 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
297 for (i = 0; i < mesh_cells; i++)
298 mesh[i] = (Bubble *)NULL;
305 /* convert cell coordinates to mesh index */
308 if ((x < 0) || (y < 0)) {
309 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
313 return ((mesh_width * y) + x);
317 mesh_to_cell(mi, cx, cy)
321 /* convert mesh index into cell coordinates */
323 *cx = mi % mesh_width;
324 *cy = mi / mesh_width;
331 /* convert screen coordinates into mesh index */
333 return cell_to_mesh((x / mesh_length), (y / mesh_length));
337 verify_mesh_index(x, y)
340 /* check to see if (x,y) is in the mesh */
342 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
344 return (cell_to_mesh(x, y));
351 /* Print a list of the cells calculated above. For debugging only. */
356 for (i = 0; i < 8; i++)
357 printf("%d ", adj[i]);
365 /* Add the given bubble to the mesh by sticking it on the front of the
366 list. bb is already allocated so no need to malloc() anything, just
370 if (null_bubble(bb)) {
371 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
376 add_bubble_to_list(&mesh[bb->cell_index], bb);
382 /* Print the contents of the mesh */
386 for (i = 0; i < mesh_cells; i++) {
387 if (! null_bubble(mesh[i])) {
388 printf("Mesh cell %d\n", i);
389 print_bubble_list(mesh[i]);
396 /* Check to see if the mesh is Okay. For debugging only. */
401 for (i = 0; i < mesh_cells; i++) {
403 while (! null_bubble(b))
410 /* Count how many bubbles there are in total. For debugging only. */
416 for (i = 0; i < mesh_cells; i++) {
418 while (! null_bubble(b)) {
429 calculate_adjacent_list()
430 /* Calculate the list of cells adjacent to a particular cell for use
436 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
437 for (i = 0; i < mesh_cells; i++) {
438 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
439 mesh_to_cell(i, &ix, &iy);
440 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
441 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
442 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
443 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
444 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
445 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
446 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
447 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
448 adjacent_list[i][8] = i;
454 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
463 maxarea = bubble_areas[bubble_max_radius+1];
465 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
467 maxarea = bubble_areas[bubble_max_radius+1];
468 #endif /* HAVE_XPM */
469 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
470 factor = (long)ceil(maxvalue / (double)LONG_MAX);
472 /* Overflow will occur in weighted_mean(). We must divide areas
473 each by factor so it will never do so. */
476 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
477 bubble_areas[i] /= factor;
478 if (bubble_areas[i] == 0)
482 for (i = 0; i <= num_bubble_pixmaps; i++) {
484 printf("area = %ld", step_pixmaps[i]->area);
486 step_pixmaps[i]->area /= factor;
487 if (step_pixmaps[i]->area == 0)
488 step_pixmaps[i]->area = 1;
490 printf("-> %ld\n", step_pixmaps[i]->area);
495 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
496 bubble_areas[i] /= factor;
497 if (bubble_areas[i] == 0)
500 #endif /* HAVE_XPM */
503 printf("maxarea = %ld\n", maxarea);
504 printf("maxvalue = %g\n", maxvalue);
505 printf("LONG_MAX = %ld\n", LONG_MAX);
506 printf("factor = %ld\n", factor);
516 /* Add a new bubble at some random position on the screen of the smallest
519 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
521 /* Can't use null_bubble() here since magic number hasn't been set */
522 if (rv == (Bubble *)NULL) {
523 fprintf(stderr, "Ran out of memory!\n");
528 rv->radius = bubble_min_radius;
529 rv->area = bubble_areas[bubble_min_radius];
533 rv->radius = step_pixmaps[0]->radius;
534 rv->area = step_pixmaps[0]->area;
535 #endif /* HAVE_XPM */
538 rv->magic = BUBBLE_MAGIC;
539 rv->x = ya_random() % screen_width;
540 rv->y = ya_random() % screen_height;
541 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
549 /* paint the bubble on the screen */
551 if (null_bubble(bb)) {
552 fprintf(stderr, "NULL bubble passed to show_bubble\n");
560 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
561 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
565 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
566 (bb->x - bb->radius),
567 (bb->y - bb->radius));
569 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
570 step_pixmaps[bb->step]->draw_gc,
571 0, 0, (bb->radius * 2),
573 (bb->x - bb->radius),
574 (bb->y - bb->radius));
575 #endif /* HAVE_XPM */
583 /* erase the bubble */
585 if (null_bubble(bb)) {
586 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
594 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
595 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
600 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
601 (bb->x - bb->radius), (bb->y - bb->radius));
603 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
604 (bb->x - bb->radius),
605 (bb->y - bb->radius),
609 #endif /* HAVE_XPM */
615 delete_bubble_in_mesh(bb, keep_bubble)
618 /* Delete an individual bubble, adjusting list of bubbles around it.
619 If keep_bubble is true then the bubble isn't actually deleted. We
620 use this to allow bubbles to change mesh cells without reallocating,
621 (it needs this when two bubbles collide and the centre position is
622 recalculated, and this may stray over a mesh boundary). */
624 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
625 bb->prev->next = bb->next;
626 bb->next->prev = bb->prev;
627 } else if ((!null_bubble(bb->prev)) &&
628 (null_bubble(bb->next))) {
629 bb->prev->next = (Bubble *)NULL;
630 bb->next = mesh[bb->cell_index];
631 } else if ((null_bubble(bb->prev)) &&
632 (!null_bubble(bb->next))) {
633 bb->next->prev = (Bubble *)NULL;
634 mesh[bb->cell_index] = bb->next;
635 bb->next = mesh[bb->cell_index];
637 /* Only item on list */
638 mesh[bb->cell_index] = (Bubble *)NULL;
647 /* Saves ugly inline code */
649 return ((unsigned long)x * (unsigned long)x);
653 get_closest_bubble(bb)
655 /* Find the closest bubble touching the this bubble, NULL if none are
658 Bubble *rv = (Bubble *)NULL;
660 unsigned long separation2, touchdist2;
662 unsigned long closest2 = ULONG_MAX;
666 if (null_bubble(bb)) {
667 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
673 for (i = 0; i < 9; i++) {
674 /* There is a bug here where bb->cell_index is negaitve.. */
676 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
677 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
681 /* printf("%d,", bb->cell_index); */
682 if (adjacent_list[bb->cell_index][i] != -1) {
683 tmp = mesh[adjacent_list[bb->cell_index][i]];
684 while (! null_bubble(tmp)) {
688 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
689 /* Add extra leeway so circles _never_ overlap */
690 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
691 if ((separation2 <= touchdist2) && (separation2 <
694 closest2 = separation2;
713 long_div_round(num, dem)
720 if ((num < 0) || (dem < 0)) {
721 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
729 if (moddo > (dem / 2))
733 if ((divvie < 0) || (moddo < 0)) {
734 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
744 weighted_mean(n1, n2, w1, w2)
749 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
752 if ((w1 <= 0) || (w2 <= 0)) {
753 fprintf(stderr, "Bad weights passed to weighted_mean() - \
754 (%d, %d, %ld, %ld)!\n", n1, n2, w1, w2);
758 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
763 bubble_eat(diner, food)
766 /* The diner eats the food. Returns true (1) if the diner still exists */
772 if ((null_bubble(diner)) || (null_bubble(food))) {
773 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
778 /* We hide the diner even in the case that it doesn't grow so that
779 if the food overlaps its boundary it is replaced. This could
780 probably be solved by letting bubbles eat others which are close
781 but not quite touching. It's probably worth it, too, since we
782 would then not have to redraw bubbles which don't change in
787 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
788 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
789 newmi = pixel_to_mesh(diner->x, diner->y);
790 diner->area += food->area;
791 delete_bubble_in_mesh(food, DELETE_BUBBLE);
793 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
794 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
798 if ((! simple) && (diner->area >
799 step_pixmaps[num_bubble_pixmaps]->area)) {
800 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
803 #endif /* HAVE_XPM */
806 if (diner->area > bubble_areas[diner->radius + 1]) {
807 /* Move the bubble to a new radius */
809 while (diner->area > bubble_areas[i+1])
816 if (diner->area > step_pixmaps[diner->step+1]->area) {
818 while (diner->area > step_pixmaps[i+1]->area)
821 diner->radius = step_pixmaps[diner->step]->radius;
824 #endif /* HAVE_XPM */
827 /* Now adjust locations and cells if need be */
828 if (newmi != diner->cell_index) {
829 delete_bubble_in_mesh(diner, KEEP_BUBBLE);
830 diner->cell_index = newmi;
838 merge_bubbles(b1, b2)
841 /* These two bubbles merge into one. If the first one wins out return
842 1 else return 2. If there is no winner (it explodes) then return 0 */
850 if ((null_bubble(b1) || null_bubble(b2))) {
851 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
858 delete_bubble_in_mesh(b1, DELETE_BUBBLE);
862 if (b1size > b2size) {
863 switch (bubble_eat(b1, b2)) {
873 } else if (b1size < b2size) {
874 switch (bubble_eat(b2, b1)) {
885 if ((ya_random() % 2) == 0) {
886 switch (bubble_eat(b1, b2)) {
897 switch (bubble_eat(b2, b1)) {
909 fprintf(stderr, "An error occurred in merge_bubbles()\n");
914 insert_new_bubble(tmp)
916 /* Calculates which bubbles are eaten when a new bubble tmp is
917 inserted. This is called recursively in case when a bubble grows
918 it eats others. Careful to pick out disappearing bubbles. */
924 if (null_bubble(tmp)) {
925 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
931 touch = get_closest_bubble(nextbub);
932 while (! null_bubble(touch)) {
933 switch (merge_bubbles(nextbub, touch)) {
935 /* touch ate nextbub and survived */
939 /* nextbub ate touch and survived */
942 /* somebody ate someone else but they exploded */
943 nextbub = (Bubble *)NULL;
946 /* something went wrong */
947 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
950 /* Check to see if there are any other bubbles still in the area
951 and if we need to do this all over again for them. */
952 if (! null_bubble(nextbub))
953 touch = get_closest_bubble(nextbub);
955 touch = (Bubble *)NULL;
961 get_length_of_bubble_list(bb)
967 while (! null_bubble(tmp)) {
977 * Pixmap stuff used regardless of whether file I/O is available. Must
978 * still check for XPM, though!
985 /* Free resources associated with XPM */
991 fprintf(stderr, "free_pixmaps() called in simple mode\n");
994 printf("free_pixmaps()\n");
997 for(i = 0; i < (num_bubble_pixmaps - 1); i++) {
998 XFreePixmap(defdsp, step_pixmaps[i]->ball);
999 XFreePixmap(defdsp, step_pixmaps[i]->shape_mask);
1000 XFreeGC(defdsp, step_pixmaps[i]->draw_gc);
1001 XFreeGC(defdsp, step_pixmaps[i]->erase_gc);
1002 XFreeColors(defdsp, defcmap, step_pixmaps[i]->xpmattrs.pixels,
1003 step_pixmaps[i]->xpmattrs.npixels, 0);
1004 XpmFreeAttributes(&step_pixmaps[i]->xpmattrs);
1011 /* This gets called when SIGINT or SIGTERM is received */
1021 /* Called when SEGV detected. Hmmmmm.... */
1024 fprintf(stderr, "SEGV detected! : %d\n", a);
1031 * Pixmaps without file I/O (but do have XPM)
1035 pixmap_sort(head, numelems)
1038 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1039 the numelems length array with first element at head into order of radius.
1043 Bubble_Step *least = 0;
1044 int minradius = INT_MAX;
1047 for (i = 0; i < numelems; i++) {
1048 if (head[i]->radius < minradius) {
1050 minradius = head[i]->radius;
1053 if (*head != least) {
1054 memcpy(&tmp, least, sizeof(Bubble_Step));
1055 memcpy(least, *head, sizeof(Bubble_Step));
1056 memcpy(*head, &tmp, sizeof(Bubble_Step));
1060 pixmap_sort(&head[1], numelems-1);
1068 return (i2 + (i2 - i1));
1072 make_pixmap_array(list)
1074 /* From a linked list of bubbles construct the array step_pixmaps */
1076 Bubble_Step *tmp = list;
1082 if (list == (Bubble_Step *)NULL) {
1083 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1087 num_bubble_pixmaps = 1;
1088 while(tmp->next != (Bubble_Step *)NULL) {
1090 ++num_bubble_pixmaps;
1093 if (num_bubble_pixmaps < 2) {
1094 fprintf(stderr, "Must be at least two bubbles in file\n");
1098 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1099 sizeof(Bubble_Step *));
1101 /* Copy them blindly into the array for sorting. */
1105 step_pixmaps[ind++] = tmp;
1107 } while(tmp != (Bubble_Step *)NULL);
1109 /* We make another bubble beyond the ones with pixmaps so that the final
1110 bubble hangs around and doesn't pop immediately. It's radius and area
1111 are found by extrapolating from the largest two bubbles with pixmaps. */
1113 step_pixmaps[num_bubble_pixmaps] =
1114 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1115 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1117 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1120 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1121 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1125 step_pixmaps[num_bubble_pixmaps]->radius =
1126 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1127 step_pixmaps[num_bubble_pixmaps-1]->radius);
1128 step_pixmaps[num_bubble_pixmaps]->area =
1129 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1133 /* Now check for correct order */
1134 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1136 if (step_pixmaps[ind]->radius < prevrad) {
1137 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1141 prevrad = step_pixmaps[ind]->radius;
1146 #ifndef NO_DEFAULT_BUBBLE
1148 make_pixmap_from_default(pixmap_data, bl)
1151 /* Read pixmap data which has been compiled into the program and a pointer
1152 to which has been passed.
1154 This is virtually copied verbatim from make_pixmap_from_file() above and
1155 changes made to either should be propagated onwards! */
1161 if (pixmap_data == (char **)0) {
1162 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1167 if (bl == (Bubble_Step *)NULL) {
1168 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1172 bl->xpmattrs.closeness = 40000;
1173 bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
1174 bl->xpmattrs.colormap = defcmap;
1176 /* This is the only line which is different from make_pixmap_from_file() */
1177 result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
1178 &bl->shape_mask, &bl->xpmattrs);
1182 fprintf(stderr, "xpm: color substitution performed\n");
1185 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1186 bl->area = calc_bubble_area(bl->radius);
1188 case XpmColorFailed:
1189 fprintf(stderr, "xpm: color allocation failed\n");
1192 fprintf(stderr, "xpm: out of memory\n");
1195 fprintf(stderr, "xpm: unknown error code %d\n", result);
1199 gcv.plane_mask = AllPlanes;
1200 gcv.foreground = default_fg_pixel;
1201 gcv.function = GXcopy;
1202 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1203 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1205 gcv.foreground = default_bg_pixel;
1206 gcv.function = GXcopy;
1207 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1208 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1212 default_to_pixmaps(void)
1213 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1216 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1217 Bubble_Step *newpix, *tmppix;
1220 /* Make sure pixmaps are freed when program is terminated */
1221 /* This is when I hit ^C */
1222 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1223 signal(SIGINT, onintr);
1224 /* xscreensaver sends SIGTERM */
1225 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1226 signal(SIGTERM, onintr);
1228 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
1229 printf("Setting signal handler for SIGSEGV\n");
1230 signal(SIGSEGV, onsegv);
1232 printf("Didn't set signal hanlder for SIGSEGV\n");
1236 for (i = 0; i < num_default_bubbles; i++) {
1237 pixpt = default_bubbles[i];
1238 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1239 make_pixmap_from_default(pixpt, newpix);
1240 /* Now add to list */
1241 if (pixmap_list == (Bubble_Step *)NULL) {
1242 pixmap_list = newpix;
1244 tmppix = pixmap_list;
1245 while (tmppix->next != (Bubble_Step *)NULL)
1246 tmppix = tmppix->next;
1247 tmppix->next = newpix;
1249 newpix->next = (Bubble_Step *)NULL;
1252 /* Finally construct step_pixmaps[] */
1253 make_pixmap_array(pixmap_list);
1256 #endif /* NO_DEFAULT_BUBBLE */
1258 #endif /* HAVE_XPM */
1269 /* Like opendir() but checks for things so we don't have to do it multiple
1270 times in the code. */
1274 if (name == (char *)NULL) {
1275 fprintf(stderr, "NULL directory name\n");
1279 if ((rv = opendir(name)) == NULL) {
1290 /* Check to see if we can use the named file. This was broken under Linux
1291 1.3.45 but seems to be okay under 1.3.54. The parameter "name" was being
1292 trashed if the file didn't exist. Yeah, I know 1.3.x are development
1298 if ((fd = open(name, O_RDONLY)) == -1) {
1308 get_random_name(dir)
1310 /* Pick an appropriate file at random out of the files in the directory dir */
1317 char buf[PATH_BUF_SIZE];
1320 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1321 return (char *)NULL;
1323 while ((dp = readdir(dfd)) != NULL) {
1324 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1326 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1327 fprintf(stderr, "name %s/%s too long\n", dir, DIRENT_NAME);
1330 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1331 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1334 if (regular_file(buf))
1338 if (numentries == 0) {
1339 fprintf(stderr, "No suitable files found in %s\n", dir);
1340 return (char *)NULL;
1342 entnum = ya_random() % numentries;
1345 if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1346 return (char *)NULL;
1347 while ((dp = readdir(dfd)) != NULL) {
1348 if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1350 if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1351 /* We warned about this previously */
1354 if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1355 fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1358 if (regular_file(buf)) {
1360 rv = (char *)xmalloc(1024 * sizeof(char));
1368 /* We've screwed up if we reach here - someone must have deleted all the
1369 files while we were counting them... */
1370 fprintf(stderr, "get_random_name(): Oops!\n");
1375 read_line(fd, buf, bufsize)
1379 /* A line is read from fd until a '\n' is found or EOF is reached. (*buf)
1380 is initially of length bufsize and is extended by bufsize chars if need
1381 be (for as many times as it takes). */
1390 rv = read(fd, &x, 1);
1392 perror("read_line(): ");
1394 } else if (rv == 0) {
1397 } else if (x == '\n') {
1402 if (pos == (size - 1)) {
1403 /* We've come to the end of the space */
1404 newbuf = (char *)xmalloc((size+bufsize) * sizeof(char));
1405 strncpy(newbuf, *buf, (size - 1));
1415 create_temp_file(name)
1417 /* Create a temporary file in /tmp and return a filedescriptor to it */
1421 if (*name != (char *)NULL)
1424 if ((*name = tempnam("/tmp", "abxdfes")) == (char *)NULL) {
1425 fprintf(stderr, "Couldn't make new temporary file\n");
1428 /* printf("Temp file created : %s\n", *name); */
1429 if ((rv = creat(*name, 0644)) == -1) {
1430 fprintf(stderr, "Couldn't open temporary file\n");
1440 make_pixmap_from_file(fname, bl)
1443 /* Read the pixmap in file fname into structure bl which must already
1449 if (bl == (Bubble_Step *)NULL) {
1450 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1454 bl->xpmattrs.closeness = 40000;
1455 bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
1456 bl->xpmattrs.colormap = defcmap;
1458 result = XpmReadFileToPixmap(defdsp, defwin, fname, &bl->ball,
1459 &bl->shape_mask, &bl->xpmattrs);
1463 fprintf(stderr, "xpm: color substitution performed\n");
1466 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1467 bl->area = calc_bubble_area(bl->radius);
1469 case XpmColorFailed:
1470 fprintf(stderr, "xpm: color allocation failed\n");
1473 fprintf(stderr, "xpm: out of memory\n");
1476 fprintf(stderr, "xpm: unknown error code %d\n", result);
1480 gcv.plane_mask = AllPlanes;
1481 gcv.foreground = default_fg_pixel;
1482 gcv.function = GXcopy;
1483 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1484 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1486 gcv.foreground = default_bg_pixel;
1487 gcv.function = GXcopy;
1488 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1489 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1491 #endif /* BUBBLES_IO */
1494 read_file_to_pixmaps(fname)
1496 /* Read the pixmaps contained in the file fname into memory. THESE SHOULD
1497 BE UNCOMPRESSED AND READY TO GO! */
1499 int fd, tmpfd=0, rv;
1502 char *buf = (char *)NULL;
1503 char *tmpname = (char *)NULL;
1504 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1505 Bubble_Step *newpix, *tmppix;
1507 /* We first create a linked list of pixmaps before allocating
1508 memory for the array */
1510 if ((fd = open(fname, O_RDONLY)) == -1) {
1511 fprintf(stderr, "Couldn't open %s\n", fname);
1515 /* Make sure pixmaps are freed when program is terminated */
1516 /* This is when I hit ^C */
1517 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1518 signal(SIGINT, onintr);
1519 /* xscreensaver sends SIGTERM */
1520 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1521 signal(SIGTERM, onintr);
1523 if (signal(SIGSEGV, SIGN_IGN) != SIG_IGN)
1524 signal(SIGSEGV, onsegv);
1531 buf = (char *)malloc(READ_LINE_BUF_SIZE * sizeof(char));
1533 switch ((rv = read_line(fd, &buf, READ_LINE_BUF_SIZE))) {
1535 fprintf(stderr, "An I/O error occurred\n");
1539 fprintf(stderr, "EOF occurred inside an XPM block\n");
1546 if (strncmp("};", buf, 2) == 0) {
1548 write(tmpfd, buf, strlen(buf));
1549 write(tmpfd, "\n", 1);
1551 /* Now process the tmpfile */
1552 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1553 make_pixmap_from_file(tmpname, newpix);
1554 /* Now add to list */
1555 if (pixmap_list == (Bubble_Step *)NULL) {
1556 pixmap_list = newpix;
1558 tmppix = pixmap_list;
1559 while (tmppix->next != (Bubble_Step *)NULL)
1560 tmppix = tmppix->next;
1561 tmppix->next = newpix;
1563 newpix->next = (Bubble_Step *)NULL;
1566 write(tmpfd, buf, strlen(buf));
1567 write(tmpfd, "\n", 1);
1570 if (strncmp("/* XPM */", buf, 9) == 0) {
1571 tmpfd = create_temp_file(&tmpname);
1572 /* This proves XPM's performance is kinda pathetic */
1574 printf("New XPM detected : %s, fd=%d\n", tmpname, tmpfd);
1579 write(tmpfd, buf, strlen(buf));
1580 write(tmpfd, "\n", 1);
1584 fprintf(stderr, "read_line returned unknown code %d\n", rv);
1592 if (buf != (char *)NULL)
1594 if (tmpname != (char *)NULL)
1598 fprintf(stderr, "There was no XPM data in the file %s\n", fname);
1602 /* Finally construct step_pixmaps[] */
1603 make_pixmap_array(pixmap_list);
1609 /* Forks a shell to execute "command" then waits for command to finish */
1611 int pid, status, wval;
1613 switch(pid=fork()) {
1615 if (execlp(BOURNESH, BOURNESH, "-c", command, (char *)NULL) == -1) {
1616 fprintf(stderr, "Couldn't exec shell %s\n", BOURNESH);
1619 /* fall through if execlp() fails */
1625 while ((wval = wait(&status)) != pid)
1634 uncompress_file(current, namebuf)
1637 /* If the file current is compressed (i.e. its name ends in .gz or .Z,
1638 no check is made to see if it is actually a compressed file...) then a
1639 new temporary file is created for it and it is decompressed into there,
1640 returning the name of the file to namebuf, else current is returned in
1644 char *tname = (char *)NULL;
1645 char argbuf[COMMAND_BUF_SIZE];
1647 if (((strlen(current) >=4) &&
1648 (strncmp(¤t[strlen(current)-3], ".gz", 3) == 0)) ||
1649 ((strlen(current) >=3) &&
1650 (strncmp(¤t[strlen(current)-2], ".Z", 2) == 0))) {
1651 fd = create_temp_file(&tname);
1652 /* close immediately but don't unlink so we should have a zero length
1653 file in /tmp which we can append to */
1655 if (sprintf(argbuf, "%s -dc %s > %s", GZIP, current, tname) >
1656 (COMMAND_BUF_SIZE-1)) {
1657 fprintf(stderr, "command buffer overflowed in uncompress_file()\n");
1661 strcpy(namebuf, tname);
1663 strcpy(namebuf, current);
1668 #endif /* BUBBLES_IO */
1678 /* Get the appropriate X resources and warn about any inconsistencies. */
1686 #endif /* HAVE_XPM */
1687 #endif /* BUBBLES_IO */
1689 threed = get_boolean_resource("3D", "Boolean");
1690 quiet = get_boolean_resource("quiet", "Boolean");
1691 simple = get_boolean_resource("simple", "Boolean");
1692 /* Forbid rendered bubbles on monochrome displays */
1693 if ((mono_p) && (! simple)) {
1695 fprintf(stderr, "Rendered bubbles not supported on monochrome \
1699 delay = get_integer_resource("delay", "Integer");
1700 nodelay = get_boolean_resource("nodelay", "Boolean");
1706 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1707 DefaultColormap(dpy,
1708 DefaultScreen(dpy)));
1709 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1710 DefaultColormap(dpy,
1711 DefaultScreen(dpy)));
1715 broken = get_boolean_resource("broken", "Boolean");
1718 fprintf(stderr, "-broken not available in simple mode\n");
1723 broken = get_boolean_resource("broken", "Boolean");
1725 pixmap_file = get_string_resource("file", "File");
1726 dirname = get_string_resource("directory", "Directory");
1727 #ifdef NO_DEFAULT_BUBBLE
1728 /* Must specify -file or -directory if no default bubble compiled in */
1729 if (strcmp(pixmap_file, "(default)") != 0) {
1730 } else if (strcmp(dirname, "(default)") != 0) {
1731 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1732 /* Die if we can't open directory - make it consistent with -file
1733 when it fails, rather than falling back to default. */
1737 fprintf(stderr, "No default bubble compiled in - use -file or \
1742 if (strcmp(pixmap_file, "(default)") != 0) {
1743 } else if (strcmp(dirname, "(default)") != 0) {
1744 if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1748 /* Use default bubble */
1749 use_default_bubble = 1;
1751 #endif /* NO_DEFAULT_BUBBLE */
1753 use_default_bubble = 1;
1754 #endif /* BUBBLES_IO */
1755 #endif /* HAVE_XPM */
1760 init_bubbles (dpy, window)
1765 XWindowAttributes xgwa;
1768 char uncompressed[1024];
1769 #endif /* BUBBLES_IO */
1778 XGetWindowAttributes (dpy, window, &xgwa);
1781 printf("sizof(int) on this platform is %d\n", sizeof(int));
1782 printf("sizof(long) on this platform is %d\n", sizeof(long));
1785 screen_width = xgwa.width;
1786 screen_height = xgwa.height;
1787 screen_depth = xgwa.depth;
1788 defcmap = xgwa.colormap;
1791 /* These are pretty much plucked out of the air */
1792 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1794 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1796 /* Some trivial values */
1797 if (bubble_min_radius < 1)
1798 bubble_min_radius = 1;
1799 if (bubble_max_radius <= bubble_min_radius)
1800 bubble_max_radius = bubble_min_radius + 1;
1802 mesh_length = (2 * bubble_max_radius) + 3;
1804 /* store area of each bubble of certain radius as number of 1/10s of
1805 a pixel area. PI is defined in <math.h> */
1806 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1807 for (i = 0; i < bubble_min_radius; i++)
1808 bubble_areas[i] = 0;
1809 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1810 bubble_areas[i] = calc_bubble_area(i);
1812 mesh_length = (2 * bubble_max_radius) + 3;
1815 fprintf(stderr, "Bug: simple mode code not set but HAVE_XPM not \
1819 /* Make sure all #ifdef sort of things have been taken care of in
1821 if (use_default_bubble) {
1822 #ifdef NO_DEFAULT_BUBBLE
1823 fprintf(stderr, "Bug: use_default_bubble and NO_DEFAULT_BUBBLE both \
1827 default_to_pixmaps();
1828 #endif /* NO_DEFAULT_BUBBLE */
1830 /* Set mesh length */
1831 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1834 if (! regular_file(pixmap_file)) {
1835 /* perror() in regular_file printed error message */
1838 uncompress_file(pixmap_file, uncompressed);
1839 read_file_to_pixmaps(uncompressed);
1840 if (strcmp(pixmap_file, uncompressed))
1841 unlink(uncompressed);
1843 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1845 fprintf(stderr, "Bug: use_default_bubble is not defined yet I/O is not \
1848 #endif /* BUBBLES_IO */
1850 #endif /* HAVE_XPM */
1852 /* Am I missing something in here??? */
1855 mesh_width = (screen_width / mesh_length) + 1;
1856 mesh_height = (screen_height / mesh_length) + 1;
1857 mesh_cells = mesh_width * mesh_height;
1860 calculate_adjacent_list();
1864 /* Graphics contexts for simple mode */
1866 gcv.foreground = default_fg_pixel;
1867 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1868 gcv.foreground = default_bg_pixel;
1869 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1872 XClearWindow (dpy, window);
1876 bubbles (dpy, window)
1884 insert_new_bubble(tmp);
1891 screenhack (dpy, window)
1895 init_bubbles (dpy, window);
1897 bubbles (dpy, window);