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);
141 /* create buffer to reduce flicker */
142 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
145 st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
148 st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
150 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
152 /* blank out screen */
153 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
154 XSetWindowBackground (st->dpy, st->window, bg);
156 /* create clip mask (so it's a circle, not a square) */
157 clip_pm = XCreatePixmap(st->dpy, st->window, st->radius*4, st->radius*4, 1);
158 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm,
160 st->start_time = time ((time_t) 0);
163 clip_gc = XCreateGC(st->dpy, clip_pm, gcflags, &gcv);
164 XFillRectangle(st->dpy, clip_pm, clip_gc, 0, 0, st->radius*4, st->radius*4);
166 XSetForeground(st->dpy, clip_gc, 1L);
167 XFillArc(st->dpy, clip_pm, clip_gc, st->radius , st->radius,
168 st->radius*2, st->radius*2, 0, 360*64);
170 /* set buffer's clip mask to the one we just made */
171 XSetClipMask(st->dpy, st->buffer_gc, clip_pm);
173 /* free everything */
174 XFreeGC(st->dpy, clip_gc);
175 XFreePixmap(st->dpy, clip_pm);
178 st->max_x_speed = st->max_y_speed = st->radius;
183 /* create GC with white fg */
185 st->white_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
192 * perform one iteration
195 onestep (struct state *st, Bool first_p)
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 = currentTimeInMs(st) + st->off;
225 st->x = ((1 + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
226 * (st->sizex - st->s/2) -st->s/4 + MINX;
227 st->y = ((1 + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
228 * (st->sizey - st->s/2) -st->s/4 + MINY;
232 /* limit change in x and y to buffer width */
233 if ( st->x < (st->oldx - st->max_x_speed) ) st->x = st->oldx - st->max_x_speed;
234 if ( st->x > (st->oldx + st->max_x_speed) ) st->x = st->oldx + st->max_x_speed;
235 if ( st->y < (st->oldy - st->max_y_speed) ) st->y = st->oldy - st->max_y_speed;
236 if ( st->y > (st->oldy + st->max_y_speed) ) st->y = st->oldy + st->max_y_speed;
241 XClearWindow (st->dpy, st->window);
242 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
243 XCopyArea(st->dpy, st->pm, st->window, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
248 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s);
250 /* copy area of screen image (pm) to buffer
252 XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
253 XCopyArea(st->dpy, st->pm, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
255 /* copy buffer to screen (window) */
256 XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
260 /* draw a box around the buffer */
261 XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
268 spotlight_draw (Display *dpy, Window window, void *closure)
270 struct state *st = (struct state *) closure;
271 onestep(st, st->first_p);
277 spotlight_reshape (Display *dpy, Window window, void *closure,
278 unsigned int w, unsigned int h)
283 spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
289 spotlight_free (Display *dpy, Window window, void *closure)
291 struct state *st = (struct state *) closure;
292 XFreeGC (dpy, st->window_gc);
293 XFreeGC (dpy, st->buffer_gc);
294 if (st->pm) XFreePixmap (dpy, st->pm);
295 if (st->buffer) XFreePixmap (dpy, st->buffer);
302 static const char *spotlight_defaults [] = {
303 ".background: black",
304 ".foreground: white",
305 "*dontClearRoot: True",
308 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
318 static XrmOptionDescRec spotlight_options [] = {
319 { "-delay", ".delay", XrmoptionSepArg, 0 },
320 { "-duration", ".duration", XrmoptionSepArg, 0 },
321 { "-radius", ".radius", XrmoptionSepArg, 0 },
325 XSCREENSAVER_MODULE ("Spotlight", spotlight)