1 /* xscreensaver, Copyright (c) 1992-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
12 #include "screenhack.h"
14 enum { DOWN = 0, LEFT, UP, RIGHT };
15 enum { VERTICAL, HORIZONTAL };
24 int bitmap_w, bitmap_h;
31 int max_width, max_height;
35 int draw_x, draw_y, draw_ix, draw_iy, draw_dx, draw_dy;
36 int draw_dir, draw_w, draw_h, draw_size, draw_inc;
41 async_load_state *img_loader;
46 slidescreen_init (Display *dpy, Window window)
48 struct state *st = (struct state *) calloc (1, sizeof(*st));
49 XWindowAttributes xgwa;
55 XGetWindowAttributes (st->dpy, st->window, &xgwa);
56 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
58 st->start_time = time ((time_t *) 0);
60 st->max_width = xgwa.width;
61 st->max_height = xgwa.height;
63 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
64 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
65 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
66 st->grid_size = get_integer_resource (st->dpy, "gridSize", "Integer");
67 st->pix_inc = get_integer_resource (st->dpy, "pixelIncrement", "Integer");
69 /* Don't let the grid be smaller than 5x5 */
70 while (st->grid_size > xgwa.width / 5)
72 while (st->grid_size > xgwa.height / 5)
75 if (st->delay < 0) st->delay = 0;
76 if (st->delay2 < 0) st->delay2 = 0;
77 if (st->duration < 1) st->duration = 1;
78 if (st->pix_inc < 1) st->pix_inc = 1;
79 if (st->grid_size < 1) st->grid_size = 1;
84 char *fgs = get_string_resource(st->dpy, "background", "Background");
85 char *bgs = get_string_resource(st->dpy, "foreground", "Foreground");
87 if (!XParseColor (st->dpy, xgwa.colormap, fgs, &fgc))
88 XParseColor (st->dpy, xgwa.colormap, "black", &bgc);
89 if (!XParseColor (st->dpy, xgwa.colormap, bgs, &bgc))
90 XParseColor (st->dpy, xgwa.colormap, "gray", &fgc);
92 fg_ok = XAllocColor (st->dpy, xgwa.colormap, &fgc);
93 bg_ok = XAllocColor (st->dpy, xgwa.colormap, &bgc);
95 /* If we weren't able to allocate the two colors we want from the
96 colormap (which is likely if the screen has been grabbed on an
97 8-bit SGI visual -- don't ask) then just go through the map
98 and find the closest color to the ones we wanted, and use those
99 pixels without actually allocating them.
115 unsigned long fgd = ~0;
116 unsigned long bgd = ~0;
117 int max = visual_cells (xgwa.screen, xgwa.visual);
118 XColor *all = (XColor *) calloc(sizeof (*all), max);
119 for (i = 0; i < max; i++)
121 all[i].flags = DoRed|DoGreen|DoBlue;
124 XQueryColors (st->dpy, xgwa.colormap, all, max);
125 for(i = 0; i < max; i++)
131 rd = (all[i].red >> 8) - (fgc.red >> 8);
132 gd = (all[i].green >> 8) - (fgc.green >> 8);
133 bd = (all[i].blue >> 8) - (fgc.blue >> 8);
134 if (rd < 0) rd = -rd;
135 if (gd < 0) gd = -gd;
136 if (bd < 0) bd = -bd;
137 dd = (rd << 1) + (gd << 2) + bd;
141 st->fg = all[i].pixel;
149 rd = (all[i].red >> 8) - (bgc.red >> 8);
150 gd = (all[i].green >> 8) - (bgc.green >> 8);
151 bd = (all[i].blue >> 8) - (bgc.blue >> 8);
152 if (rd < 0) rd = -rd;
153 if (gd < 0) gd = -gd;
154 if (bd < 0) bd = -bd;
155 dd = (rd << 1) + (gd << 2) + bd;
159 st->bg = all[i].pixel;
170 #endif /* !HAVE_JWXYZ */
173 gcv.foreground = st->fg;
174 gcv.function = GXcopy;
175 gcv.subwindow_mode = IncludeInferiors;
176 gcflags = GCForeground |GCFunction;
177 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
178 gcflags |= GCSubwindowMode;
179 st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
185 draw_grid (struct state *st)
190 XWindowAttributes xgwa;
192 XGetWindowAttributes (st->dpy, st->window, &xgwa);
194 border = get_integer_resource (st->dpy, "internalBorderWidth",
195 "InternalBorderWidth");
197 XGetWindowAttributes (st->dpy, st->window, &xgwa);
198 st->bitmap_w = xgwa.width;
199 st->bitmap_h = xgwa.height;
201 if (xgwa.width < 50 || xgwa.height < 50) /* tiny window */
203 int s = (xgwa.width < xgwa.height ? xgwa.width : xgwa.height);
205 st->grid_size = s / 2;
206 if (st->grid_size < 16) st->grid_size = 16;
207 if (st->bitmap_w < st->grid_size*2) st->bitmap_w = st->grid_size*2;
208 if (st->bitmap_h < st->grid_size*2) st->bitmap_h = st->grid_size*2;
211 st->grid_w = st->bitmap_w / st->grid_size;
212 st->grid_h = st->bitmap_h / st->grid_size;
213 st->hole_x = random () % st->grid_w;
214 st->hole_y = random () % st->grid_h;
215 st->xoff = (st->bitmap_w - (st->grid_w * st->grid_size)) / 2;
216 st->yoff = (st->bitmap_h - (st->grid_h * st->grid_size)) / 2;
226 int half2 = (border & 1 ? half+1 : half);
227 XSetForeground(st->dpy, st->gc, st->bg);
228 for (i = 0; i < st->bitmap_w; i += st->grid_size)
231 for (j = 0; j < st->bitmap_h; j += st->grid_size)
232 XDrawRectangle (st->dpy, d, st->gc,
233 st->xoff+i+half2, st->yoff+j+half2,
234 st->grid_size-border-1, st->grid_size-border-1);
237 XSetForeground(st->dpy, st->gc, st->fg);
238 for (i = 0; i <= st->bitmap_w; i += st->grid_size)
239 XFillRectangle (st->dpy, d, st->gc, st->xoff+i-half, st->yoff, border, st->bitmap_h);
240 for (i = 0; i <= st->bitmap_h; i += st->grid_size)
241 XFillRectangle (st->dpy, d, st->gc, st->xoff, st->yoff+i-half, st->bitmap_w, border);
246 XFillRectangle (st->dpy, d, st->gc, 0, 0, st->xoff, st->bitmap_h);
247 XFillRectangle (st->dpy, d, st->gc, st->bitmap_w - st->xoff, 0, st->xoff, st->bitmap_h);
251 XFillRectangle (st->dpy, d, st->gc, 0, 0, st->bitmap_w, st->yoff);
252 XFillRectangle (st->dpy, d, st->gc, 0, st->bitmap_h - st->yoff, st->bitmap_w, st->yoff);
258 slidescreen_draw_early (struct state *st)
260 while (st->early_i < 0)
266 /* for (early_i = 0; early_i < grid_size; early_i += pix_inc) */
269 points[0].x = st->xoff + st->grid_size * st->hole_x;
270 points[0].y = st->yoff + st->grid_size * st->hole_y;
271 points[1].x = points[0].x + st->grid_size;
272 points[1].y = points[0].y;
273 points[2].x = points[0].x;
274 points[2].y = points[0].y + st->early_i;
275 XFillPolygon (st->dpy, st->window, st->gc, points, 3, Convex, CoordModeOrigin);
277 points[1].x = points[0].x;
278 points[1].y = points[0].y + st->grid_size;
279 points[2].x = points[0].x + st->early_i;
280 points[2].y = points[0].y + st->grid_size;
281 XFillPolygon (st->dpy, st->window, st->gc, points, 3, Convex, CoordModeOrigin);
283 points[0].x = points[1].x + st->grid_size;
284 points[0].y = points[1].y;
285 points[2].x = points[0].x;
286 points[2].y = points[0].y - st->early_i;
287 XFillPolygon (st->dpy, st->window, st->gc, points, 3, Convex, CoordModeOrigin);
289 points[1].x = points[0].x;
290 points[1].y = points[0].y - st->grid_size;
291 points[2].x = points[1].x - st->early_i;
292 points[2].y = points[1].y;
293 XFillPolygon (st->dpy, st->window, st->gc, points, 3, Convex, CoordModeOrigin);
296 st->early_i += st->pix_inc;
297 if (st->early_i < st->grid_size)
300 XFillRectangle (st->dpy, st->window, st->gc,
301 st->xoff + st->grid_size * st->hole_x,
302 st->yoff + st->grid_size * st->hole_y,
303 st->grid_size, st->grid_size);
309 slidescreen_draw (Display *dpy, Window window, void *closure)
311 struct state *st = (struct state *) closure;
312 int this_delay = st->delay;
314 /* this code is a total kludge, but who cares, it works... */
316 if (st->img_loader) /* still loading */
318 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
319 if (! st->img_loader) { /* just finished */
320 st->start_time = time ((time_t *) 0);
326 if (!st->img_loader &&
327 st->start_time + st->duration < time ((time_t *) 0)) {
328 XWindowAttributes xgwa;
329 XGetWindowAttributes(st->dpy, st->window, &xgwa);
330 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
332 st->start_time = time ((time_t *) 0);
333 st->draw_initted = 0;
337 if (! st->draw_initted)
339 if (!slidescreen_draw_early (st))
341 st->draw_initted = 1;
350 if (st->draw_last == -1) st->draw_last = random () % 2;
352 /* alternate between horizontal and vertical slides */
353 /* note that draw_dir specifies the direction the _hole_ moves, not the tiles */
354 if (st->draw_last == VERTICAL) {
355 if (((st->grid_w > 1) ? st->draw_rnd = random () % (st->grid_w - 1) : 0)
357 st->draw_dx = -1; st->draw_dir = LEFT; st->hole_x -= st->draw_rnd;
359 st->draw_dx = 1; st->draw_dir = RIGHT; st->draw_rnd -= st->hole_x;
361 st->draw_dy = 0; st->draw_w = st->draw_size = st->draw_rnd + 1; st->draw_h = 1;
362 st->draw_last = HORIZONTAL;
364 if (((st->grid_h > 1) ? st->draw_rnd = random () % (st->grid_h - 1) : 0)
366 st->draw_dy = -1; st->draw_dir = UP; st->hole_y -= st->draw_rnd;
368 st->draw_dy = 1; st->draw_dir = DOWN; st->draw_rnd -= st->hole_y;
370 st->draw_dx = 0; st->draw_h = st->draw_size = st->draw_rnd + 1; st->draw_w = 1;
371 st->draw_last = VERTICAL;
374 st->draw_ix = st->draw_x = st->xoff + (st->hole_x + st->draw_dx) * st->grid_size;
375 st->draw_iy = st->draw_y = st->yoff + (st->hole_y + st->draw_dy) * st->grid_size;
376 st->draw_inc = st->pix_inc;
380 /* for (draw_i = 0; draw_i < grid_size; draw_i += draw_inc) */
382 int fx, fy, tox, toy;
383 if (st->draw_inc + st->draw_i > st->grid_size)
384 st->draw_inc = st->grid_size - st->draw_i;
385 tox = st->draw_x - st->draw_dx * st->draw_inc;
386 toy = st->draw_y - st->draw_dy * st->draw_inc;
388 fx = (st->draw_x < 0 ? 0 : st->draw_x > st->max_width ? st->max_width : st->draw_x);
389 fy = (st->draw_y < 0 ? 0 : st->draw_y > st->max_height ? st->max_height : st->draw_y);
390 tox = (tox < 0 ? 0 : tox > st->max_width ? st->max_width : tox);
391 toy = (toy < 0 ? 0 : toy > st->max_height ? st->max_height : toy);
393 XCopyArea (st->dpy, st->window, st->window, st->gc,
395 st->grid_size * st->draw_w, st->grid_size * st->draw_h,
398 st->draw_x -= st->draw_dx * st->draw_inc;
399 st->draw_y -= st->draw_dy * st->draw_inc;
400 switch (st->draw_dir)
402 case DOWN: XFillRectangle (st->dpy, st->window, st->gc,
403 st->draw_ix, st->draw_y + st->grid_size * st->draw_h, st->grid_size * st->draw_w, st->draw_iy - st->draw_y);
405 case LEFT: XFillRectangle (st->dpy, st->window, st->gc, st->draw_ix, st->draw_iy, st->draw_x - st->draw_ix, st->grid_size * st->draw_h);
407 case UP: XFillRectangle (st->dpy, st->window, st->gc, st->draw_ix, st->draw_iy, st->grid_size * st->draw_w, st->draw_y - st->draw_iy);
409 case RIGHT: XFillRectangle (st->dpy, st->window, st->gc,
410 st->draw_x + st->grid_size * st->draw_w, st->draw_iy, st->draw_ix - st->draw_x, st->grid_size * st->draw_h);
415 st->draw_i += st->draw_inc;
416 if (st->draw_i >= st->grid_size)
420 switch (st->draw_dir)
422 case DOWN: st->hole_y += st->draw_size; break;
423 case LEFT: st->hole_x--; break;
424 case UP: st->hole_y--; break;
425 case RIGHT: st->hole_x += st->draw_size; break;
428 this_delay = st->delay2;
435 slidescreen_reshape (Display *dpy, Window window, void *closure,
436 unsigned int w, unsigned int h)
438 struct state *st = (struct state *) closure;
441 if (! st->img_loader) {
442 XWindowAttributes xgwa;
443 XGetWindowAttributes (st->dpy, st->window, &xgwa);
444 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
446 st->start_time = time ((time_t *) 0);
451 slidescreen_event (Display *dpy, Window window, void *closure, XEvent *event)
453 struct state *st = (struct state *) closure;
454 if (screenhack_event_helper (dpy, window, event))
463 slidescreen_free (Display *dpy, Window window, void *closure)
465 struct state *st = (struct state *) closure;
466 XFreeGC (dpy, st->gc);
472 static const char *slidescreen_defaults [] = {
473 "*dontClearRoot: True",
476 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
480 ".background: Black",
481 ".foreground: #BEBEBE",
483 "*pixelIncrement: 10",
484 "*internalBorderWidth: 4",
489 "*ignoreRotation: True",
490 "*rotateImages: True",
495 static XrmOptionDescRec slidescreen_options [] = {
496 { "-grid-size", ".gridSize", XrmoptionSepArg, 0 },
497 { "-ibw", ".internalBorderWidth", XrmoptionSepArg, 0 },
498 { "-increment", ".pixelIncrement", XrmoptionSepArg, 0 },
499 { "-delay", ".delay", XrmoptionSepArg, 0 },
500 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
501 {"-duration", ".duration", XrmoptionSepArg, 0 },
505 XSCREENSAVER_MODULE ("SlideScreen", slidescreen)