2 * spotlight - an xscreensaver module
3 * Copyright (c) 1999, 2001 Rick Schultz <rick.schultz@gmail.com>
5 * loosely based on the BackSpace module "StefView" by Darcy Brockbank
8 /* modified from a module from the xscreensaver distribution */
11 * xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
13 * Permission to use, copy, modify, distribute, and sell this software and its
14 * documentation for any purpose is hereby granted without fee, provided that
15 * the above copyright notice appear in all copies and that both that
16 * copyright notice and this permission notice appear in supporting
17 * documentation. No representations are made about the suitability of this
18 * software for any purpose. It is provided "as is" without express or
26 #include "screenhack.h"
28 #define X_PERIOD 15000.0
29 #define Y_PERIOD 12000.0
36 int sizex, sizey; /* screen size */
45 GC buffer_gc; /* draw in buffer, then flush to screen
47 int radius; /* radius of spotlight in pixels */
49 Pixmap pm; /* pixmap grabbed from screen */
50 Pixmap buffer; /* pixmap for the buffer */
52 int x, y, s; /* x & y coords of buffer (upper left corner) */
53 /* s is the width of the buffer */
55 int off; /* random offset from currentTimeInMs(), so that
56 two concurrent copies of spotlight have different
59 int oldx, oldy, max_x_speed, max_y_speed;
60 /* used to keep the new buffer position
61 over the old spotlight image to make sure
62 the old image is completely erased */
65 async_load_state *img_loader;
70 /* The path the spotlight follows around the screen is sinusoidal.
71 This function is fed to sin() to get the x & y coords */
73 currentTimeInMs(struct state *st)
75 struct timeval curTime;
76 unsigned long ret_unsigned;
77 #ifdef GETTIMEOFDAY_TWO_ARGS
78 struct timezone tz = {0,0};
79 gettimeofday(&curTime, &tz);
81 gettimeofday(&curTime);
83 ret_unsigned = curTime.tv_sec *1000U + curTime.tv_usec / 1000;
84 return (ret_unsigned <= LONG_MAX) ? ret_unsigned : -1 - (long)(ULONG_MAX - ret_unsigned);
89 spotlight_init (Display *dpy, Window window)
91 struct state *st = (struct state *) calloc (1, sizeof(*st));
93 XWindowAttributes xgwa;
104 XGetWindowAttributes (st->dpy, st->window, &xgwa);
105 st->screen = xgwa.screen;
106 st->sizex = xgwa.width;
107 st->sizey = xgwa.height;
108 cmap = xgwa.colormap;
109 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
111 /* read parameters, keep em sane */
112 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
113 if (st->delay < 1) st->delay = 1;
114 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
115 if (st->duration < 1) st->duration = 1;
116 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
117 if (st->radius < 0) st->radius = 125;
119 if (xgwa.width > 2560) st->radius *= 2; /* Retina displays */
121 /* Don't let the spotlight be bigger than the window */
122 while (st->radius > xgwa.width * 0.45)
124 while (st->radius > xgwa.height * 0.45)
131 gcv.function = GXcopy;
132 gcv.subwindow_mode = IncludeInferiors;
133 gcflags = GCForeground | GCFunction;
137 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
138 gcflags |= GCSubwindowMode;
140 st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
142 st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
143 XClearWindow(st->dpy, st->window);
147 /* create buffer to reduce flicker */
148 #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
151 st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
154 st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
156 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
158 /* create clip mask (so it's a circle, not a square) */
159 clip_pm = XCreatePixmap(st->dpy, st->window, st->radius*4, st->radius*4, 1);
160 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm,
162 st->start_time = time ((time_t *) 0);
165 clip_gc = XCreateGC(st->dpy, clip_pm, gcflags, &gcv);
166 XFillRectangle(st->dpy, clip_pm, clip_gc, 0, 0, st->radius*4, st->radius*4);
168 XSetForeground(st->dpy, clip_gc, 1L);
169 XFillArc(st->dpy, clip_pm, clip_gc, st->radius , st->radius,
170 st->radius*2, st->radius*2, 0, 360*64);
172 /* set buffer's clip mask to the one we just made */
173 XSetClipMask(st->dpy, st->buffer_gc, clip_pm);
175 /* free everything */
176 XFreeGC(st->dpy, clip_gc);
177 XFreePixmap(st->dpy, clip_pm);
180 st->max_x_speed = st->max_y_speed = st->radius;
184 /* blank out screen */
185 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
192 * perform one iteration
195 onestep (struct state *st, Bool first_p)
198 unsigned long now_unsigned;
200 if (st->img_loader) /* still loading */
202 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0,
204 if (! st->img_loader) { /* just finished */
205 st->start_time = time ((time_t *) 0);
210 if (!st->img_loader &&
211 st->start_time + st->duration < time ((time_t *) 0)) {
212 st->img_loader = load_image_async_simple (0, st->screen, st->window,
213 st->pm, 0, &st->geom);
217 #define nrnd(x) (random() % (x))
222 st->s = st->radius *4 ; /* s = width of buffer */
224 now_unsigned = (unsigned long) currentTimeInMs(st) + st->off;
225 now = (now_unsigned <= LONG_MAX) ? now_unsigned : -1 - (long)(ULONG_MAX - now_unsigned);
229 ((1 + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
230 * (st->geom.width - st->s/2) -st->s/4;
232 ((1 + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
233 * (st->geom.height - st->s/2) -st->s/4;
237 /* limit change in x and y to buffer width */
238 if ( st->x < (st->oldx - st->max_x_speed) ) st->x = st->oldx - st->max_x_speed;
239 if ( st->x > (st->oldx + st->max_x_speed) ) st->x = st->oldx + st->max_x_speed;
240 if ( st->y < (st->oldy - st->max_y_speed) ) st->y = st->oldy - st->max_y_speed;
241 if ( st->y > (st->oldy + st->max_y_speed) ) st->y = st->oldy + st->max_y_speed;
246 XClearWindow (st->dpy, st->window);
247 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
248 XCopyArea(st->dpy, st->pm, st->window, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
253 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s);
255 /* copy area of screen image (pm) to buffer
257 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
258 XCopyArea(st->dpy, st->pm, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
260 if (st->first_time) {
261 /* blank out screen */
262 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
266 /* copy buffer to screen (window) */
267 XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
270 XSetForeground (st->dpy, st->window_gc,
271 WhitePixel (st->dpy, DefaultScreen (st->dpy)));
272 XDrawRectangle(st->dpy, st->window, st->window_gc,
273 st->geom.x, st->geom.y, st->geom.width, st->geom.height);
278 /* draw a box around the buffer */
279 XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
286 spotlight_draw (Display *dpy, Window window, void *closure)
288 struct state *st = (struct state *) closure;
289 onestep(st, st->first_p);
295 spotlight_reshape (Display *dpy, Window window, void *closure,
296 unsigned int w, unsigned int h)
301 spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
303 struct state *st = (struct state *) closure;
304 if (screenhack_event_helper (dpy, window, event))
313 spotlight_free (Display *dpy, Window window, void *closure)
315 struct state *st = (struct state *) closure;
316 XFreeGC (dpy, st->window_gc);
317 XFreeGC (dpy, st->buffer_gc);
318 if (st->pm) XFreePixmap (dpy, st->pm);
319 if (st->buffer) XFreePixmap (dpy, st->buffer);
326 static const char *spotlight_defaults [] = {
327 ".background: black",
328 ".foreground: white",
329 "*dontClearRoot: True",
332 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
340 "*ignoreRotation: True",
341 "*rotateImages: True",
346 static XrmOptionDescRec spotlight_options [] = {
347 { "-delay", ".delay", XrmoptionSepArg, 0 },
348 { "-duration", ".duration", XrmoptionSepArg, 0 },
349 { "-radius", ".radius", XrmoptionSepArg, 0 },
353 XSCREENSAVER_MODULE ("Spotlight", spotlight)