1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.16 1998/11/19 07:25:01 jwz Exp $*/
6 * Copyright (C) 1995-1996 James Macnicol
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation. No representations are made about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
18 * I got my original inspiration for this by looking at the bottom of a
19 * frying pan while something was cooking and watching the little bubbles
20 * coming off the bottom of the pan as the oil was boiling joining together
21 * to form bigger bubbles and finally to *pop* and disappear. I had some
22 * time on my hands so I wrote this little xscreensaver module to imitate
23 * it. Now that it's done it reminds me more of the bubbles you get in
24 * a glass of fizzy soft drink.....
26 * The problem seemed to be that the position/size etc. of all the bubbles
27 * on the screen had to be remembered and searched through to find when
28 * bubbles hit each other and combined. To do this more efficiently, the
29 * window/screen is divided up into a square mesh of side length mesh_length
30 * and separate lists of bubbles contained in each cell of the mesh are
31 * kept. Only the cells in the immediate vicinity of the bubble in question
32 * are searched. This should make things more efficient although the whole
33 * thing seems to use up too much CPU, but then I'm using an ancient PC so
34 * perhaps it's not surprising .
35 * (Six months after I wrote the above I now have a Pentium with PCI graphics
36 * and things are _much_ nicer.)
38 * Author: James Macnicol
39 * Internet E-mail : J.Macnicol@student.anu.edu.au
43 #include "screenhack.h"
52 # include <sys/wait.h>
54 # if __DECC_VER >= 50200000
55 # include <sys/wait.h>
72 extern void init_default_bubbles(void);
73 extern int num_default_bubbles;
74 extern char **default_bubbles[];
76 char *progclass = "Bubbles";
90 XrmOptionDescRec options [] = {
91 { "-simple", ".simple", XrmoptionNoArg, "true" },
93 { "-broken", ".broken", XrmoptionNoArg, "true" },
95 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
96 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
97 { "-3D", ".3D", XrmoptionNoArg, "true" },
98 { "-delay", ".delay", XrmoptionSepArg, 0 },
106 static Bubble **mesh;
107 static int mesh_length;
108 static int mesh_width;
109 static int mesh_height;
110 static int mesh_cells;
112 static int **adjacent_list;
114 static int screen_width;
115 static int screen_height;
116 static int screen_depth;
117 static unsigned int default_fg_pixel, default_bg_pixel;
119 * I know it's not elegant to save this stuff in global variables
120 * but we need it for the signal handler.
122 static Display *defdsp;
123 static Window defwin;
124 static Colormap defcmap;
125 static Visual *defvisual;
127 /* For simple mode only */
128 static int bubble_min_radius;
129 static int bubble_max_radius;
130 static long *bubble_areas;
131 static GC draw_gc, erase_gc;
134 static int num_bubble_pixmaps;
135 static Bubble_Step **step_pixmaps;
136 #endif /* HAVE_XPM */
140 static Bool simple = False;
142 static Bool simple = True;
144 static Bool broken = False;
145 static Bool quiet = False;
146 static Bool threed = False;
150 * To prevent forward references, some stuff is up here
154 calc_bubble_area(int r)
155 /* Calculate the area of a bubble of radius r */
159 10.0 * PI * (double)r * (double)r * (double)r);
162 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
164 return (long)(10.0 * PI * (double)r * (double)r);
173 if ((ret = malloc(size)) == NULL) {
174 fprintf(stderr, "%s: out of memory\n", progname);
182 die_bad_bubble(Bubble *bb)
183 /* This is for use with GDB */
185 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
191 null_bubble(Bubble *bb)
192 /* Returns true if the pointer passed is NULL. If not then this checks to
193 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
194 number is set correctly. This only a sanity check for debugging and is
195 turned off if DEBUG isn't set. */
197 if (bb == (Bubble *)NULL)
200 if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
201 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
204 if (bb->magic != BUBBLE_MAGIC) {
205 fprintf(stderr, "Magic = %d\n", bb->magic);
209 if ((bb->x < 0) || (bb->x > screen_width) ||
210 (bb->y < 0) || (bb->y > screen_height) ||
211 (bb->radius < bubble_min_radius) || (bb->radius >
212 bubble_max_radius)) {
214 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
215 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
220 if ((bb->x < 0) || (bb->x > screen_width) ||
221 (bb->y < 0) || (bb->y > screen_height) ||
222 (bb->radius < step_pixmaps[0]->radius) ||
223 (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
225 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
226 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
229 #endif /* HAVE_XPM */
237 print_bubble_list(Bubble *bb)
238 /* Print list of where all the bubbles are. For debugging purposes only. */
240 if (! null_bubble(bb)) {
241 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
242 print_bubble_list(bb->next);
248 add_bubble_to_list(Bubble **list, Bubble *bb)
249 /* Take a pointer to a list of bubbles and stick bb at the head of the
252 Bubble *head = *list;
254 if (null_bubble(head)) {
255 bb->prev = (Bubble *)NULL;
256 bb->next = (Bubble *)NULL;
259 bb->prev = (Bubble *)NULL;
273 /* Setup the mesh of bubbles */
277 mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
278 for (i = 0; i < mesh_cells; i++)
279 mesh[i] = (Bubble *)NULL;
283 cell_to_mesh(int x, int y)
284 /* convert cell coordinates to mesh index */
287 if ((x < 0) || (y < 0)) {
288 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
292 return ((mesh_width * y) + x);
296 mesh_to_cell(int mi, int *cx, int *cy)
297 /* convert mesh index into cell coordinates */
299 *cx = mi % mesh_width;
300 *cy = mi / mesh_width;
304 pixel_to_mesh(int x, int y)
305 /* convert screen coordinates into mesh index */
307 return cell_to_mesh((x / mesh_length), (y / mesh_length));
311 verify_mesh_index(int x, int y)
312 /* check to see if (x,y) is in the mesh */
314 if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
316 return (cell_to_mesh(x, y));
321 print_adjacents(int *adj)
322 /* Print a list of the cells calculated above. For debugging only. */
327 for (i = 0; i < 8; i++)
328 printf("%d ", adj[i]);
334 add_to_mesh(Bubble *bb)
335 /* Add the given bubble to the mesh by sticking it on the front of the
336 list. bb is already allocated so no need to malloc() anything, just
340 if (null_bubble(bb)) {
341 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
346 add_bubble_to_list(&mesh[bb->cell_index], bb);
352 /* Print the contents of the mesh */
356 for (i = 0; i < mesh_cells; i++) {
357 if (! null_bubble(mesh[i])) {
358 printf("Mesh cell %d\n", i);
359 print_bubble_list(mesh[i]);
366 /* Check to see if the mesh is Okay. For debugging only. */
371 for (i = 0; i < mesh_cells; i++) {
373 while (! null_bubble(b))
380 /* Count how many bubbles there are in total. For debugging only. */
386 for (i = 0; i < mesh_cells; i++) {
388 while (! null_bubble(b)) {
399 calculate_adjacent_list (void)
400 /* Calculate the list of cells adjacent to a particular cell for use
406 adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
407 for (i = 0; i < mesh_cells; i++) {
408 adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
409 mesh_to_cell(i, &ix, &iy);
410 adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
411 adjacent_list[i][1] = verify_mesh_index(++ix, iy);
412 adjacent_list[i][2] = verify_mesh_index(++ix, iy);
413 adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
414 adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
415 adjacent_list[i][5] = verify_mesh_index(--ix, iy);
416 adjacent_list[i][6] = verify_mesh_index(--ix, iy);
417 adjacent_list[i][7] = verify_mesh_index(ix, --iy);
418 adjacent_list[i][8] = i;
424 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
433 maxarea = bubble_areas[bubble_max_radius+1];
435 maxarea = step_pixmaps[num_bubble_pixmaps]->area;
437 maxarea = bubble_areas[bubble_max_radius+1];
438 #endif /* HAVE_XPM */
439 maxvalue = (double)screen_width * 2.0 * (double)maxarea;
440 factor = (long)ceil(maxvalue / (double)LONG_MAX);
442 /* Overflow will occur in weighted_mean(). We must divide areas
443 each by factor so it will never do so. */
446 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
447 bubble_areas[i] /= factor;
448 if (bubble_areas[i] == 0)
452 for (i = 0; i <= num_bubble_pixmaps; i++) {
454 printf("area = %ld", step_pixmaps[i]->area);
456 step_pixmaps[i]->area /= factor;
457 if (step_pixmaps[i]->area == 0)
458 step_pixmaps[i]->area = 1;
460 printf("-> %ld\n", step_pixmaps[i]->area);
465 for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
466 bubble_areas[i] /= factor;
467 if (bubble_areas[i] == 0)
470 #endif /* HAVE_XPM */
473 printf("maxarea = %ld\n", maxarea);
474 printf("maxvalue = %g\n", maxvalue);
475 printf("LONG_MAX = %ld\n", LONG_MAX);
476 printf("factor = %ld\n", factor);
486 /* Add a new bubble at some random position on the screen of the smallest
489 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
491 /* Can't use null_bubble() here since magic number hasn't been set */
492 if (rv == (Bubble *)NULL) {
493 fprintf(stderr, "Ran out of memory!\n");
498 rv->radius = bubble_min_radius;
499 rv->area = bubble_areas[bubble_min_radius];
503 rv->radius = step_pixmaps[0]->radius;
504 rv->area = step_pixmaps[0]->area;
505 #endif /* HAVE_XPM */
508 rv->magic = BUBBLE_MAGIC;
509 rv->x = random() % screen_width;
510 rv->y = random() % screen_height;
511 rv->cell_index = pixel_to_mesh(rv->x, rv->y);
517 show_bubble(Bubble *bb)
518 /* paint the bubble on the screen */
520 if (null_bubble(bb)) {
521 fprintf(stderr, "NULL bubble passed to show_bubble\n");
529 XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
530 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
534 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
535 (bb->x - bb->radius),
536 (bb->y - bb->radius));
538 XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin,
539 step_pixmaps[bb->step]->draw_gc,
540 0, 0, (bb->radius * 2),
542 (bb->x - bb->radius),
543 (bb->y - bb->radius));
544 #endif /* HAVE_XPM */
550 hide_bubble(Bubble *bb)
551 /* erase the bubble */
553 if (null_bubble(bb)) {
554 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
562 XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
563 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
568 XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
569 (bb->x - bb->radius), (bb->y - bb->radius));
571 XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
572 (bb->x - bb->radius),
573 (bb->y - bb->radius),
577 #endif /* HAVE_XPM */
583 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
584 /* Delete an individual bubble, adjusting list of bubbles around it.
585 If keep_bubble is true then the bubble isn't actually deleted. We
586 use this to allow bubbles to change mesh cells without reallocating,
587 (it needs this when two bubbles collide and the centre position is
588 recalculated, and this may stray over a mesh boundary). */
590 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
591 bb->prev->next = bb->next;
592 bb->next->prev = bb->prev;
593 } else if ((!null_bubble(bb->prev)) &&
594 (null_bubble(bb->next))) {
595 bb->prev->next = (Bubble *)NULL;
596 bb->next = mesh[bb->cell_index];
597 } else if ((null_bubble(bb->prev)) &&
598 (!null_bubble(bb->next))) {
599 bb->next->prev = (Bubble *)NULL;
600 mesh[bb->cell_index] = bb->next;
601 bb->next = mesh[bb->cell_index];
603 /* Only item on list */
604 mesh[bb->cell_index] = (Bubble *)NULL;
612 /* Saves ugly inline code */
614 return ((unsigned long)x * (unsigned long)x);
618 get_closest_bubble(Bubble *bb)
619 /* Find the closest bubble touching the this bubble, NULL if none are
622 Bubble *rv = (Bubble *)NULL;
624 unsigned long separation2, touchdist2;
626 unsigned long closest2 = ULONG_MAX;
630 if (null_bubble(bb)) {
631 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
637 for (i = 0; i < 9; i++) {
638 /* There is a bug here where bb->cell_index is negaitve.. */
640 if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
641 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
645 /* printf("%d,", bb->cell_index); */
646 if (adjacent_list[bb->cell_index][i] != -1) {
647 tmp = mesh[adjacent_list[bb->cell_index][i]];
648 while (! null_bubble(tmp)) {
652 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
653 /* Add extra leeway so circles _never_ overlap */
654 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
655 if ((separation2 <= touchdist2) && (separation2 <
658 closest2 = separation2;
677 long_div_round(long num, long dem)
682 if ((num < 0) || (dem < 0)) {
683 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
691 if (moddo > (dem / 2))
695 if ((divvie < 0) || (moddo < 0)) {
696 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
706 weighted_mean(int n1, int n2, long w1, long w2)
707 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
710 if ((w1 <= 0) || (w2 <= 0)) {
712 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
717 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
722 bubble_eat(Bubble *diner, Bubble *food)
723 /* The diner eats the food. Returns true (1) if the diner still exists */
729 if ((null_bubble(diner)) || (null_bubble(food))) {
730 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
735 /* We hide the diner even in the case that it doesn't grow so that
736 if the food overlaps its boundary it is replaced. This could
737 probably be solved by letting bubbles eat others which are close
738 but not quite touching. It's probably worth it, too, since we
739 would then not have to redraw bubbles which don't change in
744 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
745 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
746 newmi = pixel_to_mesh(diner->x, diner->y);
747 diner->area += food->area;
748 delete_bubble_in_mesh(food, DELETE_BUBBLE);
750 if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
751 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
755 if ((! simple) && (diner->area >
756 step_pixmaps[num_bubble_pixmaps]->area)) {
757 delete_bubble_in_mesh(diner, DELETE_BUBBLE);
760 #endif /* HAVE_XPM */
763 if (diner->area > bubble_areas[diner->radius + 1]) {
764 /* Move the bubble to a new radius */
766 while (diner->area > bubble_areas[i+1])
773 if (diner->area > step_pixmaps[diner->step+1]->area) {
775 while (diner->area > step_pixmaps[i+1]->area)
778 diner->radius = step_pixmaps[diner->step]->radius;
781 #endif /* HAVE_XPM */
784 /* Now adjust locations and cells if need be */
785 if (newmi != diner->cell_index) {
786 delete_bubble_in_mesh(diner, KEEP_BUBBLE);
787 diner->cell_index = newmi;
795 merge_bubbles(Bubble *b1, Bubble *b2)
796 /* These two bubbles merge into one. If the first one wins out return
797 1 else return 2. If there is no winner (it explodes) then return 0 */
805 if ((null_bubble(b1) || null_bubble(b2))) {
806 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
813 delete_bubble_in_mesh(b1, DELETE_BUBBLE);
817 if (b1size > b2size) {
818 switch (bubble_eat(b1, b2)) {
828 } else if (b1size < b2size) {
829 switch (bubble_eat(b2, b1)) {
840 if ((random() % 2) == 0) {
841 switch (bubble_eat(b1, b2)) {
852 switch (bubble_eat(b2, b1)) {
864 fprintf(stderr, "An error occurred in merge_bubbles()\n");
869 insert_new_bubble(Bubble *tmp)
870 /* Calculates which bubbles are eaten when a new bubble tmp is
871 inserted. This is called recursively in case when a bubble grows
872 it eats others. Careful to pick out disappearing bubbles. */
878 if (null_bubble(tmp)) {
879 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
885 touch = get_closest_bubble(nextbub);
886 while (! null_bubble(touch)) {
887 switch (merge_bubbles(nextbub, touch)) {
889 /* touch ate nextbub and survived */
893 /* nextbub ate touch and survived */
896 /* somebody ate someone else but they exploded */
897 nextbub = (Bubble *)NULL;
900 /* something went wrong */
901 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
904 /* Check to see if there are any other bubbles still in the area
905 and if we need to do this all over again for them. */
906 if (! null_bubble(nextbub))
907 touch = get_closest_bubble(nextbub);
909 touch = (Bubble *)NULL;
915 get_length_of_bubble_list(Bubble *bb)
920 while (! null_bubble(tmp)) {
930 * Pixmap stuff used regardless of whether file I/O is available. Must
931 * still check for XPM, though!
937 * Pixmaps without file I/O (but do have XPM)
941 pixmap_sort(Bubble_Step **head, int numelems)
942 /* Couldn't get qsort to work right with this so I wrote my own. This puts
943 the numelems length array with first element at head into order of radius.
947 Bubble_Step *least = 0;
948 int minradius = INT_MAX;
951 for (i = 0; i < numelems; i++) {
952 if (head[i]->radius < minradius) {
954 minradius = head[i]->radius;
957 if (*head != least) {
958 memcpy(&tmp, least, sizeof(Bubble_Step));
959 memcpy(least, *head, sizeof(Bubble_Step));
960 memcpy(*head, &tmp, sizeof(Bubble_Step));
964 pixmap_sort(&head[1], numelems-1);
968 extrapolate(int i1, int i2)
970 return (i2 + (i2 - i1));
974 make_pixmap_array(Bubble_Step *list)
975 /* From a linked list of bubbles construct the array step_pixmaps */
977 Bubble_Step *tmp = list;
983 if (list == (Bubble_Step *)NULL) {
984 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
988 num_bubble_pixmaps = 1;
989 while(tmp->next != (Bubble_Step *)NULL) {
991 ++num_bubble_pixmaps;
994 if (num_bubble_pixmaps < 2) {
995 fprintf(stderr, "Must be at least two bubbles in file\n");
999 step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) *
1000 sizeof(Bubble_Step *));
1002 /* Copy them blindly into the array for sorting. */
1006 step_pixmaps[ind++] = tmp;
1008 } while(tmp != (Bubble_Step *)NULL);
1010 /* We make another bubble beyond the ones with pixmaps so that the final
1011 bubble hangs around and doesn't pop immediately. It's radius and area
1012 are found by extrapolating from the largest two bubbles with pixmaps. */
1014 step_pixmaps[num_bubble_pixmaps] =
1015 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1016 step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1018 pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1021 if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1022 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1026 step_pixmaps[num_bubble_pixmaps]->radius =
1027 extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1028 step_pixmaps[num_bubble_pixmaps-1]->radius);
1029 step_pixmaps[num_bubble_pixmaps]->area =
1030 calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1034 /* Now check for correct order */
1035 for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1037 if (step_pixmaps[ind]->radius < prevrad) {
1038 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1042 prevrad = step_pixmaps[ind]->radius;
1048 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1049 /* Read pixmap data which has been compiled into the program and a pointer
1050 to which has been passed.
1052 This is virtually copied verbatim from make_pixmap_from_file() above and
1053 changes made to either should be propagated onwards! */
1059 if (pixmap_data == (char **)0) {
1060 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1065 if (bl == (Bubble_Step *)NULL) {
1066 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1070 bl->xpmattrs.valuemask = 0;
1073 bl->xpmattrs.valuemask |= XpmCloseness;
1074 bl->xpmattrs.closeness = 40000;
1077 bl->xpmattrs.valuemask |= XpmVisual;
1078 bl->xpmattrs.visual = defvisual;
1081 bl->xpmattrs.valuemask |= XpmDepth;
1082 bl->xpmattrs.depth = screen_depth;
1085 bl->xpmattrs.valuemask |= XpmColormap;
1086 bl->xpmattrs.colormap = defcmap;
1090 /* This is the only line which is different from make_pixmap_from_file() */
1091 result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
1092 &bl->shape_mask, &bl->xpmattrs);
1096 fprintf(stderr, "xpm: color substitution performed\n");
1099 bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1100 bl->area = calc_bubble_area(bl->radius);
1102 case XpmColorFailed:
1103 fprintf(stderr, "xpm: color allocation failed\n");
1106 fprintf(stderr, "xpm: out of memory\n");
1109 fprintf(stderr, "xpm: unknown error code %d\n", result);
1113 gcv.plane_mask = AllPlanes;
1114 gcv.foreground = default_fg_pixel;
1115 gcv.function = GXcopy;
1116 bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1117 XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1119 gcv.foreground = default_bg_pixel;
1120 gcv.function = GXcopy;
1121 bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1122 XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);
1126 default_to_pixmaps (void)
1127 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1130 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1131 Bubble_Step *newpix, *tmppix;
1134 init_default_bubbles();
1136 for (i = 0; i < num_default_bubbles; i++) {
1137 pixpt = default_bubbles[i];
1138 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1139 make_pixmap_from_default(pixpt, newpix);
1140 /* Now add to list */
1141 if (pixmap_list == (Bubble_Step *)NULL) {
1142 pixmap_list = newpix;
1144 tmppix = pixmap_list;
1145 while (tmppix->next != (Bubble_Step *)NULL)
1146 tmppix = tmppix->next;
1147 tmppix->next = newpix;
1149 newpix->next = (Bubble_Step *)NULL;
1152 /* Finally construct step_pixmaps[] */
1153 make_pixmap_array(pixmap_list);
1156 #endif /* HAVE_XPM */
1165 get_resources(Display *dpy, Window window)
1166 /* Get the appropriate X resources and warn about any inconsistencies. */
1169 XWindowAttributes xgwa;
1171 XGetWindowAttributes (dpy, window, &xgwa);
1172 cmap = xgwa.colormap;
1174 threed = get_boolean_resource("3D", "Boolean");
1175 quiet = get_boolean_resource("quiet", "Boolean");
1176 simple = get_boolean_resource("simple", "Boolean");
1177 /* Forbid rendered bubbles on monochrome displays */
1178 if ((mono_p) && (! simple)) {
1181 "Rendered bubbles not supported on monochrome displays\n");
1184 delay = get_integer_resource("delay", "Integer");
1185 nodelay = get_boolean_resource("nodelay", "Boolean");
1191 default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1193 default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1198 broken = get_boolean_resource("broken", "Boolean");
1201 fprintf(stderr, "-broken not available in simple mode\n");
1206 broken = get_boolean_resource("broken", "Boolean");
1207 #endif /* HAVE_XPM */
1212 init_bubbles (Display *dpy, Window window)
1215 XWindowAttributes xgwa;
1221 get_resources(dpy, window);
1223 XGetWindowAttributes (dpy, window, &xgwa);
1226 printf("sizof(int) on this platform is %d\n", sizeof(int));
1227 printf("sizof(long) on this platform is %d\n", sizeof(long));
1230 screen_width = xgwa.width;
1231 screen_height = xgwa.height;
1232 screen_depth = xgwa.depth;
1233 defcmap = xgwa.colormap;
1234 defvisual = xgwa.visual;
1237 /* These are pretty much plucked out of the air */
1238 bubble_min_radius = (int)(0.006*(double)(MIN(screen_width,
1240 bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1242 /* Some trivial values */
1243 if (bubble_min_radius < 1)
1244 bubble_min_radius = 1;
1245 if (bubble_max_radius <= bubble_min_radius)
1246 bubble_max_radius = bubble_min_radius + 1;
1248 mesh_length = (2 * bubble_max_radius) + 3;
1250 /* store area of each bubble of certain radius as number of 1/10s of
1251 a pixel area. PI is defined in <math.h> */
1252 bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1253 for (i = 0; i < bubble_min_radius; i++)
1254 bubble_areas[i] = 0;
1255 for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1256 bubble_areas[i] = calc_bubble_area(i);
1258 mesh_length = (2 * bubble_max_radius) + 3;
1262 "Bug: simple mode code not set but HAVE_XPM not defined\n");
1265 /* Make sure all #ifdef sort of things have been taken care of in
1267 default_to_pixmaps();
1269 /* Set mesh length */
1270 mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1271 #endif /* HAVE_XPM */
1273 /* Am I missing something in here??? */
1276 mesh_width = (screen_width / mesh_length) + 1;
1277 mesh_height = (screen_height / mesh_length) + 1;
1278 mesh_cells = mesh_width * mesh_height;
1281 calculate_adjacent_list();
1285 /* Graphics contexts for simple mode */
1287 gcv.foreground = default_fg_pixel;
1288 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1289 gcv.foreground = default_bg_pixel;
1290 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1293 XClearWindow (dpy, window);
1297 bubbles (Display *dpy, Window window)
1303 insert_new_bubble(tmp);
1310 screenhack (Display *dpy, Window window)
1312 init_bubbles (dpy, window);
1314 bubbles (dpy, window);
1315 screenhack_handle_events (dpy);