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
25 #include "screenhack.h"
29 #define X_PERIOD 15000.0
30 #define Y_PERIOD 12000.0
37 int sizex, sizey; /* screen size */
46 GC buffer_gc; /* draw in buffer, then flush to screen
48 int radius; /* radius of spotlight in pixels */
50 Pixmap pm; /* pixmap grabbed from screen */
51 Pixmap buffer; /* pixmap for the buffer */
53 int x, y, s; /* x & y coords of buffer (upper left corner) */
54 /* s is the width of the buffer */
56 int off; /* random offset from currentTimeInMs(), so that
57 two concurrent copies of spotlight have different
60 int oldx, oldy, max_x_speed, max_y_speed;
61 /* used to keep the new buffer position
62 over the old spotlight image to make sure
63 the old image is completely erased */
66 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 #ifdef GETTIMEOFDAY_TWO_ARGS
77 struct timezone tz = {0,0};
78 gettimeofday(&curTime, &tz);
80 gettimeofday(&curTime);
82 return curTime.tv_sec*1000 + curTime.tv_usec/1000.0;
87 spotlight_init (Display *dpy, Window window)
89 struct state *st = (struct state *) calloc (1, sizeof(*st));
91 XWindowAttributes xgwa;
102 XGetWindowAttributes (st->dpy, st->window, &xgwa);
103 st->screen = xgwa.screen;
104 st->sizex = xgwa.width;
105 st->sizey = xgwa.height;
106 cmap = xgwa.colormap;
107 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
109 /* read parameters, keep em sane */
110 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
111 if (st->delay < 1) st->delay = 1;
112 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
113 if (st->duration < 1) st->duration = 1;
114 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
115 if (st->radius < 0) st->radius = 125;
117 /* Don't let the spotlight be bigger than the window */
118 while (st->radius > xgwa.width * 0.45)
120 while (st->radius > xgwa.height * 0.45)
127 gcv.function = GXcopy;
128 gcv.subwindow_mode = IncludeInferiors;
129 gcflags = GCForeground | GCFunction;
133 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
134 gcflags |= GCSubwindowMode;
136 st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
138 st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
139 XClearWindow(st->dpy, st->window);
143 /* create buffer to reduce flicker */
144 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
147 st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
150 st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
152 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
154 /* create clip mask (so it's a circle, not a square) */
155 clip_pm = XCreatePixmap(st->dpy, st->window, st->radius*4, st->radius*4, 1);
156 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm,
158 st->start_time = time ((time_t) 0);
161 clip_gc = XCreateGC(st->dpy, clip_pm, gcflags, &gcv);
162 XFillRectangle(st->dpy, clip_pm, clip_gc, 0, 0, st->radius*4, st->radius*4);
164 XSetForeground(st->dpy, clip_gc, 1L);
165 XFillArc(st->dpy, clip_pm, clip_gc, st->radius , st->radius,
166 st->radius*2, st->radius*2, 0, 360*64);
168 /* set buffer's clip mask to the one we just made */
169 XSetClipMask(st->dpy, st->buffer_gc, clip_pm);
171 /* free everything */
172 XFreeGC(st->dpy, clip_gc);
173 XFreePixmap(st->dpy, clip_pm);
176 st->max_x_speed = st->max_y_speed = st->radius;
181 /* create GC with white fg */
183 st->white_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
186 /* blank out screen */
187 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
194 * perform one iteration
197 onestep (struct state *st, Bool first_p)
201 if (st->img_loader) /* still loading */
203 st->img_loader = load_image_async_simple (st->img_loader, 0, 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,
217 #define nrnd(x) (random() % (x))
222 st->s = st->radius *4 ; /* s = width of buffer */
224 now = currentTimeInMs(st) + st->off;
227 st->x = ((1 + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
228 * (st->sizex - st->s/2) -st->s/4 + MINX;
229 st->y = ((1 + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
230 * (st->sizey - st->s/2) -st->s/4 + MINY;
234 /* limit change in x and y to buffer width */
235 if ( st->x < (st->oldx - st->max_x_speed) ) st->x = st->oldx - st->max_x_speed;
236 if ( st->x > (st->oldx + st->max_x_speed) ) st->x = st->oldx + st->max_x_speed;
237 if ( st->y < (st->oldy - st->max_y_speed) ) st->y = st->oldy - st->max_y_speed;
238 if ( st->y > (st->oldy + st->max_y_speed) ) st->y = st->oldy + st->max_y_speed;
243 XClearWindow (st->dpy, st->window);
244 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
245 XCopyArea(st->dpy, st->pm, st->window, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
250 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s);
252 /* copy area of screen image (pm) to buffer
254 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
255 XCopyArea(st->dpy, st->pm, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
257 if (st->first_time) {
258 /* blank out screen */
259 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
263 /* copy buffer to screen (window) */
264 XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
268 /* draw a box around the buffer */
269 XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
276 spotlight_draw (Display *dpy, Window window, void *closure)
278 struct state *st = (struct state *) closure;
279 onestep(st, st->first_p);
285 spotlight_reshape (Display *dpy, Window window, void *closure,
286 unsigned int w, unsigned int h)
291 spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
293 struct state *st = (struct state *) closure;
294 if (screenhack_event_helper (dpy, window, event))
303 spotlight_free (Display *dpy, Window window, void *closure)
305 struct state *st = (struct state *) closure;
306 XFreeGC (dpy, st->window_gc);
307 XFreeGC (dpy, st->buffer_gc);
308 if (st->pm) XFreePixmap (dpy, st->pm);
309 if (st->buffer) XFreePixmap (dpy, st->buffer);
316 static const char *spotlight_defaults [] = {
317 ".background: black",
318 ".foreground: white",
319 "*dontClearRoot: True",
322 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
330 "*ignoreRotation: True",
331 "*rotateImages: True",
336 static XrmOptionDescRec spotlight_options [] = {
337 { "-delay", ".delay", XrmoptionSepArg, 0 },
338 { "-duration", ".duration", XrmoptionSepArg, 0 },
339 { "-radius", ".radius", XrmoptionSepArg, 0 },
343 XSCREENSAVER_MODULE ("Spotlight", spotlight)