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"
30 #define X_PERIOD 15000.0
31 #define Y_PERIOD 12000.0
38 int sizex, sizey; /* screen size */
47 GC buffer_gc; /* draw in buffer, then flush to screen
49 int radius; /* radius of spotlight in pixels */
51 Pixmap pm; /* pixmap grabbed from screen */
52 Pixmap buffer; /* pixmap for the buffer */
54 int x, y, s; /* x & y coords of buffer (upper left corner) */
55 /* s is the width of the buffer */
57 int off; /* random offset from currentTimeInMs(), so that
58 two concurrent copies of spotlight have different
61 int oldx, oldy, max_x_speed, max_y_speed;
62 /* used to keep the new buffer position
63 over the old spotlight image to make sure
64 the old image is completely erased */
67 async_load_state *img_loader;
71 /* The path the spotlight follows around the screen is sinusoidal.
72 This function is fed to sin() to get the x & y coords */
74 currentTimeInMs(struct state *st)
76 struct timeval curTime;
77 unsigned long ret_unsigned;
78 #ifdef GETTIMEOFDAY_TWO_ARGS
79 struct timezone tz = {0,0};
80 gettimeofday(&curTime, &tz);
82 gettimeofday(&curTime);
84 ret_unsigned = curTime.tv_sec *1000U + curTime.tv_usec / 1000;
85 return (ret_unsigned <= LONG_MAX) ? ret_unsigned : -1 - (long)(ULONG_MAX - ret_unsigned);
90 spotlight_init (Display *dpy, Window window)
92 struct state *st = (struct state *) calloc (1, sizeof(*st));
94 XWindowAttributes xgwa;
105 XGetWindowAttributes (st->dpy, st->window, &xgwa);
106 st->screen = xgwa.screen;
107 st->sizex = xgwa.width;
108 st->sizey = xgwa.height;
109 cmap = xgwa.colormap;
110 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
112 /* read parameters, keep em sane */
113 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
114 if (st->delay < 1) st->delay = 1;
115 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
116 if (st->duration < 1) st->duration = 1;
117 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
118 if (st->radius < 0) st->radius = 125;
120 /* Don't let the spotlight be bigger than the window */
121 while (st->radius > xgwa.width * 0.45)
123 while (st->radius > xgwa.height * 0.45)
130 gcv.function = GXcopy;
131 gcv.subwindow_mode = IncludeInferiors;
132 gcflags = GCForeground | GCFunction;
136 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
137 gcflags |= GCSubwindowMode;
139 st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
141 st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
142 XClearWindow(st->dpy, st->window);
146 /* create buffer to reduce flicker */
147 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
150 st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
153 st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
155 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
157 /* create clip mask (so it's a circle, not a square) */
158 clip_pm = XCreatePixmap(st->dpy, st->window, st->radius*4, st->radius*4, 1);
159 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm,
161 st->start_time = time ((time_t *) 0);
164 clip_gc = XCreateGC(st->dpy, clip_pm, gcflags, &gcv);
165 XFillRectangle(st->dpy, clip_pm, clip_gc, 0, 0, st->radius*4, st->radius*4);
167 XSetForeground(st->dpy, clip_gc, 1L);
168 XFillArc(st->dpy, clip_pm, clip_gc, st->radius , st->radius,
169 st->radius*2, st->radius*2, 0, 360*64);
171 /* set buffer's clip mask to the one we just made */
172 XSetClipMask(st->dpy, st->buffer_gc, clip_pm);
174 /* free everything */
175 XFreeGC(st->dpy, clip_gc);
176 XFreePixmap(st->dpy, clip_pm);
179 st->max_x_speed = st->max_y_speed = st->radius;
183 /* blank out screen */
184 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
191 * perform one iteration
194 onestep (struct state *st, Bool first_p)
197 unsigned long now_unsigned;
199 if (st->img_loader) /* still loading */
201 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
202 if (! st->img_loader) { /* just finished */
203 st->start_time = time ((time_t *) 0);
208 if (!st->img_loader &&
209 st->start_time + st->duration < time ((time_t *) 0)) {
210 st->img_loader = load_image_async_simple (0, st->screen, st->window,
215 #define nrnd(x) (random() % (x))
220 st->s = st->radius *4 ; /* s = width of buffer */
222 now_unsigned = (unsigned long) currentTimeInMs(st) + st->off;
223 now = (now_unsigned <= LONG_MAX) ? now_unsigned : -1 - (long)(ULONG_MAX - now_unsigned);
226 st->x = ((1 + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
227 * (st->sizex - st->s/2) -st->s/4 + MINX;
228 st->y = ((1 + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
229 * (st->sizey - st->s/2) -st->s/4 + MINY;
233 /* limit change in x and y to buffer width */
234 if ( st->x < (st->oldx - st->max_x_speed) ) st->x = st->oldx - st->max_x_speed;
235 if ( st->x > (st->oldx + st->max_x_speed) ) st->x = st->oldx + st->max_x_speed;
236 if ( st->y < (st->oldy - st->max_y_speed) ) st->y = st->oldy - st->max_y_speed;
237 if ( st->y > (st->oldy + st->max_y_speed) ) st->y = st->oldy + st->max_y_speed;
242 XClearWindow (st->dpy, st->window);
243 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
244 XCopyArea(st->dpy, st->pm, st->window, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
249 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s);
251 /* copy area of screen image (pm) to buffer
253 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
254 XCopyArea(st->dpy, st->pm, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
256 if (st->first_time) {
257 /* blank out screen */
258 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
262 /* copy buffer to screen (window) */
263 XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
267 /* draw a box around the buffer */
268 XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
275 spotlight_draw (Display *dpy, Window window, void *closure)
277 struct state *st = (struct state *) closure;
278 onestep(st, st->first_p);
284 spotlight_reshape (Display *dpy, Window window, void *closure,
285 unsigned int w, unsigned int h)
290 spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
292 struct state *st = (struct state *) closure;
293 if (screenhack_event_helper (dpy, window, event))
302 spotlight_free (Display *dpy, Window window, void *closure)
304 struct state *st = (struct state *) closure;
305 XFreeGC (dpy, st->window_gc);
306 XFreeGC (dpy, st->buffer_gc);
307 if (st->pm) XFreePixmap (dpy, st->pm);
308 if (st->buffer) XFreePixmap (dpy, st->buffer);
315 static const char *spotlight_defaults [] = {
316 ".background: black",
317 ".foreground: white",
318 "*dontClearRoot: True",
321 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
329 "*ignoreRotation: True",
330 "*rotateImages: True",
335 static XrmOptionDescRec spotlight_options [] = {
336 { "-delay", ".delay", XrmoptionSepArg, 0 },
337 { "-duration", ".duration", XrmoptionSepArg, 0 },
338 { "-radius", ".radius", XrmoptionSepArg, 0 },
342 XSCREENSAVER_MODULE ("Spotlight", spotlight)