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
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 { "-3D", ".3D", XrmoptionNoArg, "true" },
95 { "-delay", ".delay", XrmoptionSepArg, 0 },
96 { "-mode", ".mode", XrmoptionSepArg, 0 },
97 { "-drop", ".mode", XrmoptionNoArg, "drop" },
98 { "-rise", ".mode", XrmoptionNoArg, "rise" },
99 { "-trails", ".trails", XrmoptionNoArg, "true" },
122 unsigned int default_fg_pixel, default_bg_pixel;
124 int bubble_min_radius; /* For simple mode only */
125 int bubble_max_radius;
127 int *bubble_droppages;
128 GC draw_gc, erase_gc;
131 int num_bubble_pixmaps;
132 Bubble_Step **step_pixmaps;
145 static int drop_bubble( struct state *st, Bubble *bb );
148 * To prevent forward references, some stuff is up here
152 calc_bubble_area(struct state *st, int r)
153 /* Calculate the area of a bubble of radius r */
157 10.0 * PI * (double)r * (double)r * (double)r);
160 return (long)(10.0 * PI * (double)r * (double)r * (double)r);
162 return (long)(10.0 * PI * (double)r * (double)r);
171 if ((ret = malloc(size)) == NULL) {
172 fprintf(stderr, "%s: out of memory\n", progname);
180 die_bad_bubble(Bubble *bb)
181 /* This is for use with GDB */
183 fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
189 null_bubble(Bubble *bb)
190 /* Returns true if the pointer passed is NULL. If not then this checks to
191 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
192 number is set correctly. This only a sanity check for debugging and is
193 turned off if DEBUG isn't set. */
195 if (bb == (Bubble *)NULL)
198 if ((bb->cell_index < 0) || (bb->cell_index > st->mesh_cells)) {
199 fprintf(stderr, "cell_index = %d\n", bb->cell_index);
202 if (bb->magic != BUBBLE_MAGIC) {
203 fprintf(stderr, "Magic = %d\n", bb->magic);
207 if ((bb->x < 0) || (bb->x > st->screen_width) ||
208 (bb->y < 0) || (bb->y > st->screen_height) ||
209 (bb->radius < st->bubble_min_radius) || (bb->radius >
210 st->bubble_max_radius)) {
212 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
213 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
218 if ((bb->x < 0) || (bb->x > st->screen_width) ||
219 (bb->y < 0) || (bb->y > st->screen_height) ||
220 (bb->radius < st->step_pixmaps[0]->radius) ||
221 (bb->radius > st->step_pixmaps[st->num_bubble_pixmaps-1]->radius)) {
223 "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
224 bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
235 print_bubble_list(Bubble *bb)
236 /* Print list of where all the bubbles are. For debugging purposes only. */
238 if (! null_bubble(bb)) {
239 printf(" (%d, %d) %d\n", bb->x, bb->y, bb->radius);
240 print_bubble_list(bb->next);
246 add_bubble_to_list(Bubble **list, Bubble *bb)
247 /* Take a pointer to a list of bubbles and stick bb at the head of the
250 Bubble *head = *list;
252 if (null_bubble(head)) {
253 bb->prev = (Bubble *)NULL;
254 bb->next = (Bubble *)NULL;
257 bb->prev = (Bubble *)NULL;
270 init_mesh (struct state *st)
271 /* Setup the mesh of bubbles */
275 st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
276 for (i = 0; i < st->mesh_cells; i++)
277 st->mesh[i] = (Bubble *)NULL;
281 cell_to_mesh(struct state *st, int x, int y)
282 /* convert cell coordinates to mesh index */
285 if ((x < 0) || (y < 0)) {
286 fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
290 return ((st->mesh_width * y) + x);
294 mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
295 /* convert mesh index into cell coordinates */
297 *cx = mi % st->mesh_width;
298 *cy = mi / st->mesh_width;
302 pixel_to_mesh(struct state *st, int x, int y)
303 /* convert screen coordinates into mesh index */
305 return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
309 verify_mesh_index(struct state *st, int x, int y)
310 /* check to see if (x,y) is in the mesh */
312 if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
314 return (cell_to_mesh(st, x, y));
319 print_adjacents(int *adj)
320 /* Print a list of the cells calculated above. For debugging only. */
325 for (i = 0; i < 8; i++)
326 printf("%d ", adj[i]);
332 add_to_mesh(struct state *st, Bubble *bb)
333 /* Add the given bubble to the mesh by sticking it on the front of the
334 list. bb is already allocated so no need to malloc() anything, just
338 if (null_bubble(bb)) {
339 fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
344 add_bubble_to_list(&st->mesh[bb->cell_index], bb);
349 print_mesh (struct state *st)
350 /* Print the contents of the mesh */
354 for (i = 0; i < st->mesh_cells; i++) {
355 if (! null_bubble(st->mesh[i])) {
356 printf("Mesh cell %d\n", i);
357 print_bubble_list(st->mesh[i]);
363 valid_mesh (struct state *st)
364 /* Check to see if the mesh is Okay. For debugging only. */
369 for (i = 0; i < st->mesh_cells; i++) {
371 while (! null_bubble(b))
377 total_bubbles (struct state *st)
378 /* Count how many bubbles there are in total. For debugging only. */
384 for (i = 0; i < st->mesh_cells; i++) {
386 while (! null_bubble(b)) {
397 calculate_adjacent_list (struct state *st)
398 /* Calculate the list of cells adjacent to a particular cell for use
404 st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
405 for (i = 0; i < st->mesh_cells; i++) {
406 st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
407 mesh_to_cell(st, i, &ix, &iy);
408 st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
409 st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
410 st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
411 st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
412 st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
413 st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
414 st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
415 st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
416 st->adjacent_list[i][8] = i;
421 adjust_areas (struct state *st)
422 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
431 maxarea = st->bubble_areas[st->bubble_max_radius+1];
433 maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
434 #else /* !FANCY_BUBBLES */
435 maxarea = st->bubble_areas[st->bubble_max_radius+1];
436 #endif /* !FANCY_BUBBLES */
437 maxvalue = (double)st->screen_width * 2.0 * (double)maxarea;
438 factor = (long)ceil(maxvalue / (double)LONG_MAX);
440 /* Overflow will occur in weighted_mean(). We must divide areas
441 each by factor so it will never do so. */
444 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
445 st->bubble_areas[i] /= factor;
446 if (st->bubble_areas[i] == 0)
447 st->bubble_areas[i] = 1;
450 for (i = 0; i <= st->num_bubble_pixmaps; i++) {
452 printf("area = %ld", st->step_pixmaps[i]->area);
454 st->step_pixmaps[i]->area /= factor;
455 if (st->step_pixmaps[i]->area == 0)
456 st->step_pixmaps[i]->area = 1;
458 printf("-> %ld\n", st->step_pixmaps[i]->area);
462 #else /* !FANCY_BUBBLES */
463 for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
464 st->bubble_areas[i] /= factor;
465 if (st->bubble_areas[i] == 0)
466 st->bubble_areas[i] = 1;
468 #endif /* !FANCY_BUBBLES */
471 printf("maxarea = %ld\n", maxarea);
472 printf("maxvalue = %g\n", maxvalue);
473 printf("LONG_MAX = %ld\n", LONG_MAX);
474 printf("factor = %ld\n", factor);
483 new_bubble (struct state *st)
484 /* Add a new bubble at some random position on the screen of the smallest
487 Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
489 /* Can't use null_bubble() here since magic number hasn't been set */
490 if (rv == (Bubble *)NULL) {
491 fprintf(stderr, "Ran out of memory!\n");
496 rv->radius = st->bubble_min_radius;
497 rv->area = st->bubble_areas[st->bubble_min_radius];
501 rv->radius = st->step_pixmaps[0]->radius;
502 rv->area = st->step_pixmaps[0]->area;
503 #endif /* FANCY_BUBBLES */
506 rv->magic = BUBBLE_MAGIC;
507 rv->x = random() % st->screen_width;
508 rv->y = random() % st->screen_height;
509 rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
515 show_bubble(struct state *st, Bubble *bb)
516 /* paint the bubble on the screen */
518 if (null_bubble(bb)) {
519 fprintf(stderr, "NULL bubble passed to show_bubble\n");
527 XDrawArc(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
528 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
532 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc,
533 (bb->x - bb->radius),
534 (bb->y - bb->radius));
536 XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window,
537 st->step_pixmaps[bb->step]->draw_gc,
538 0, 0, (bb->radius * 2),
540 (bb->x - bb->radius),
541 (bb->y - bb->radius));
542 #endif /* FANCY_BUBBLES */
548 hide_bubble(struct state *st, Bubble *bb)
549 /* erase the bubble */
551 if (null_bubble(bb)) {
552 fprintf(stderr, "NULL bubble passed to hide_bubble\n");
560 XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
561 (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
566 XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc,
567 (bb->x - bb->radius), (bb->y - bb->radius));
569 XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
570 (bb->x - bb->radius),
571 (bb->y - bb->radius),
575 #endif /* FANCY_BUBBLES */
581 delete_bubble_in_mesh(struct state *st, Bubble *bb, int keep_bubble)
582 /* Delete an individual bubble, adjusting list of bubbles around it.
583 If keep_bubble is true then the bubble isn't actually deleted. We
584 use this to allow bubbles to change mesh cells without reallocating,
585 (it needs this when two bubbles collide and the centre position is
586 recalculated, and this may stray over a mesh boundary). */
588 if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
589 bb->prev->next = bb->next;
590 bb->next->prev = bb->prev;
591 } else if ((!null_bubble(bb->prev)) &&
592 (null_bubble(bb->next))) {
593 bb->prev->next = (Bubble *)NULL;
594 bb->next = st->mesh[bb->cell_index];
595 } else if ((null_bubble(bb->prev)) &&
596 (!null_bubble(bb->next))) {
597 bb->next->prev = (Bubble *)NULL;
598 st->mesh[bb->cell_index] = bb->next;
599 bb->next = st->mesh[bb->cell_index];
601 /* Only item on list */
602 st->mesh[bb->cell_index] = (Bubble *)NULL;
610 /* Saves ugly inline code */
612 return ((unsigned long)x * (unsigned long)x);
616 get_closest_bubble(struct state *st, Bubble *bb)
617 /* Find the closest bubble touching the this bubble, NULL if none are
620 Bubble *rv = (Bubble *)NULL;
622 unsigned long separation2, touchdist2;
624 unsigned long closest2 = ULONG_MAX;
628 if (null_bubble(bb)) {
629 fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
635 for (i = 0; i < 9; i++) {
636 /* There is a bug here where bb->cell_index is negaitve.. */
638 if ((bb->cell_index < 0) || (bb->cell_index >= st->mesh_cells)) {
639 fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
643 /* printf("%d,", bb->cell_index); */
644 if (st->adjacent_list[bb->cell_index][i] != -1) {
645 tmp = st->mesh[st->adjacent_list[bb->cell_index][i]];
646 while (! null_bubble(tmp)) {
650 separation2 = ulongsqrint(dx) + ulongsqrint(dy);
651 /* Add extra leeway so circles _never_ overlap */
652 touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
653 if ((separation2 <= touchdist2) && (separation2 <
656 closest2 = separation2;
669 ldr_barf (struct state *st)
675 long_div_round(long num, long dem)
680 if ((num < 0) || (dem < 0)) {
681 fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
689 if (moddo > (dem / 2))
693 if ((divvie < 0) || (moddo < 0)) {
694 fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
704 weighted_mean(int n1, int n2, long w1, long w2)
705 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
708 if ((w1 <= 0) || (w2 <= 0)) {
710 "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
715 return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
720 bubble_eat(struct state *st, Bubble *diner, Bubble *food)
721 /* The diner eats the food. Returns true (1) if the diner still exists */
727 if ((null_bubble(diner)) || (null_bubble(food))) {
728 fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
733 /* We hide the diner even in the case that it doesn't grow so that
734 if the food overlaps its boundary it is replaced. This could
735 probably be solved by letting bubbles eat others which are close
736 but not quite touching. It's probably worth it, too, since we
737 would then not have to redraw bubbles which don't change in
740 hide_bubble(st, diner);
741 hide_bubble(st, food);
742 diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
743 diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
744 newmi = pixel_to_mesh(st, diner->x, diner->y);
745 diner->area += food->area;
746 delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
749 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
750 diner->area = st->bubble_areas[st->bubble_max_radius];
753 if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
754 diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
756 #endif /* FANCY_BUBBLES */
759 if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
760 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
764 if ((! st->simple) && (diner->area >
765 st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
766 delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
769 #endif /* FANCY_BUBBLES */
773 if (diner->area > st->bubble_areas[diner->radius + 1]) {
774 /* Move the bubble to a new radius */
776 while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
780 show_bubble(st, diner);
783 if (diner->area > st->step_pixmaps[diner->step+1]->area) {
785 while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
788 diner->radius = st->step_pixmaps[diner->step]->radius;
790 show_bubble(st, diner);
791 #endif /* FANCY_BUBBLES */
794 /* Now adjust locations and cells if need be */
795 if (newmi != diner->cell_index) {
796 delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
797 diner->cell_index = newmi;
798 add_to_mesh(st, diner);
805 merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
806 /* These two bubbles merge into one. If the first one wins out return
807 1 else return 2. If there is no winner (it explodes) then return 0 */
815 if ((null_bubble(b1) || null_bubble(b2))) {
816 fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
823 delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
827 if (b1size > b2size) {
828 switch (bubble_eat(st, b1, b2)) {
838 } else if (b1size < b2size) {
839 switch (bubble_eat(st, b2, b1)) {
850 if ((random() % 2) == 0) {
851 switch (bubble_eat(st, b1, b2)) {
862 switch (bubble_eat(st, b2, b1)) {
874 fprintf(stderr, "An error occurred in merge_bubbles()\n");
879 insert_new_bubble(struct state *st, Bubble *tmp)
880 /* Calculates which bubbles are eaten when a new bubble tmp is
881 inserted. This is called recursively in case when a bubble grows
882 it eats others. Careful to pick out disappearing bubbles. */
888 if (null_bubble(tmp)) {
889 fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
895 touch = get_closest_bubble(st, nextbub);
896 if (null_bubble(touch))
901 /* Merge all touching bubbles */
902 while (! null_bubble(touch)) {
903 switch (merge_bubbles(st, nextbub, touch)) {
905 /* touch ate nextbub and survived */
909 /* nextbub ate touch and survived */
912 /* somebody ate someone else but they exploded */
913 nextbub = (Bubble *)NULL;
916 /* something went wrong */
917 fprintf(stderr, "Error occurred in insert_new_bubble()\n");
921 /* Check to see if any bubble survived. */
922 if (null_bubble(nextbub))
925 /* Check to see if there are any other bubbles still in the area
926 and if we need to do this all over again for them. */
927 touch = get_closest_bubble(st, nextbub);
930 if (null_bubble(nextbub))
933 /* Shift bubble down. Break if we run off the screen. */
935 if (drop_bubble( st, nextbub ) == -1)
939 /* Check to see if there are any other bubbles still in the area
940 and if we need to do this all over again for them. */
941 touch = get_closest_bubble(st, nextbub);
942 if (null_bubble(touch)) {
943 /* We also continue every so often if we're dropping and the bubble is at max size */
946 if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
951 if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
954 #endif /* FANCY_BUBBLES */
964 leave_trail(struct state *st, Bubble *bb )
968 tmp = new_bubble(st);
970 tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
971 tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
972 add_to_mesh(st, tmp);
973 insert_new_bubble(st, tmp);
974 show_bubble( st, tmp );
979 drop_bubble( struct state *st, Bubble *bb )
983 hide_bubble( st, bb );
986 (bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
989 (bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
990 #endif /* FANCY_BUBBLES */
991 if ((bb->y < 0) || (bb->y > st->screen_height)) {
992 delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
996 show_bubble( st, bb );
998 /* Now adjust locations and cells if need be */
999 newmi = pixel_to_mesh(st, bb->x, bb->y);
1000 if (newmi != bb->cell_index) {
1001 delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
1002 bb->cell_index = newmi;
1003 add_to_mesh(st, bb);
1008 if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
1009 leave_trail( st, bb );
1011 #ifdef FANCY_BUBBLES
1013 if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
1014 leave_trail( st, bb );
1016 #endif /* FANCY_BUBBLES */
1025 get_length_of_bubble_list(Bubble *bb)
1030 while (! null_bubble(tmp)) {
1040 * Pixmap stuff used regardless of whether file I/O is available. Must
1041 * still check for XPM, though!
1044 #ifdef FANCY_BUBBLES
1047 * Pixmaps without file I/O (but do have XPM)
1051 pixmap_sort(Bubble_Step **head, int numelems)
1052 /* Couldn't get qsort to work right with this so I wrote my own. This puts
1053 the numelems length array with first element at head into order of radius.
1057 Bubble_Step *least = 0;
1058 int minradius = INT_MAX;
1061 for (i = 0; i < numelems; i++) {
1062 if (head[i]->radius < minradius) {
1064 minradius = head[i]->radius;
1067 if (*head != least) {
1068 memcpy(&tmp, least, sizeof(Bubble_Step));
1069 memcpy(least, *head, sizeof(Bubble_Step));
1070 memcpy(*head, &tmp, sizeof(Bubble_Step));
1074 pixmap_sort(&head[1], numelems-1);
1078 extrapolate(int i1, int i2)
1080 return (i2 + (i2 - i1));
1084 make_pixmap_array(struct state *st, Bubble_Step *list)
1085 /* From a linked list of bubbles construct the array step_pixmaps */
1087 Bubble_Step *tmp = list;
1093 if (list == (Bubble_Step *)NULL) {
1094 fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1098 st->num_bubble_pixmaps = 1;
1099 while(tmp->next != (Bubble_Step *)NULL) {
1101 ++st->num_bubble_pixmaps;
1104 if (st->num_bubble_pixmaps < 2) {
1105 fprintf(stderr, "Must be at least two bubbles in file\n");
1109 st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) *
1110 sizeof(Bubble_Step *));
1112 /* Copy them blindly into the array for sorting. */
1116 st->step_pixmaps[ind++] = tmp;
1118 } while(tmp != (Bubble_Step *)NULL);
1120 /* We make another bubble beyond the ones with pixmaps so that the final
1121 bubble hangs around and doesn't pop immediately. It's radius and area
1122 are found by extrapolating from the largest two bubbles with pixmaps. */
1124 st->step_pixmaps[st->num_bubble_pixmaps] =
1125 (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1126 st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
1128 pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
1131 if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
1132 fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1136 st->step_pixmaps[st->num_bubble_pixmaps]->radius =
1137 extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
1138 st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
1139 st->step_pixmaps[st->num_bubble_pixmaps]->area =
1140 calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
1144 /* Now check for correct order */
1145 for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
1147 if (st->step_pixmaps[ind]->radius < prevrad) {
1148 fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1152 prevrad = st->step_pixmaps[ind]->radius;
1156 /* Now populate the droppage values */
1157 for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
1158 st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
1162 make_pixmap_from_default(struct state *st, char **pixmap_data, Bubble_Step *bl)
1163 /* Read pixmap data which has been compiled into the program and a pointer
1164 to which has been passed.
1166 This is virtually copied verbatim from make_pixmap_from_file() above and
1167 changes made to either should be propagated onwards! */
1172 if (pixmap_data == (char **)0) {
1173 fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1178 if (bl == (Bubble_Step *)NULL) {
1179 fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1183 #ifdef FANCY_BUBBLES
1186 bl->ball = xpm_data_to_pixmap (st->dpy, st->window, (char **) pixmap_data,
1187 &w, &h, &bl->shape_mask);
1188 bl->radius = MAX(w, h) / 2;
1189 bl->area = calc_bubble_area(st, bl->radius);
1191 #endif /* FANCY_BUBBLES */
1193 gcv.foreground = st->default_fg_pixel;
1194 gcv.function = GXcopy;
1195 bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1196 XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
1198 gcv.foreground = st->default_bg_pixel;
1199 gcv.function = GXcopy;
1200 bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1201 XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
1205 default_to_pixmaps (struct state *st)
1206 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1209 Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1210 Bubble_Step *newpix, *tmppix;
1213 init_default_bubbles();
1215 for (i = 0; i < num_default_bubbles; i++) {
1216 pixpt = default_bubbles[i];
1217 newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1218 make_pixmap_from_default(st, pixpt, newpix);
1219 /* Now add to list */
1220 if (pixmap_list == (Bubble_Step *)NULL) {
1221 pixmap_list = newpix;
1223 tmppix = pixmap_list;
1224 while (tmppix->next != (Bubble_Step *)NULL)
1225 tmppix = tmppix->next;
1226 tmppix->next = newpix;
1228 newpix->next = (Bubble_Step *)NULL;
1231 /* Finally construct step_pixmaps[] */
1232 make_pixmap_array(st, pixmap_list);
1235 #endif /* FANCY_BUBBLES */
1244 get_resources(struct state *st)
1245 /* Get the appropriate X resources and warn about any inconsistencies. */
1248 XWindowAttributes xgwa;
1251 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1252 cmap = xgwa.colormap;
1254 st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1255 st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1256 st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1257 /* Forbid rendered bubbles on monochrome displays */
1258 if ((mono_p) && (! st->simple)) {
1261 "Rendered bubbles not supported on monochrome displays\n");
1264 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1266 s = get_string_resource (st->dpy, "mode", "Mode");
1268 if (!s || !*s || !strcasecmp (s, "float"))
1270 else if (!strcasecmp (s, "rise"))
1272 else if (!strcasecmp (s, "drop"))
1275 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1277 st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1278 st->drop_dir = (st->drop ? 1 : -1);
1279 if (st->drop || rise)
1282 st->default_fg_pixel = get_pixel_resource (st->dpy,
1283 cmap, "foreground", "Foreground");
1284 st->default_bg_pixel = get_pixel_resource (st->dpy,
1285 cmap, "background", "Background");
1289 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1292 fprintf(stderr, "-broken not available in simple mode\n");
1294 #ifndef FANCY_BUBBLES
1296 #else /* FANCY_BUBBLES */
1297 st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1298 #endif /* FANCY_BUBBLES */
1303 bubbles_init (Display *dpy, Window window)
1305 struct state *st = (struct state *) calloc (1, sizeof(*st));
1307 XWindowAttributes xgwa;
1311 st->window = window;
1315 XGetWindowAttributes (st->dpy, st->window, &xgwa);
1318 printf("sizof(int) on this platform is %d\n", sizeof(int));
1319 printf("sizof(long) on this platform is %d\n", sizeof(long));
1322 st->screen_width = xgwa.width;
1323 st->screen_height = xgwa.height;
1324 st->screen_depth = xgwa.depth;
1327 /* These are pretty much plucked out of the air */
1328 st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width,
1329 st->screen_height)));
1330 st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1331 st->screen_height)));
1332 /* Some trivial values */
1333 if (st->bubble_min_radius < 1)
1334 st->bubble_min_radius = 1;
1335 if (st->bubble_max_radius <= st->bubble_min_radius)
1336 st->bubble_max_radius = st->bubble_min_radius + 1;
1338 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1340 /* store area of each bubble of certain radius as number of 1/10s of
1341 a pixel area. PI is defined in <math.h> */
1342 st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1343 for (i = 0; i < st->bubble_min_radius; i++)
1344 st->bubble_areas[i] = 0;
1345 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1346 st->bubble_areas[i] = calc_bubble_area(st, i);
1348 /* Now populate the droppage values */
1349 st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1350 for (i = 0; i < st->bubble_min_radius; i++)
1351 st->bubble_droppages[i] = 0;
1352 for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1353 st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1355 st->mesh_length = (2 * st->bubble_max_radius) + 3;
1357 #ifndef FANCY_BUBBLES
1359 "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1361 #else /* FANCY_BUBBLES */
1362 /* Make sure all #ifdef sort of things have been taken care of in
1364 default_to_pixmaps(st);
1366 /* Set mesh length */
1367 st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1368 #endif /* FANCY_BUBBLES */
1370 /* Am I missing something in here??? */
1373 st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1374 st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1375 st->mesh_cells = st->mesh_width * st->mesh_height;
1378 calculate_adjacent_list(st);
1382 /* Graphics contexts for simple mode */
1384 gcv.foreground = st->default_fg_pixel;
1385 st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1386 gcv.foreground = st->default_bg_pixel;
1387 st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1390 XClearWindow (st->dpy, st->window);
1392 # ifndef FANCY_BUBBLES
1399 static unsigned long
1400 bubbles_draw (Display *dpy, Window window, void *closure)
1402 struct state *st = (struct state *) closure;
1404 for (i = 0; i < 5; i++)
1406 Bubble *tmp = new_bubble(st);
1407 add_to_mesh(st, tmp);
1408 insert_new_bubble(st, tmp);
1415 bubbles_reshape (Display *dpy, Window window, void *closure,
1416 unsigned int w, unsigned int h)
1421 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1427 bubbles_free (Display *dpy, Window window, void *closure)
1429 struct state *st = (struct state *) closure;
1433 XSCREENSAVER_MODULE ("Bubbles", bubbles)