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 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
108 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
110 /* read parameters, keep em sane */
111 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
112 if (st->delay < 1) st->delay = 1;
113 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
114 if (st->duration < 1) st->duration = 1;
115 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
116 if (st->radius < 0) st->radius = 125;
118 /* Don't let the spotlight be bigger than the window */
119 while (st->radius > xgwa.width * 0.45)
121 while (st->radius > xgwa.height * 0.45)
128 gcv.function = GXcopy;
129 gcv.subwindow_mode = IncludeInferiors;
130 gcflags = GCForeground | GCFunction;
134 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
135 gcflags |= GCSubwindowMode;
137 st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
139 st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
140 XClearWindow(st->dpy, st->window);
142 /* create buffer to reduce flicker */
143 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
146 st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
149 st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
151 XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
153 /* blank out screen */
154 XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
155 XSetWindowBackground (st->dpy, st->window, bg);
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;
184 /* create GC with white fg */
186 st->white_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
193 * perform one iteration
196 onestep (struct state *st, Bool first_p)
200 if (st->img_loader) /* still loading */
202 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
203 if (! st->img_loader) { /* just finished */
204 st->start_time = time ((time_t) 0);
209 if (!st->img_loader &&
210 st->start_time + st->duration < time ((time_t) 0)) {
211 st->img_loader = load_image_async_simple (0, st->screen, st->window,
216 #define nrnd(x) (random() % (x))
221 st->s = st->radius *4 ; /* s = width of buffer */
223 now = currentTimeInMs(st) + st->off;
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 /* copy buffer to screen (window) */
257 XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
261 /* draw a box around the buffer */
262 XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
269 spotlight_draw (Display *dpy, Window window, void *closure)
271 struct state *st = (struct state *) closure;
272 onestep(st, st->first_p);
278 spotlight_reshape (Display *dpy, Window window, void *closure,
279 unsigned int w, unsigned int h)
284 spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
290 spotlight_free (Display *dpy, Window window, void *closure)
292 struct state *st = (struct state *) closure;
293 XFreeGC (dpy, st->window_gc);
294 XFreeGC (dpy, st->buffer_gc);
295 if (st->pm) XFreePixmap (dpy, st->pm);
296 if (st->buffer) XFreePixmap (dpy, st->buffer);
303 static const char *spotlight_defaults [] = {
304 ".background: black",
305 ".foreground: white",
306 "*dontClearRoot: True",
309 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
319 static XrmOptionDescRec spotlight_options [] = {
320 { "-delay", ".delay", XrmoptionSepArg, 0 },
321 { "-duration", ".duration", XrmoptionSepArg, 0 },
322 { "-radius", ".radius", XrmoptionSepArg, 0 },
326 XSCREENSAVER_MODULE ("Spotlight", spotlight)