1 /* xscreensaver, Copyright (c) 2005 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 <X11/Xutil.h>
21 #include "xpm-pixmap.h"
27 unsigned long fill_color;
35 XWindowAttributes xgwa;
37 unsigned long fg_color, bg_color;
58 reset_boxes (state *st)
60 static Bool once = False;
65 st->color_horiz_p = random() & 1;
67 if (once && st->colors)
68 free_colors (st->dpy, st->xgwa.colormap, st->colors, st->ncolors);
72 char *s = get_string_resource ("mode", "Mode");
73 if (!s || !*s || !strcasecmp (s, "random"))
75 else if (!strcasecmp (s, "squares") || !strcasecmp (s, "square"))
77 else if (!strcasecmp (s, "circles") || !strcasecmp (s, "circle"))
82 "%s: mode must be random, squares, or circles, not '%s'\n",
89 st->circles_p = random() & 1;
91 st->circles_p = (mode == 1);
95 if (st->image || get_boolean_resource ("grab", "Boolean"))
98 if (st->image) XDestroyImage (st->image);
101 if (!get_boolean_resource ("peek", "Boolean"))
102 p = XCreatePixmap (st->dpy, st->window,
103 st->xgwa.width, st->xgwa.height,
106 load_random_image (st->xgwa.screen, st->window,
107 (p ? p : st->window), 0, 0);
108 st->image = XGetImage (st->dpy, (p ? p : st->window), 0, 0,
109 st->xgwa.width, st->xgwa.height, ~0L, ZPixmap);
110 if (p) XFreePixmap (st->dpy, p);
111 XSync (st->dpy, False);
112 XSetWindowBackground (st->dpy, st->window, st->bg_color);
117 st->ncolors = get_integer_resource ("colors", "Colors"); /* re-get */
118 make_smooth_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
119 st->colors, &st->ncolors, True, 0, False);
122 XClearWindow (st->dpy, st->window);
127 init_boxes (Display *dpy, Window window)
130 state *st = (state *) calloc (1, sizeof (*st));
135 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
136 XSelectInput (dpy, window, st->xgwa.your_event_mask | ExposureMask);
138 if (! get_boolean_resource ("grab", "Boolean"))
140 st->ncolors = get_integer_resource ("colors", "Colors");
141 if (st->ncolors < 1) st->ncolors = 1;
142 st->colors = (XColor *) malloc (sizeof(XColor) * st->ncolors);
145 st->inc = get_integer_resource ("growBy", "GrowBy");
146 st->spacing = get_integer_resource ("spacing", "Spacing");
147 st->border_size = get_integer_resource ("borderSize", "BorderSize");
148 st->fg_color = get_pixel_resource ("foreground", "Foreground",
149 st->dpy, st->xgwa.colormap);
150 st->bg_color = get_pixel_resource ("background", "Background",
151 st->dpy, st->xgwa.colormap);
152 if (st->inc < 1) st->inc = 1;
153 if (st->border_size < 0) st->border_size = 0;
155 gcv.line_width = st->border_size;
156 gcv.background = st->bg_color;
157 st->gc = XCreateGC (st->dpy, st->window, GCBackground|GCLineWidth, &gcv);
159 st->box_count = get_integer_resource ("boxCount", "BoxCount");
160 if (st->box_count < 1) st->box_count = 1;
163 st->boxes_size = st->box_count * 2;
164 st->boxes = (box *) calloc (st->boxes_size, sizeof(*st->boxes));
172 reshape_boxes (state *st)
175 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
176 for (i = 0; i < st->nboxes; i++)
178 box *b = &st->boxes[i];
185 boxes_overlap_p (box *a, box *b, int pad)
187 /* Two rectangles overlap if the max of the tops is less than the
188 min of the bottoms and the max of the lefts is less than the min
193 # define MAX(A,B) ((A)>(B)?(A):(B))
194 # define MIN(A,B) ((A)<(B)?(A):(B))
196 int maxleft = MAX(a->x - pad, b->x);
197 int maxtop = MAX(a->y - pad, b->y);
198 int minright = MIN(a->x + a->w + pad + pad - 1, b->x + b->w);
199 int minbot = MIN(a->y + a->h + pad + pad - 1, b->y + b->h);
200 return (maxtop < minbot && maxleft < minright);
205 circles_overlap_p (box *a, box *b, int pad)
207 int ar = a->w/2; /* radius */
209 int ax = a->x + ar; /* center */
213 int d2 = (((bx - ax) * (bx - ax)) + /* distance between centers squared */
214 ((by - ay) * (by - ay)));
215 int r2 = ((ar + br + pad) * /* sum of radii squared */
222 box_collides_p (state *st, box *a, int pad)
226 /* collide with wall */
227 if (a->x - pad < 0 ||
229 a->x + a->w + pad + pad >= st->xgwa.width ||
230 a->y + a->h + pad + pad >= st->xgwa.height)
233 /* collide with another box */
234 for (i = 0; i < st->nboxes; i++)
236 box *b = &st->boxes[i];
239 ? circles_overlap_p (a, b, pad)
240 : boxes_overlap_p (a, b, pad)))
249 grow_boxes (state *st)
251 int inc2 = st->inc + st->spacing + st->border_size;
255 /* check box collisions, and grow if none.
257 for (i = 0; i < st->nboxes; i++)
259 box *a = &st->boxes[i];
260 if (!(a->flags & ALIVE)) continue;
262 if (box_collides_p (st, a, inc2))
271 a->w += st->inc + st->inc;
272 a->h += st->inc + st->inc;
278 while (live_count < st->box_count)
282 if (st->boxes_size <= st->nboxes)
284 st->boxes_size = (st->boxes_size * 1.2) + st->nboxes;
286 realloc (st->boxes, st->boxes_size * sizeof(*st->boxes));
289 fprintf (stderr, "%s: out of memory (%d boxes)\n",
290 progname, st->boxes_size);
295 a = &st->boxes[st->nboxes-1];
298 for (i = 0; i < 5000; i++)
300 a->x = inc2 + (random() % (st->xgwa.width - inc2));
301 a->y = inc2 + (random() % (st->xgwa.height - inc2));
305 if (! box_collides_p (st, a, inc2))
313 if (! (a->flags & ALIVE) || /* too many retries; */
314 st->nboxes > 65535) /* that's about 1MB of box structs. */
316 st->nboxes--; /* go into "fade out" mode now. */
317 st->growing_p = False;
319 XSync (st->dpy, False);
325 /* Pick colors for this box */
328 int w = st->image->width;
329 int h = st->image->height;
330 a->fill_color = XGetPixel (st->image, a->x % w, a->y % h);
334 int n = (st->color_horiz_p
335 ? (a->x * st->ncolors / st->xgwa.width)
336 : (a->y * st->ncolors / st->xgwa.height));
337 a->fill_color = st->colors [n % st->ncolors].pixel;
344 shrink_boxes (state *st)
349 for (i = 0; i < st->nboxes; i++)
351 box *a = &st->boxes[i];
353 if (a->w <= 0 || a->h <= 0) continue;
357 a->w -= st->inc + st->inc;
358 a->h -= st->inc + st->inc;
360 if (a->w < 0) a->w = 0;
361 if (a->h < 0) a->h = 0;
363 if (a->w > 0 && a->h > 0)
373 draw_boxes (state *st)
376 for (i = 0; i < st->nboxes; i++)
378 box *b = &st->boxes[i];
382 /* When shrinking, black out an area outside of the border
383 before re-drawing the box.
386 if (b->w <= -st->inc*2 || b->h <= -st->inc*2) continue;
388 XSetForeground (st->dpy, st->gc, st->bg_color);
389 XSetLineAttributes (st->dpy, st->gc,
390 (st->inc + st->border_size) * 2,
391 LineSolid, CapButt, JoinMiter);
394 XDrawArc (st->dpy, st->window, st->gc,
396 (b->w > 0 ? b->w : 1),
397 (b->h > 0 ? b->h : 1),
400 XDrawRectangle (st->dpy, st->window, st->gc,
402 (b->w > 0 ? b->w : 1),
403 (b->h > 0 ? b->h : 1));
404 XSetLineAttributes (st->dpy, st->gc, st->border_size,
405 LineSolid, CapButt, JoinMiter);
408 if (b->w <= 0 || b->h <= 0) continue;
409 if (! (b->flags & CHANGED)) continue;
410 b->flags &= ~CHANGED;
412 XSetForeground (st->dpy, st->gc, b->fill_color);
415 XFillArc (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h,
418 XFillRectangle (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h);
420 if (st->border_size > 0)
422 unsigned int bd = (st->image
424 : st->colors [(b->fill_color + st->ncolors/2)
425 % st->ncolors].pixel);
426 XSetForeground (st->dpy, st->gc, bd);
428 XDrawArc (st->dpy, st->window, st->gc, b->x, b->y, b->w, b->h,
431 XDrawRectangle (st->dpy, st->window, st->gc,
432 b->x, b->y, b->w, b->h);
438 handle_events (state *st)
440 XSync (st->dpy, False);
441 while (XPending (st->dpy))
444 XNextEvent (st->dpy, &event);
445 if (event.xany.type == ConfigureNotify ||
446 event.xany.type == Expose)
448 else if (event.xany.type == ButtonPress)
449 st->growing_p = !st->growing_p;
451 screenhack_handle_event (st->dpy, &event);
457 char *progclass = "BoxFit";
459 char *defaults [] = {
460 ".background: black",
461 ".foreground: #444444",
474 XrmOptionDescRec options [] = {
475 { "-delay", ".delay", XrmoptionSepArg, 0 },
476 { "-colors", ".colors", XrmoptionSepArg, 0 },
477 { "-count", ".boxCount", XrmoptionSepArg, 0 },
478 { "-growby", ".growBy", XrmoptionSepArg, 0 },
479 { "-spacing", ".spacing", XrmoptionSepArg, 0 },
480 { "-border", ".borderSize", XrmoptionSepArg, 0 },
481 { "-circles", ".mode", XrmoptionNoArg, "circles" },
482 { "-squares", ".mode", XrmoptionNoArg, "squares" },
483 { "-random", ".mode", XrmoptionNoArg, "random" },
484 { "-grab", ".grab", XrmoptionNoArg, "True" },
485 { "-no-grab", ".grab", XrmoptionNoArg, "False" },
486 { "-peek", ".peek", XrmoptionNoArg, "True" },
487 { "-no-peek", ".peek", XrmoptionNoArg, "False" },
493 screenhack (Display *dpy, Window window)
495 state *st = init_boxes (dpy, window);
496 int delay = get_integer_resource ("delay", "Integer");
507 if (delay) usleep (delay);