1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.28 2006/03/13 11:41:31 jwz Exp $*/
6 * Copyright (C) 1995-1996 James Macnicol
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation. No representations are made about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
18 * I got my original inspiration for this by looking at the bottom of a
19 * frying pan while something was cooking and watching the little bubbles
20 * coming off the bottom of the pan as the oil was boiling joining together
21 * to form bigger bubbles and finally to *pop* and disappear. I had some
22 * time on my hands so I wrote this little xscreensaver module to imitate
23 * it. Now that it's done it reminds me more of the bubbles you get in
24 * a glass of fizzy soft drink.....
26 * The problem seemed to be that the position/size etc. of all the bubbles
27 * on the screen had to be remembered and searched through to find when
28 * bubbles hit each other and combined. To do this more efficiently, the
29 * window/screen is divided up into a square mesh of side length mesh_length
30 * and separate lists of bubbles contained in each cell of the mesh are
31 * kept. Only the cells in the immediate vicinity of the bubble in question
32 * are searched. This should make things more efficient although the whole
33 * thing seems to use up too much CPU, but then I'm using an ancient PC so
34 * perhaps it's not surprising .
35 * (Six months after I wrote the above I now have a Pentium with PCI graphics
36 * and things are _much_ nicer.)
38 * Author: James Macnicol
39 * Internet E-mail : j-macnicol@adfa.edu.au
50 # include <sys/wait.h>
52 # if __DECC_VER >= 50200000
53 # include <sys/wait.h>
61 #include "screenhack.h"
64 #include "xpm-pixmap.h"
66 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
67 # define FANCY_BUBBLES
74 static const char *bubbles_defaults [] = {
88 static XrmOptionDescRec bubbles_options [] = {
89 { "-simple", ".simple", XrmoptionNoArg, "true" },
91 { "-broken", ".broken", XrmoptionNoArg, "true" },
93 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
94 { "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
95 { "-3D", ".3D", XrmoptionNoArg, "true" },
96 { "-delay", ".delay", XrmoptionSepArg, 0 },
97 { "-mode", ".mode", XrmoptionSepArg, 0 },
98 { "-drop", ".mode", XrmoptionNoArg, "drop" },
99 { "-rise", ".mode", XrmoptionNoArg, "rise" },
100 { "-trails", ".trails", XrmoptionNoArg, "true" },
123 unsigned int default_fg_pixel, default_bg_pixel;
125 int bubble_min_radius; /* For simple mode only */
126 int bubble_max_radius;
128 int *bubble_droppages;
129 GC draw_gc, erase_gc;
132 int num_bubble_pixmaps;
133 Bubble_Step **step_pixmaps;
146 static int drop_bubble( struct state *st, Bubble *bb );
149 * To prevent forward references, some stuff is up here
153 calc_bubble_area(struct state *st, int r)
154 /* Calculate the area of a bubble of radius r */
158 10.0 * PI * (double)r * (double)r * (double)r);
161 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
163 return (long)(10.0 * PI * (double)r * (double)r);
172 if ((ret = malloc(size)) == NULL) {
173 fprintf(stderr, "%s: out of memory\n", progname);
181 die_bad_bubble(Bubble *bb)
182 /* This is for use with GDB */
184 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
190 null_bubble(Bubble *bb)
191 /* Returns true if the pointer passed is NULL. If not then this checks to
192 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
193 number is set correctly. This only a sanity check for debugging and is
194 turned off if DEBUG isn't set. */
196 if (bb == (Bubble *)NULL)
199 if ((bb->cell_index < 0) || (bb->cell_index > st->mesh_cells)) {
200 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
203 if (bb->magic != BUBBLE_MAGIC) {
204 fprintf(stderr, "Magic = %d\n", bb->magic);
208 if ((bb->x < 0) || (bb->x > st->screen_width) ||
209 (bb->y < 0) || (bb->y > st->screen_height) ||
210 (bb->radius < st->bubble_min_radius) || (bb->radius >
211 st->bubble_max_radius)) {
213 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
214 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
219 if ((bb->x < 0) || (bb->x > st->screen_width) ||
220 (bb->y < 0) || (bb->y > st->screen_height) ||
221 (bb->radius < st->step_pixmaps[0]->radius) ||
222 (bb->radius > st->step_pixmaps[st->num_bubble_pixmaps-1]->radius)) {
224 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
225 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
236 print_bubble_list(Bubble *bb)
237 /* Print list of where all the bubbles are. For debugging purposes only. */
239 if (! null_bubble(bb)) {
240 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
241 print_bubble_list(bb->next);
247 add_bubble_to_list(Bubble **list, Bubble *bb)
248 /* Take a pointer to a list of bubbles and stick bb at the head of the
251 Bubble *head = *list;
253 if (null_bubble(head)) {
254 bb->prev = (Bubble *)NULL;
255 bb->next = (Bubble *)NULL;
258 bb->prev = (Bubble *)NULL;
271 init_mesh (struct state *st)
272 /* Setup the mesh of bubbles */
276 st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
277 for (i = 0; i < st->mesh_cells; i++)
278 st->mesh[i] = (Bubble *)NULL;
282 cell_to_mesh(struct state *st, int x, int y)
283 /* convert cell coordinates to mesh index */
286 if ((x < 0) || (y < 0)) {
287 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
291 return ((st->mesh_width * y) + x);
295 mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
296 /* convert mesh index into cell coordinates */
298 *cx = mi % st->mesh_width;
299 *cy = mi / st->mesh_width;
303 pixel_to_mesh(struct state *st, int x, int y)
304 /* convert screen coordinates into mesh index */
306 return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
310 verify_mesh_index(struct state *st, int x, int y)
311 /* check to see if (x,y) is in the mesh */
313 if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
315 return (cell_to_mesh(st, x, y));
320 print_adjacents(int *adj)
321 /* Print a list of the cells calculated above. For debugging only. */
326 for (i = 0; i < 8; i++)
327 printf("%d ", adj[i]);
333 add_to_mesh(struct state *st, Bubble *bb)
334 /* Add the given bubble to the mesh by sticking it on the front of the
335 list. bb is already allocated so no need to malloc() anything, just
339 if (null_bubble(bb)) {
340 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
345 add_bubble_to_list(&st->mesh[bb->cell_index], bb);
350 print_mesh (struct state *st)
351 /* Print the contents of the mesh */
355 for (i = 0; i < st->mesh_cells; i++) {
356 if (! null_bubble(st->mesh[i])) {
357 printf("Mesh cell %d\n", i);
358 print_bubble_list(st->mesh[i]);
364 valid_mesh (struct state *st)
365 /* Check to see if the mesh is Okay. For debugging only. */
370 for (i = 0; i < st->mesh_cells; i++) {
372 while (! null_bubble(b))
378 total_bubbles (struct state *st)
379 /* Count how many bubbles there are in total. For debugging only. */
385 for (i = 0; i < st->mesh_cells; i++) {
387 while (! null_bubble(b)) {
398 calculate_adjacent_list (struct state *st)
399 /* Calculate the list of cells adjacent to a particular cell for use
405 st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
406 for (i = 0; i < st->mesh_cells; i++) {
407 st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
408 mesh_to_cell(st, i, &ix, &iy);
409 st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
410 st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
411 st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
412 st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
413 st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
414 st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
415 st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
416 st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
417 st->adjacent_list[i][8] = i;
422 adjust_areas (struct state *st)
423 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
432 maxarea = st->bubble_areas[st->bubble_max_radius+1];
434 maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
435 #else /* !FANCY_BUBBLES */
436 maxarea = st->bubble_areas[st->bubble_max_radius+1];
437 #endif /* !FANCY_BUBBLES */
438 maxvalue = (double)st->screen_width * 2.0 * (double)maxarea;
439 factor = (long)ceil(maxvalue / (double)LONG_MAX);
441 /* Overflow will occur in weighted_mean(). We must divide areas
442 each by factor so it will never do so. */
445 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
446 st->bubble_areas[i] /= factor;
447 if (st->bubble_areas[i] == 0)
448 st->bubble_areas[i] = 1;
451 for (i = 0; i <= st->num_bubble_pixmaps; i++) {
453 printf("area = %ld", st->step_pixmaps[i]->area);
455 st->step_pixmaps[i]->area /= factor;
456 if (st->step_pixmaps[i]->area == 0)
457 st->step_pixmaps[i]->area = 1;
459 printf("-> %ld\n", st->step_pixmaps[i]->area);
463 #else /* !FANCY_BUBBLES */
464 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
465 st->bubble_areas[i] /= factor;
466 if (st->bubble_areas[i] == 0)
467 st->bubble_areas[i] = 1;
469 #endif /* !FANCY_BUBBLES */
472 printf("maxarea = %ld\n", maxarea);
473 printf("maxvalue = %g\n", maxvalue);
474 printf("LONG_MAX = %ld\n", LONG_MAX);
475 printf("factor = %ld\n", factor);
484 new_bubble (struct state *st)
485 /* Add a new bubble at some random position on the screen of the smallest
488 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
490 /* Can't use null_bubble() here since magic number hasn't been set */
491 if (rv == (Bubble *)NULL) {
492 fprintf(stderr, "Ran out of memory!\n");
497 rv->radius = st->bubble_min_radius;
498 rv->area = st->bubble_areas[st->bubble_min_radius];
502 rv->radius = st->step_pixmaps[0]->radius;
503 rv->area = st->step_pixmaps[0]->area;
504 #endif /* FANCY_BUBBLES */
507 rv->magic = BUBBLE_MAGIC;
508 rv->x = random() % st->screen_width;
509 rv->y = random() % st->screen_height;
510 rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
516 show_bubble(struct state *st, Bubble *bb)
517 /* paint the bubble on the screen */
519 if (null_bubble(bb)) {
520 fprintf(stderr, "NULL bubble passed to show_bubble\n");
528 XDrawArc(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
529 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
533 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc,
534 (bb->x - bb->radius),
535 (bb->y - bb->radius));
537 XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window,
538 st->step_pixmaps[bb->step]->draw_gc,
539 0, 0, (bb->radius * 2),
541 (bb->x - bb->radius),
542 (bb->y - bb->radius));
543 #endif /* FANCY_BUBBLES */
549 hide_bubble(struct state *st, Bubble *bb)
550 /* erase the bubble */
552 if (null_bubble(bb)) {
553 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
561 XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
562 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
567 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc,
568 (bb->x - bb->radius), (bb->y - bb->radius));
570 XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
571 (bb->x - bb->radius),
572 (bb->y - bb->radius),
576 #endif /* FANCY_BUBBLES */
582 delete_bubble_in_mesh(struct state *st, Bubble *bb, int keep_bubble)
583 /* Delete an individual bubble, adjusting list of bubbles around it.
584 If keep_bubble is true then the bubble isn't actually deleted. We
585 use this to allow bubbles to change mesh cells without reallocating,
586 (it needs this when two bubbles collide and the centre position is
587 recalculated, and this may stray over a mesh boundary). */
589 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
590 bb->prev->next = bb->next;
591 bb->next->prev = bb->prev;
592 } else if ((!null_bubble(bb->prev)) &&
593 (null_bubble(bb->next))) {
594 bb->prev->next = (Bubble *)NULL;
595 bb->next = st->mesh[bb->cell_index];
596 } else if ((null_bubble(bb->prev)) &&
597 (!null_bubble(bb->next))) {
598 bb->next->prev = (Bubble *)NULL;
599 st->mesh[bb->cell_index] = bb->next;
600 bb->next = st->mesh[bb->cell_index];
602 /* Only item on list */
603 st->mesh[bb->cell_index] = (Bubble *)NULL;
611 /* Saves ugly inline code */
613 return ((unsigned long)x * (unsigned long)x);
617 get_closest_bubble(struct state *st, Bubble *bb)
618 /* Find the closest bubble touching the this bubble, NULL if none are
621 Bubble *rv = (Bubble *)NULL;
623 unsigned long separation2, touchdist2;
625 unsigned long closest2 = ULONG_MAX;
629 if (null_bubble(bb)) {
630 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
636 for (i = 0; i < 9; i++) {
637 /* There is a bug here where bb->cell_index is negaitve.. */
639 if ((bb->cell_index < 0) || (bb->cell_index >= st->mesh_cells)) {
640 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
644 /* printf("%d,", bb->cell_index); */
645 if (st->adjacent_list[bb->cell_index][i] != -1) {
646 tmp = st->mesh[st->adjacent_list[bb->cell_index][i]];
647 while (! null_bubble(tmp)) {
651 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
652 /* Add extra leeway so circles _never_ overlap */
653 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
654 if ((separation2 <= touchdist2) && (separation2 <
657 closest2 = separation2;
670 ldr_barf (struct state *st)
676 long_div_round(long num, long dem)
681 if ((num < 0) || (dem < 0)) {
682 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
690 if (moddo > (dem / 2))
694 if ((divvie < 0) || (moddo < 0)) {
695 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
705 weighted_mean(int n1, int n2, long w1, long w2)
706 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
709 if ((w1 <= 0) || (w2 <= 0)) {
711 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
716 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
721 bubble_eat(struct state *st, Bubble *diner, Bubble *food)
722 /* The diner eats the food. Returns true (1) if the diner still exists */
728 if ((null_bubble(diner)) || (null_bubble(food))) {
729 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
734 /* We hide the diner even in the case that it doesn't grow so that
735 if the food overlaps its boundary it is replaced. This could
736 probably be solved by letting bubbles eat others which are close
737 but not quite touching. It's probably worth it, too, since we
738 would then not have to redraw bubbles which don't change in
741 hide_bubble(st, diner);
742 hide_bubble(st, food);
743 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
744 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
745 newmi = pixel_to_mesh(st, diner->x, diner->y);
746 diner->area += food->area;
747 delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
750 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
751 diner->area = st->bubble_areas[st->bubble_max_radius];
754 if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
755 diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
757 #endif /* FANCY_BUBBLES */
760 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
761 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
765 if ((! st->simple) && (diner->area >
766 st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
767 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
770 #endif /* FANCY_BUBBLES */
774 if (diner->area > st->bubble_areas[diner->radius + 1]) {
775 /* Move the bubble to a new radius */
777 while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
781 show_bubble(st, diner);
784 if (diner->area > st->step_pixmaps[diner->step+1]->area) {
786 while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
789 diner->radius = st->step_pixmaps[diner->step]->radius;
791 show_bubble(st, diner);
792 #endif /* FANCY_BUBBLES */
795 /* Now adjust locations and cells if need be */
796 if (newmi != diner->cell_index) {
797 delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
798 diner->cell_index = newmi;
799 add_to_mesh(st, diner);
806 merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
807 /* These two bubbles merge into one. If the first one wins out return
808 1 else return 2. If there is no winner (it explodes) then return 0 */
816 if ((null_bubble(b1) || null_bubble(b2))) {
817 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
824 delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
828 if (b1size > b2size) {
829 switch (bubble_eat(st, b1, b2)) {
839 } else if (b1size < b2size) {
840 switch (bubble_eat(st, b2, b1)) {
851 if ((random() % 2) == 0) {
852 switch (bubble_eat(st, b1, b2)) {
863 switch (bubble_eat(st, b2, b1)) {
875 fprintf(stderr, "An error occurred in merge_bubbles()\n");
880 insert_new_bubble(struct state *st, Bubble *tmp)
881 /* Calculates which bubbles are eaten when a new bubble tmp is
882 inserted. This is called recursively in case when a bubble grows
883 it eats others. Careful to pick out disappearing bubbles. */
889 if (null_bubble(tmp)) {
890 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
896 touch = get_closest_bubble(st, nextbub);
897 if (null_bubble(touch))
902 /* Merge all touching bubbles */
903 while (! null_bubble(touch)) {
904 switch (merge_bubbles(st, nextbub, touch)) {
906 /* touch ate nextbub and survived */
910 /* nextbub ate touch and survived */
913 /* somebody ate someone else but they exploded */
914 nextbub = (Bubble *)NULL;
917 /* something went wrong */
918 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
922 /* Check to see if any bubble survived. */
923 if (null_bubble(nextbub))
926 /* Check to see if there are any other bubbles still in the area
927 and if we need to do this all over again for them. */
928 touch = get_closest_bubble(st, nextbub);
931 if (null_bubble(nextbub))
934 /* Shift bubble down. Break if we run off the screen. */
936 if (drop_bubble( st, nextbub ) == -1)
940 /* Check to see if there are any other bubbles still in the area
941 and if we need to do this all over again for them. */
942 touch = get_closest_bubble(st, nextbub);
943 if (null_bubble(touch)) {
944 /* We also continue every so often if we're dropping and the bubble is at max size */
947 if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
952 if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
955 #endif /* FANCY_BUBBLES */
965 leave_trail(struct state *st, Bubble *bb )
969 tmp = new_bubble(st);
971 tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
972 tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
973 add_to_mesh(st, tmp);
974 insert_new_bubble(st, tmp);
975 show_bubble( st, tmp );
980 drop_bubble( struct state *st, Bubble *bb )
984 hide_bubble( st, bb );
987 (bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
990 (bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
991 #endif /* FANCY_BUBBLES */
992 if ((bb->y < 0) || (bb->y > st->screen_height)) {
993 delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
997 show_bubble( st, bb );
999 /* Now adjust locations and cells if need be */
1000 newmi = pixel_to_mesh(st, bb->x, bb->y);
1001 if (newmi != bb->cell_index) {
1002 delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
1003 bb->cell_index = newmi;
1004 add_to_mesh(st, bb);
1009 if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
1010 leave_trail( st, bb );
1012 #ifdef FANCY_BUBBLES
1014 if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
1015 leave_trail( st, bb );
1017 #endif /* FANCY_BUBBLES */
1026 get_length_of_bubble_list(Bubble *bb)
1031 while (! null_bubble(tmp)) {
1041 * Pixmap stuff used regardless of whether file I/O is available. Must
1042 * still check for XPM, though!
1045 #ifdef FANCY_BUBBLES
1048 * Pixmaps without file I/O (but do have XPM)
1052 pixmap_sort(Bubble_Step **head, int numelems)
1053 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1054 the numelems length array with first element at head into order of radius.
1058 Bubble_Step *least = 0;
1059 int minradius = INT_MAX;
1062 for (i = 0; i < numelems; i++) {
1063 if (head[i]->radius < minradius) {
1065 minradius = head[i]->radius;
1068 if (*head != least) {
1069 memcpy(&tmp, least, sizeof(Bubble_Step));
1070 memcpy(least, *head, sizeof(Bubble_Step));
1071 memcpy(*head, &tmp, sizeof(Bubble_Step));
1075 pixmap_sort(&head[1], numelems-1);
1079 extrapolate(int i1, int i2)
1081 return (i2 + (i2 - i1));
1085 make_pixmap_array(struct state *st, Bubble_Step *list)
1086 /* From a linked list of bubbles construct the array step_pixmaps */
1088 Bubble_Step *tmp = list;
1094 if (list == (Bubble_Step *)NULL) {
1095 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1099 st->num_bubble_pixmaps = 1;
1100 while(tmp->next != (Bubble_Step *)NULL) {
1102 ++st->num_bubble_pixmaps;
1105 if (st->num_bubble_pixmaps < 2) {
1106 fprintf(stderr, "Must be at least two bubbles in file\n");
1110 st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) *
1111 sizeof(Bubble_Step *));
1113 /* Copy them blindly into the array for sorting. */
1117 st->step_pixmaps[ind++] = tmp;
1119 } while(tmp != (Bubble_Step *)NULL);
1121 /* We make another bubble beyond the ones with pixmaps so that the final
1122 bubble hangs around and doesn't pop immediately. It's radius and area
1123 are found by extrapolating from the largest two bubbles with pixmaps. */
1125 st->step_pixmaps[st->num_bubble_pixmaps] =
1126 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1127 st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
1129 pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
1132 if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
1133 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1137 st->step_pixmaps[st->num_bubble_pixmaps]->radius =
1138 extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
1139 st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
1140 st->step_pixmaps[st->num_bubble_pixmaps]->area =
1141 calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
1145 /* Now check for correct order */
1146 for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
1148 if (st->step_pixmaps[ind]->radius < prevrad) {
1149 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1153 prevrad = st->step_pixmaps[ind]->radius;
1157 /* Now populate the droppage values */
1158 for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
1159 st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
1163 make_pixmap_from_default(struct state *st, char **pixmap_data, Bubble_Step *bl)
1164 /* Read pixmap data which has been compiled into the program and a pointer
1165 to which has been passed.
1167 This is virtually copied verbatim from make_pixmap_from_file() above and
1168 changes made to either should be propagated onwards! */
1173 if (pixmap_data == (char **)0) {
1174 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1179 if (bl == (Bubble_Step *)NULL) {
1180 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1184 #ifdef FANCY_BUBBLES
1187 bl->ball = xpm_data_to_pixmap (st->dpy, st->window, (char **) pixmap_data,
1188 &w, &h, &bl->shape_mask);
1189 bl->radius = MAX(w, h) / 2;
1190 bl->area = calc_bubble_area(st, bl->radius);
1192 #endif /* FANCY_BUBBLES */
1194 gcv.foreground = st->default_fg_pixel;
1195 gcv.function = GXcopy;
1196 bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1197 XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
1199 gcv.foreground = st->default_bg_pixel;
1200 gcv.function = GXcopy;
1201 bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1202 XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
1206 default_to_pixmaps (struct state *st)
1207 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1210 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1211 Bubble_Step *newpix, *tmppix;
1214 init_default_bubbles();
1216 for (i = 0; i < num_default_bubbles; i++) {
1217 pixpt = default_bubbles[i];
1218 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1219 make_pixmap_from_default(st, pixpt, newpix);
1220 /* Now add to list */
1221 if (pixmap_list == (Bubble_Step *)NULL) {
1222 pixmap_list = newpix;
1224 tmppix = pixmap_list;
1225 while (tmppix->next != (Bubble_Step *)NULL)
1226 tmppix = tmppix->next;
1227 tmppix->next = newpix;
1229 newpix->next = (Bubble_Step *)NULL;
1232 /* Finally construct step_pixmaps[] */
1233 make_pixmap_array(st, pixmap_list);
1236 #endif /* FANCY_BUBBLES */
1245 get_resources(struct state *st)
1246 /* Get the appropriate X resources and warn about any inconsistencies. */
1249 XWindowAttributes xgwa;
1252 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1253 cmap = xgwa.colormap;
1255 st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1256 st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1257 st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1258 /* Forbid rendered bubbles on monochrome displays */
1259 if ((mono_p) && (! st->simple)) {
1262 "Rendered bubbles not supported on monochrome displays\n");
1265 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1266 nodelay = get_boolean_resource(st->dpy, "nodelay", "Boolean");
1272 s = get_string_resource (st->dpy, "mode", "Mode");
1274 if (!s || !*s || !strcasecmp (s, "float"))
1276 else if (!strcasecmp (s, "rise"))
1278 else if (!strcasecmp (s, "drop"))
1281 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1283 st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1284 st->drop_dir = (st->drop ? 1 : -1);
1285 if (st->drop || rise)
1288 st->default_fg_pixel = get_pixel_resource (st->dpy,
1289 cmap, "foreground", "Foreground");
1290 st->default_bg_pixel = get_pixel_resource (st->dpy,
1291 cmap, "background", "Background");
1295 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1298 fprintf(stderr, "-broken not available in simple mode\n");
1300 #ifndef FANCY_BUBBLES
1302 #else /* FANCY_BUBBLES */
1303 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1304 #endif /* FANCY_BUBBLES */
1309 bubbles_init (Display *dpy, Window window)
1311 struct state *st = (struct state *) calloc (1, sizeof(*st));
1313 XWindowAttributes xgwa;
1317 st->window = window;
1321 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1324 printf("sizof(int) on this platform is %d\n", sizeof(int));
1325 printf("sizof(long) on this platform is %d\n", sizeof(long));
1328 st->screen_width = xgwa.width;
1329 st->screen_height = xgwa.height;
1330 st->screen_depth = xgwa.depth;
1333 /* These are pretty much plucked out of the air */
1334 st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width,
1335 st->screen_height)));
1336 st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1337 st->screen_height)));
1338 /* Some trivial values */
1339 if (st->bubble_min_radius < 1)
1340 st->bubble_min_radius = 1;
1341 if (st->bubble_max_radius <= st->bubble_min_radius)
1342 st->bubble_max_radius = st->bubble_min_radius + 1;
1344 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1346 /* store area of each bubble of certain radius as number of 1/10s of
1347 a pixel area. PI is defined in <math.h> */
1348 st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1349 for (i = 0; i < st->bubble_min_radius; i++)
1350 st->bubble_areas[i] = 0;
1351 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1352 st->bubble_areas[i] = calc_bubble_area(st, i);
1354 /* Now populate the droppage values */
1355 st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1356 for (i = 0; i < st->bubble_min_radius; i++)
1357 st->bubble_droppages[i] = 0;
1358 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1359 st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1361 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1363 #ifndef FANCY_BUBBLES
1365 "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1367 #else /* FANCY_BUBBLES */
1368 /* Make sure all #ifdef sort of things have been taken care of in
1370 default_to_pixmaps(st);
1372 /* Set mesh length */
1373 st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1374 #endif /* FANCY_BUBBLES */
1376 /* Am I missing something in here??? */
1379 st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1380 st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1381 st->mesh_cells = st->mesh_width * st->mesh_height;
1384 calculate_adjacent_list(st);
1388 /* Graphics contexts for simple mode */
1390 gcv.foreground = st->default_fg_pixel;
1391 st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1392 gcv.foreground = st->default_bg_pixel;
1393 st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1396 XClearWindow (st->dpy, st->window);
1398 # ifndef FANCY_BUBBLES
1405 static unsigned long
1406 bubbles_draw (Display *dpy, Window window, void *closure)
1408 struct state *st = (struct state *) closure;
1411 tmp = new_bubble(st);
1412 add_to_mesh(st, tmp);
1413 insert_new_bubble(st, tmp);
1419 bubbles_reshape (Display *dpy, Window window, void *closure,
1420 unsigned int w, unsigned int h)
1425 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1431 bubbles_free (Display *dpy, Window window, void *closure)
1433 struct state *st = (struct state *) closure;
1437 XSCREENSAVER_MODULE ("Bubbles", bubbles)