1 /* bubbles.c - frying pan / soft drink in a glass simulation */
3 /*$Id: bubbles.c,v 1.30 2008/07/31 19:27:48 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
46 #undef DEBUG /* doesn't compile */
52 # include <sys/wait.h>
54 # if __DECC_VER >= 50200000
55 # include <sys/wait.h>
63 #include "screenhack.h"
66 #include "xpm-pixmap.h"
68 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
69 # define FANCY_BUBBLES
76 static const char *bubbles_defaults [] = {
90 static XrmOptionDescRec bubbles_options [] = {
91 { "-simple", ".simple", XrmoptionNoArg, "true" },
93 { "-broken", ".broken", XrmoptionNoArg, "true" },
95 { "-quiet", ".quiet", XrmoptionNoArg, "true" },
96 { "-3D", ".3D", XrmoptionNoArg, "true" },
97 { "-delay", ".delay", XrmoptionSepArg, 0 },
98 { "-mode", ".mode", XrmoptionSepArg, 0 },
99 { "-drop", ".mode", XrmoptionNoArg, "drop" },
100 { "-rise", ".mode", XrmoptionNoArg, "rise" },
101 { "-trails", ".trails", XrmoptionNoArg, "true" },
124 unsigned int default_fg_pixel, default_bg_pixel;
126 int bubble_min_radius; /* For simple mode only */
127 int bubble_max_radius;
129 int *bubble_droppages;
130 GC draw_gc, erase_gc;
133 int num_bubble_pixmaps;
134 Bubble_Step **step_pixmaps;
147 static int drop_bubble( struct state *st, Bubble *bb );
150 * To prevent forward references, some stuff is up here
154 calc_bubble_area(struct state *st, 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 > st->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 > st->screen_width) ||
210 (bb->y < 0) || (bb->y > st->screen_height) ||
211 (bb->radius < st->bubble_min_radius) || (bb->radius >
212 st->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 > st->screen_width) ||
221 (bb->y < 0) || (bb->y > st->screen_height) ||
222 (bb->radius < st->step_pixmaps[0]->radius) ||
223 (bb->radius > st->step_pixmaps[st->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);
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;
272 init_mesh (struct state *st)
273 /* Setup the mesh of bubbles */
277 st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
278 for (i = 0; i < st->mesh_cells; i++)
279 st->mesh[i] = (Bubble *)NULL;
283 cell_to_mesh(struct state *st, 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 ((st->mesh_width * y) + x);
296 mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
297 /* convert mesh index into cell coordinates */
299 *cx = mi % st->mesh_width;
300 *cy = mi / st->mesh_width;
304 pixel_to_mesh(struct state *st, int x, int y)
305 /* convert screen coordinates into mesh index */
307 return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
311 verify_mesh_index(struct state *st, int x, int y)
312 /* check to see if (x,y) is in the mesh */
314 if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
316 return (cell_to_mesh(st, 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(struct state *st, 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(&st->mesh[bb->cell_index], bb);
351 print_mesh (struct state *st)
352 /* Print the contents of the mesh */
356 for (i = 0; i < st->mesh_cells; i++) {
357 if (! null_bubble(st->mesh[i])) {
358 printf("Mesh cell %d\n", i);
359 print_bubble_list(st->mesh[i]);
365 valid_mesh (struct state *st)
366 /* Check to see if the mesh is Okay. For debugging only. */
371 for (i = 0; i < st->mesh_cells; i++) {
373 while (! null_bubble(b))
379 total_bubbles (struct state *st)
380 /* Count how many bubbles there are in total. For debugging only. */
386 for (i = 0; i < st->mesh_cells; i++) {
388 while (! null_bubble(b)) {
399 calculate_adjacent_list (struct state *st)
400 /* Calculate the list of cells adjacent to a particular cell for use
406 st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
407 for (i = 0; i < st->mesh_cells; i++) {
408 st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
409 mesh_to_cell(st, i, &ix, &iy);
410 st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
411 st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
412 st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
413 st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
414 st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
415 st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
416 st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
417 st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
418 st->adjacent_list[i][8] = i;
423 adjust_areas (struct state *st)
424 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
433 maxarea = st->bubble_areas[st->bubble_max_radius+1];
435 maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
436 #else /* !FANCY_BUBBLES */
437 maxarea = st->bubble_areas[st->bubble_max_radius+1];
438 #endif /* !FANCY_BUBBLES */
439 maxvalue = (double)st->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 = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
447 st->bubble_areas[i] /= factor;
448 if (st->bubble_areas[i] == 0)
449 st->bubble_areas[i] = 1;
452 for (i = 0; i <= st->num_bubble_pixmaps; i++) {
454 printf("area = %ld", st->step_pixmaps[i]->area);
456 st->step_pixmaps[i]->area /= factor;
457 if (st->step_pixmaps[i]->area == 0)
458 st->step_pixmaps[i]->area = 1;
460 printf("-> %ld\n", st->step_pixmaps[i]->area);
464 #else /* !FANCY_BUBBLES */
465 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
466 st->bubble_areas[i] /= factor;
467 if (st->bubble_areas[i] == 0)
468 st->bubble_areas[i] = 1;
470 #endif /* !FANCY_BUBBLES */
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);
485 new_bubble (struct state *st)
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 = st->bubble_min_radius;
499 rv->area = st->bubble_areas[st->bubble_min_radius];
503 rv->radius = st->step_pixmaps[0]->radius;
504 rv->area = st->step_pixmaps[0]->area;
505 #endif /* FANCY_BUBBLES */
508 rv->magic = BUBBLE_MAGIC;
509 rv->x = random() % st->screen_width;
510 rv->y = random() % st->screen_height;
511 rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
517 show_bubble(struct state *st, 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(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
530 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
534 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc,
535 (bb->x - bb->radius),
536 (bb->y - bb->radius));
538 XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window,
539 st->step_pixmaps[bb->step]->draw_gc,
540 0, 0, (bb->radius * 2),
542 (bb->x - bb->radius),
543 (bb->y - bb->radius));
544 #endif /* FANCY_BUBBLES */
550 hide_bubble(struct state *st, Bubble *bb)
551 /* erase the bubble */
553 if (null_bubble(bb)) {
554 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
562 XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
563 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
568 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc,
569 (bb->x - bb->radius), (bb->y - bb->radius));
571 XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
572 (bb->x - bb->radius),
573 (bb->y - bb->radius),
577 #endif /* FANCY_BUBBLES */
583 delete_bubble_in_mesh(struct state *st, 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 = st->mesh[bb->cell_index];
597 } else if ((null_bubble(bb->prev)) &&
598 (!null_bubble(bb->next))) {
599 bb->next->prev = (Bubble *)NULL;
600 st->mesh[bb->cell_index] = bb->next;
601 bb->next = st->mesh[bb->cell_index];
603 /* Only item on list */
604 st->mesh[bb->cell_index] = (Bubble *)NULL;
612 /* Saves ugly inline code */
614 return ((unsigned long)x * (unsigned long)x);
618 get_closest_bubble(struct state *st, 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 >= st->mesh_cells)) {
641 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
645 /* printf("%d,", bb->cell_index); */
646 if (st->adjacent_list[bb->cell_index][i] != -1) {
647 tmp = st->mesh[st->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;
671 ldr_barf (struct state *st)
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(struct state *st, 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
742 hide_bubble(st, diner);
743 hide_bubble(st, food);
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(st, diner->x, diner->y);
747 diner->area += food->area;
748 delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
751 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
752 diner->area = st->bubble_areas[st->bubble_max_radius];
755 if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
756 diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
758 #endif /* FANCY_BUBBLES */
761 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
762 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
766 if ((! st->simple) && (diner->area >
767 st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
768 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
771 #endif /* FANCY_BUBBLES */
775 if (diner->area > st->bubble_areas[diner->radius + 1]) {
776 /* Move the bubble to a new radius */
778 while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
782 show_bubble(st, diner);
785 if (diner->area > st->step_pixmaps[diner->step+1]->area) {
787 while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
790 diner->radius = st->step_pixmaps[diner->step]->radius;
792 show_bubble(st, diner);
793 #endif /* FANCY_BUBBLES */
796 /* Now adjust locations and cells if need be */
797 if (newmi != diner->cell_index) {
798 delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
799 diner->cell_index = newmi;
800 add_to_mesh(st, diner);
807 merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
808 /* These two bubbles merge into one. If the first one wins out return
809 1 else return 2. If there is no winner (it explodes) then return 0 */
817 if ((null_bubble(b1) || null_bubble(b2))) {
818 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
825 delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
829 if (b1size > b2size) {
830 switch (bubble_eat(st, b1, b2)) {
840 } else if (b1size < b2size) {
841 switch (bubble_eat(st, b2, b1)) {
852 if ((random() % 2) == 0) {
853 switch (bubble_eat(st, b1, b2)) {
864 switch (bubble_eat(st, b2, b1)) {
876 fprintf(stderr, "An error occurred in merge_bubbles()\n");
881 insert_new_bubble(struct state *st, Bubble *tmp)
882 /* Calculates which bubbles are eaten when a new bubble tmp is
883 inserted. This is called recursively in case when a bubble grows
884 it eats others. Careful to pick out disappearing bubbles. */
890 if (null_bubble(tmp)) {
891 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
897 touch = get_closest_bubble(st, nextbub);
898 if (null_bubble(touch))
903 /* Merge all touching bubbles */
904 while (! null_bubble(touch)) {
905 switch (merge_bubbles(st, nextbub, touch)) {
907 /* touch ate nextbub and survived */
911 /* nextbub ate touch and survived */
914 /* somebody ate someone else but they exploded */
915 nextbub = (Bubble *)NULL;
918 /* something went wrong */
919 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
923 /* Check to see if any bubble survived. */
924 if (null_bubble(nextbub))
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 touch = get_closest_bubble(st, nextbub);
932 if (null_bubble(nextbub))
935 /* Shift bubble down. Break if we run off the screen. */
937 if (drop_bubble( st, nextbub ) == -1)
941 /* Check to see if there are any other bubbles still in the area
942 and if we need to do this all over again for them. */
943 touch = get_closest_bubble(st, nextbub);
944 if (null_bubble(touch)) {
945 /* We also continue every so often if we're dropping and the bubble is at max size */
948 if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
953 if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
956 #endif /* FANCY_BUBBLES */
966 leave_trail(struct state *st, Bubble *bb )
970 tmp = new_bubble(st);
972 tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
973 tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
974 add_to_mesh(st, tmp);
975 insert_new_bubble(st, tmp);
976 show_bubble( st, tmp );
981 drop_bubble( struct state *st, Bubble *bb )
985 hide_bubble( st, bb );
988 (bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
991 (bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
992 #endif /* FANCY_BUBBLES */
993 if ((bb->y < 0) || (bb->y > st->screen_height)) {
994 delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
998 show_bubble( st, bb );
1000 /* Now adjust locations and cells if need be */
1001 newmi = pixel_to_mesh(st, bb->x, bb->y);
1002 if (newmi != bb->cell_index) {
1003 delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
1004 bb->cell_index = newmi;
1005 add_to_mesh(st, bb);
1010 if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
1011 leave_trail( st, bb );
1013 #ifdef FANCY_BUBBLES
1015 if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
1016 leave_trail( st, bb );
1018 #endif /* FANCY_BUBBLES */
1027 get_length_of_bubble_list(Bubble *bb)
1032 while (! null_bubble(tmp)) {
1042 * Pixmap stuff used regardless of whether file I/O is available. Must
1043 * still check for XPM, though!
1046 #ifdef FANCY_BUBBLES
1049 * Pixmaps without file I/O (but do have XPM)
1053 pixmap_sort(Bubble_Step **head, int numelems)
1054 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1055 the numelems length array with first element at head into order of radius.
1059 Bubble_Step *least = 0;
1060 int minradius = INT_MAX;
1063 for (i = 0; i < numelems; i++) {
1064 if (head[i]->radius < minradius) {
1066 minradius = head[i]->radius;
1069 if (*head != least) {
1070 memcpy(&tmp, least, sizeof(Bubble_Step));
1071 memcpy(least, *head, sizeof(Bubble_Step));
1072 memcpy(*head, &tmp, sizeof(Bubble_Step));
1076 pixmap_sort(&head[1], numelems-1);
1080 extrapolate(int i1, int i2)
1082 return (i2 + (i2 - i1));
1086 make_pixmap_array(struct state *st, Bubble_Step *list)
1087 /* From a linked list of bubbles construct the array step_pixmaps */
1089 Bubble_Step *tmp = list;
1095 if (list == (Bubble_Step *)NULL) {
1096 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1100 st->num_bubble_pixmaps = 1;
1101 while(tmp->next != (Bubble_Step *)NULL) {
1103 ++st->num_bubble_pixmaps;
1106 if (st->num_bubble_pixmaps < 2) {
1107 fprintf(stderr, "Must be at least two bubbles in file\n");
1111 st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) *
1112 sizeof(Bubble_Step *));
1114 /* Copy them blindly into the array for sorting. */
1118 st->step_pixmaps[ind++] = tmp;
1120 } while(tmp != (Bubble_Step *)NULL);
1122 /* We make another bubble beyond the ones with pixmaps so that the final
1123 bubble hangs around and doesn't pop immediately. It's radius and area
1124 are found by extrapolating from the largest two bubbles with pixmaps. */
1126 st->step_pixmaps[st->num_bubble_pixmaps] =
1127 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1128 st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
1130 pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
1133 if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
1134 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1138 st->step_pixmaps[st->num_bubble_pixmaps]->radius =
1139 extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
1140 st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
1141 st->step_pixmaps[st->num_bubble_pixmaps]->area =
1142 calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
1146 /* Now check for correct order */
1147 for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
1149 if (st->step_pixmaps[ind]->radius < prevrad) {
1150 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1154 prevrad = st->step_pixmaps[ind]->radius;
1158 /* Now populate the droppage values */
1159 for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
1160 st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
1164 make_pixmap_from_default(struct state *st, char **pixmap_data, Bubble_Step *bl)
1165 /* Read pixmap data which has been compiled into the program and a pointer
1166 to which has been passed.
1168 This is virtually copied verbatim from make_pixmap_from_file() above and
1169 changes made to either should be propagated onwards! */
1174 if (pixmap_data == (char **)0) {
1175 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1180 if (bl == (Bubble_Step *)NULL) {
1181 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1185 #ifdef FANCY_BUBBLES
1188 bl->ball = xpm_data_to_pixmap (st->dpy, st->window, (char **) pixmap_data,
1189 &w, &h, &bl->shape_mask);
1190 bl->radius = MAX(w, h) / 2;
1191 bl->area = calc_bubble_area(st, bl->radius);
1193 #endif /* FANCY_BUBBLES */
1195 gcv.foreground = st->default_fg_pixel;
1196 gcv.function = GXcopy;
1197 bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1198 XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
1200 gcv.foreground = st->default_bg_pixel;
1201 gcv.function = GXcopy;
1202 bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1203 XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
1207 default_to_pixmaps (struct state *st)
1208 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1211 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1212 Bubble_Step *newpix, *tmppix;
1215 init_default_bubbles();
1217 for (i = 0; i < num_default_bubbles; i++) {
1218 pixpt = default_bubbles[i];
1219 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1220 make_pixmap_from_default(st, pixpt, newpix);
1221 /* Now add to list */
1222 if (pixmap_list == (Bubble_Step *)NULL) {
1223 pixmap_list = newpix;
1225 tmppix = pixmap_list;
1226 while (tmppix->next != (Bubble_Step *)NULL)
1227 tmppix = tmppix->next;
1228 tmppix->next = newpix;
1230 newpix->next = (Bubble_Step *)NULL;
1233 /* Finally construct step_pixmaps[] */
1234 make_pixmap_array(st, pixmap_list);
1237 #endif /* FANCY_BUBBLES */
1246 get_resources(struct state *st)
1247 /* Get the appropriate X resources and warn about any inconsistencies. */
1250 XWindowAttributes xgwa;
1253 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1254 cmap = xgwa.colormap;
1256 st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1257 st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1258 st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1259 /* Forbid rendered bubbles on monochrome displays */
1260 if ((mono_p) && (! st->simple)) {
1263 "Rendered bubbles not supported on monochrome displays\n");
1266 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1268 s = get_string_resource (st->dpy, "mode", "Mode");
1270 if (!s || !*s || !strcasecmp (s, "float"))
1272 else if (!strcasecmp (s, "rise"))
1274 else if (!strcasecmp (s, "drop"))
1277 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1279 st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1280 st->drop_dir = (st->drop ? 1 : -1);
1281 if (st->drop || rise)
1284 st->default_fg_pixel = get_pixel_resource (st->dpy,
1285 cmap, "foreground", "Foreground");
1286 st->default_bg_pixel = get_pixel_resource (st->dpy,
1287 cmap, "background", "Background");
1291 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1294 fprintf(stderr, "-broken not available in simple mode\n");
1296 #ifndef FANCY_BUBBLES
1298 #else /* FANCY_BUBBLES */
1299 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1300 #endif /* FANCY_BUBBLES */
1305 bubbles_init (Display *dpy, Window window)
1307 struct state *st = (struct state *) calloc (1, sizeof(*st));
1309 XWindowAttributes xgwa;
1313 st->window = window;
1317 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1320 printf("sizof(int) on this platform is %d\n", sizeof(int));
1321 printf("sizof(long) on this platform is %d\n", sizeof(long));
1324 st->screen_width = xgwa.width;
1325 st->screen_height = xgwa.height;
1326 st->screen_depth = xgwa.depth;
1329 /* These are pretty much plucked out of the air */
1330 st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width,
1331 st->screen_height)));
1332 st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1333 st->screen_height)));
1334 /* Some trivial values */
1335 if (st->bubble_min_radius < 1)
1336 st->bubble_min_radius = 1;
1337 if (st->bubble_max_radius <= st->bubble_min_radius)
1338 st->bubble_max_radius = st->bubble_min_radius + 1;
1340 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1342 /* store area of each bubble of certain radius as number of 1/10s of
1343 a pixel area. PI is defined in <math.h> */
1344 st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1345 for (i = 0; i < st->bubble_min_radius; i++)
1346 st->bubble_areas[i] = 0;
1347 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1348 st->bubble_areas[i] = calc_bubble_area(st, i);
1350 /* Now populate the droppage values */
1351 st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1352 for (i = 0; i < st->bubble_min_radius; i++)
1353 st->bubble_droppages[i] = 0;
1354 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1355 st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1357 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1359 #ifndef FANCY_BUBBLES
1361 "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1363 #else /* FANCY_BUBBLES */
1364 /* Make sure all #ifdef sort of things have been taken care of in
1366 default_to_pixmaps(st);
1368 /* Set mesh length */
1369 st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1370 #endif /* FANCY_BUBBLES */
1372 /* Am I missing something in here??? */
1375 st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1376 st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1377 st->mesh_cells = st->mesh_width * st->mesh_height;
1380 calculate_adjacent_list(st);
1384 /* Graphics contexts for simple mode */
1386 gcv.foreground = st->default_fg_pixel;
1387 st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1388 gcv.foreground = st->default_bg_pixel;
1389 st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1392 XClearWindow (st->dpy, st->window);
1394 # ifndef FANCY_BUBBLES
1401 static unsigned long
1402 bubbles_draw (Display *dpy, Window window, void *closure)
1404 struct state *st = (struct state *) closure;
1406 for (i = 0; i < 5; i++)
1408 Bubble *tmp = new_bubble(st);
1409 add_to_mesh(st, tmp);
1410 insert_new_bubble(st, tmp);
1417 bubbles_reshape (Display *dpy, Window window, void *closure,
1418 unsigned int w, unsigned int h)
1423 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1429 bubbles_free (Display *dpy, Window window, void *closure)
1431 struct state *st = (struct state *) closure;
1435 XSCREENSAVER_MODULE ("Bubbles", bubbles)