1 /* xscreensaver, Copyright (c) 2005-2014 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Boxfit -- fills space with a gradient of growing boxes or circles.
13 * Written by jwz, 21-Feb-2005.
15 * Inspired by http://www.levitated.net/daily/levBoxFitting.html
18 #include "screenhack.h"
20 #include "xpm-pixmap.h"
27 unsigned long fill_color;
35 XWindowAttributes xgwa;
37 unsigned long fg_color, bg_color;
59 async_load_state *img_loader;
60 Pixmap loading_pixmap;
66 reset_boxes (state *st)
70 st->color_horiz_p = random() & 1;
72 if (st->done_once && st->colors)
73 free_colors (st->xgwa.screen, st->xgwa.colormap, st->colors, st->ncolors);
77 char *s = get_string_resource (st->dpy, "mode", "Mode");
78 if (!s || !*s || !strcasecmp (s, "random"))
80 else if (!strcasecmp (s, "squares") || !strcasecmp (s, "square"))
82 else if (!strcasecmp (s, "circles") || !strcasecmp (s, "circle"))
87 "%s: mode must be random, squares, or circles, not '%s'\n",
94 st->circles_p = random() & 1;
96 st->circles_p = (st->mode == 1);
100 if (st->image || get_boolean_resource (st->dpy, "grab", "Boolean"))
102 if (st->image) XDestroyImage (st->image);
105 if (st->loading_pixmap) abort();
106 if (st->img_loader) abort();
107 if (!get_boolean_resource (st->dpy, "peek", "Boolean"))
108 st->loading_pixmap = XCreatePixmap (st->dpy, st->window,
109 st->xgwa.width, st->xgwa.height,
112 XClearWindow (st->dpy, st->window);
113 st->img_loader = load_image_async_simple (0, st->xgwa.screen,
122 st->ncolors = get_integer_resource (st->dpy, "colors", "Colors"); /* re-get */
123 if (st->ncolors < 1) st->ncolors = 1;
124 make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
125 st->colors, &st->ncolors, True, 0, False);
126 if (st->ncolors < 1) abort();
127 XClearWindow (st->dpy, st->window);
133 reshape_boxes (state *st)
136 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
137 for (i = 0; i < st->nboxes; i++)
139 box *b = &st->boxes[i];
145 boxfit_init (Display *dpy, Window window)
148 state *st = (state *) calloc (1, sizeof (*st));
152 st->delay = get_integer_resource (dpy, "delay", "Integer");
154 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
155 /* XSelectInput (dpy, window, st->xgwa.your_event_mask | ExposureMask);*/
157 if (! get_boolean_resource (dpy, "grab", "Boolean"))
159 st->ncolors = get_integer_resource (dpy, "colors", "Colors");
160 if (st->ncolors < 1) st->ncolors = 1;
161 st->colors = (XColor *) malloc (sizeof(XColor) * st->ncolors);
164 st->inc = get_integer_resource (dpy, "growBy", "GrowBy");
165 st->spacing = get_integer_resource (dpy, "spacing", "Spacing");
166 st->border_size = get_integer_resource (dpy, "borderSize", "BorderSize");
167 st->fg_color = get_pixel_resource (st->dpy, st->xgwa.colormap,
168 "foreground", "Foreground");
169 st->bg_color = get_pixel_resource (st->dpy, st->xgwa.colormap,
170 "background", "Background");
171 if (st->inc < 1) st->inc = 1;
172 if (st->border_size < 0) st->border_size = 0;
174 gcv.line_width = st->border_size;
175 gcv.background = st->bg_color;
176 st->gc = XCreateGC (st->dpy, st->window, GCBackground|GCLineWidth, &gcv);
178 st->box_count = get_integer_resource (dpy, "boxCount", "BoxCount");
179 if (st->box_count < 1) st->box_count = 1;
182 st->boxes_size = st->box_count * 2;
183 st->boxes = (box *) calloc (st->boxes_size, sizeof(*st->boxes));
194 boxes_overlap_p (box *a, box *b, int pad)
196 /* Two rectangles overlap if the max of the tops is less than the
197 min of the bottoms and the max of the lefts is less than the min
202 # define MAX(A,B) ((A)>(B)?(A):(B))
203 # define MIN(A,B) ((A)<(B)?(A):(B))
205 int maxleft = MAX(a->x - pad, b->x);
206 int maxtop = MAX(a->y - pad, b->y);
207 int minright = MIN(a->x + a->w + pad + pad - 1, b->x + b->w);
208 int minbot = MIN(a->y + a->h + pad + pad - 1, b->y + b->h);
209 return (maxtop < minbot && maxleft < minright);
214 circles_overlap_p (box *a, box *b, int pad)
216 int ar = a->w/2; /* radius */
218 int ax = a->x + ar; /* center */
222 int d2 = (((bx - ax) * (bx - ax)) + /* distance between centers squared */
223 ((by - ay) * (by - ay)));
224 int r2 = ((ar + br + pad) * /* sum of radii squared */
231 box_collides_p (state *st, box *a, int pad)
235 /* collide with wall */
236 if (a->x - pad < 0 ||
238 a->x + a->w + pad + pad >= st->xgwa.width ||
239 a->y + a->h + pad + pad >= st->xgwa.height)
242 /* collide with another box */
243 for (i = 0; i < st->nboxes; i++)
245 box *b = &st->boxes[i];
248 ? circles_overlap_p (a, b, pad)
249 : boxes_overlap_p (a, b, pad)))
258 grow_boxes (state *st)
260 int inc2 = st->inc + st->spacing + st->border_size;
264 /* check box collisions, and grow if none.
266 for (i = 0; i < st->nboxes; i++)
268 box *a = &st->boxes[i];
269 if (!(a->flags & ALIVE)) continue;
271 if (box_collides_p (st, a, inc2))
280 a->w += st->inc + st->inc;
281 a->h += st->inc + st->inc;
287 while (live_count < st->box_count)
291 if (st->boxes_size <= st->nboxes)
293 st->boxes_size = (st->boxes_size * 1.2) + st->nboxes;
295 realloc (st->boxes, st->boxes_size * sizeof(*st->boxes));
298 fprintf (stderr, "%s: out of memory (%d boxes)\n",
299 progname, st->boxes_size);
304 a = &st->boxes[st->nboxes-1];
307 for (i = 0; i < 100; i++)
309 a->x = inc2 + (random() % (st->xgwa.width - inc2));
310 a->y = inc2 + (random() % (st->xgwa.height - inc2));
314 if (! box_collides_p (st, a, inc2))
322 if (! (a->flags & ALIVE) || /* too many retries; */
323 st->nboxes > 65535) /* that's about 1MB of box structs. */
325 st->nboxes--; /* go into "fade out" mode now. */
326 st->growing_p = False;
327 return 2000000; /* make customizable... */
330 /* Pick colors for this box */
333 int w = st->image->width;
334 int h = st->image->height;
335 a->fill_color = XGetPixel (st->image, a->x % w, a->y % h);
339 int n = (st->color_horiz_p
340 ? (a->x * st->ncolors / st->xgwa.width)
341 : (a->y * st->ncolors / st->xgwa.height));
342 a->fill_color = st->colors [n % st->ncolors].pixel;
351 shrink_boxes (state *st)
356 for (i = 0; i < st->nboxes; i++)
358 box *a = &st->boxes[i];
360 if (a->w <= 0 || a->h <= 0) continue;
364 a->w -= st->inc + st->inc;
365 a->h -= st->inc + st->inc;
367 if (a->w < 0) a->w = 0;
368 if (a->h < 0) a->h = 0;
370 if (a->w > 0 && a->h > 0)
374 if (remaining == 0) {
384 draw_boxes (state *st)
387 for (i = 0; i < st->nboxes; i++)
389 box *b = &st->boxes[i];
391 if (b->flags & UNDEAD) continue;
392 if (! (b->flags & CHANGED)) continue;
393 b->flags &= ~CHANGED;
397 /* When shrinking, black out an area outside of the border
398 before re-drawing the box.
400 int margin = st->inc + st->border_size;
402 XSetForeground (st->dpy, st->gc, st->bg_color);
404 XFillArc (st->dpy, st->window, st->gc,
405 b->x - margin, b->y - margin,
406 b->w + (margin*2), b->h + (margin*2),
409 XFillRectangle (st->dpy, st->window, st->gc,
410 b->x - margin, b->y - margin,
411 b->w + (margin*2), b->h + (margin*2));
413 if (b->w <= 0 || b->h <= 0)
414 b->flags |= UNDEAD; /* really very dead now */
417 if (b->w <= 0 || b->h <= 0) continue;
419 XSetForeground (st->dpy, st->gc, b->fill_color);
422 XFillArc (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h,
425 XFillRectangle (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h);
427 if (st->border_size > 0)
429 unsigned int bd = (st->image
431 : st->colors [(b->fill_color + st->ncolors/2)
432 % st->ncolors].pixel);
433 XSetForeground (st->dpy, st->gc, bd);
435 XDrawArc (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h,
438 XDrawRectangle (st->dpy, st->window, st->gc,
439 b->x, b->y, b->w, b->h);
446 boxfit_draw (Display *dpy, Window window, void *closure)
448 state *st = (state *) closure;
451 if (st->img_loader) /* still loading */
453 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
454 if (! st->img_loader) /* just finished */
456 st->image = XGetImage (st->dpy,
457 (st->loading_pixmap ? st->loading_pixmap :
460 st->xgwa.width, st->xgwa.height, ~0L,
462 if (st->loading_pixmap) XFreePixmap (st->dpy, st->loading_pixmap);
463 XSetWindowBackground (st->dpy, st->window, st->bg_color);
464 if (st->loading_pixmap)
465 XClearWindow (st->dpy, st->window);
467 st->countdown = 2000000;
468 st->loading_pixmap = 0;
473 if (st->countdown > 0)
475 st->countdown -= st->delay;
476 if (st->countdown <= 0)
479 XClearWindow (st->dpy, st->window);
486 delay = grow_boxes (st);
488 delay = shrink_boxes (st);
495 boxfit_reshape (Display *dpy, Window window, void *closure,
496 unsigned int w, unsigned int h)
498 state *st = (state *) closure;
503 boxfit_event (Display *dpy, Window window, void *closure, XEvent *event)
505 state *st = (state *) closure;
506 if (screenhack_event_helper (dpy, window, event))
508 st->growing_p = !st->growing_p;
515 boxfit_free (Display *dpy, Window window, void *closure)
520 static const char *boxfit_defaults [] = {
521 ".background: black",
522 ".foreground: #444444",
533 "*grabDesktopImages: False", /* HAVE_COCOA */
534 "*chooseRandomImages: True", /* HAVE_COCOA */
536 "*ignoreRotation: True",
537 "*rotateImages: True",
542 static XrmOptionDescRec boxfit_options [] = {
543 { "-delay", ".delay", XrmoptionSepArg, 0 },
544 { "-colors", ".colors", XrmoptionSepArg, 0 },
545 { "-count", ".boxCount", XrmoptionSepArg, 0 },
546 { "-growby", ".growBy", XrmoptionSepArg, 0 },
547 { "-spacing", ".spacing", XrmoptionSepArg, 0 },
548 { "-border", ".borderSize", XrmoptionSepArg, 0 },
549 { "-mode", ".mode", XrmoptionSepArg, 0 },
550 { "-circles", ".mode", XrmoptionNoArg, "circles" },
551 { "-squares", ".mode", XrmoptionNoArg, "squares" },
552 { "-random", ".mode", XrmoptionNoArg, "random" },
553 { "-grab", ".grab", XrmoptionNoArg, "True" },
554 { "-no-grab", ".grab", XrmoptionNoArg, "False" },
555 { "-peek", ".peek", XrmoptionNoArg, "True" },
556 { "-no-peek", ".peek", XrmoptionNoArg, "False" },
561 XSCREENSAVER_MODULE ("BoxFit", boxfit)