2616b849a9662c0561607849bb78f8379fe85fcf
[xscreensaver] / hacks / spotlight.c
1 /*
2  * spotlight - an xscreensaver module
3  * Copyright (c) 1999, 2001 Rick Schultz <rick@skapunx.net>
4  *
5  * loosely based on the BackSpace module "StefView" by Darcy Brockbank
6  */
7
8 /* modified from a module from the xscreensaver distribution */
9
10 /*
11  * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
12  * Jamie Zawinski <jwz@jwz.org>
13  *
14  * Permission to use, copy, modify, distribute, and sell this software and its
15  * documentation for any purpose is hereby granted without fee, provided that
16  * the above copyright notice appear in all copies and that both that
17  * copyright notice and this permission notice appear in supporting
18  * documentation.  No representations are made about the suitability of this
19  * software for any purpose.  It is provided "as is" without express or 
20  * implied warranty.
21  */
22
23 /* #define DEBUG */
24 #include <math.h>
25 #include "screenhack.h"
26 #include <X11/Xutil.h>
27 #include <sys/time.h>
28
29 #define MINX 0.0
30 #define MINY 0.0
31 #define X_PERIOD 15000.0
32 #define Y_PERIOD 12000.0
33
34 static int sizex, sizey; /* screen size */
35 static int delay;        /* in case it's too fast... */
36 static GC window_gc;
37 #ifdef DEBUG
38 static GC white_gc;
39 #endif
40 static GC buffer_gc;     /* draw in buffer, then flush to screen
41                             to avoid flicker */
42 static int radius;       /* radius of spotlight in pixels */
43
44 static Pixmap pm;        /* pixmap grabbed from screen */
45 static Pixmap clip_pm;   /* pixmap for clipping (spotlight shape) */
46 static Pixmap buffer;    /* pixmap for the buffer */
47
48 static GC clip_gc;       /* GC for the clip pixmap */
49
50 static int x, y, s;      /* x & y coords of buffer (upper left corner) */
51                          /* s is the width of the buffer */
52
53 static int off = 0;     /* random offset from currentTimeInMs(), so that
54                            two concurrent copies of spotlight have different
55                            behavior. */
56
57 static int oldx, oldy, max_x_speed, max_y_speed;
58                          /* used to keep the new buffer position
59                             over the old spotlight image to make sure 
60                             the old image is completely erased */
61
62 /* The path the spotlight follows around the screen is sinusoidal.
63    This function is fed to sin() to get the x & y coords */
64 static long
65 currentTimeInMs(void)
66 {
67   struct timeval curTime;
68 #ifdef GETTIMEOFDAY_TWO_ARGS
69   struct timezone tz = {0,0};
70   gettimeofday(&curTime, &tz);
71 #else
72   gettimeofday(&curTime);
73 #endif
74   return curTime.tv_sec*1000 + curTime.tv_usec/1000.0;
75 }
76
77
78 static void
79 init_hack (Display *dpy, Window window)
80 {
81   XGCValues gcv;
82   XWindowAttributes xgwa;
83   long gcflags;
84   Colormap cmap;
85   unsigned long fg, bg;
86
87   XGetWindowAttributes (dpy, window, &xgwa);
88   sizex = xgwa.width;
89   sizey = xgwa.height;
90   cmap = xgwa.colormap;
91   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
92   bg = get_pixel_resource ("background", "Background", dpy, cmap);
93
94   /* read parameters, keep em sane */
95   delay = get_integer_resource ("delay", "Integer");
96   if (delay < 1) delay = 1;
97   radius = get_integer_resource ("radius", "Integer");
98   if (radius < 0) radius = 125;
99
100   /* Don't let the spotlight be bigger than 1/4 of the window */
101   if (radius > xgwa.width  / 4) radius = xgwa.width  / 4;
102   if (radius > xgwa.height / 4) radius = xgwa.height / 4;
103
104   /* do the dance */
105   gcv.function = GXcopy;
106   gcv.subwindow_mode = IncludeInferiors;
107   gcflags = GCForeground | GCFunction;
108   gcv.foreground = bg;
109
110 #ifdef NOPE
111   if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
112     gcflags |= GCSubwindowMode;
113 #endif
114   window_gc = XCreateGC(dpy, window, gcflags, &gcv);
115
116   /* grab screen to pixmap */
117   pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
118   load_random_image (xgwa.screen, window, pm, NULL);
119   XClearWindow(dpy, window);
120   XFlush (dpy);
121
122   /* create buffer to reduce flicker */
123   buffer = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
124   buffer_gc = XCreateGC(dpy, buffer, gcflags, &gcv);
125   XFillRectangle(dpy, buffer, buffer_gc, 0, 0, sizex, sizey);
126
127   /* blank out screen */
128   XFillRectangle(dpy, window, window_gc, 0, 0, sizex, sizey);
129   XSetWindowBackground (dpy, window, bg);
130
131   /* create clip mask (so it's a circle, not a square) */
132   clip_pm = XCreatePixmap(dpy, window, radius*4, radius*4, 1);
133
134   gcv.foreground = 0L;
135   clip_gc = XCreateGC(dpy, clip_pm, gcflags, &gcv);
136   XFillRectangle(dpy, clip_pm, clip_gc, 0, 0, radius*4, radius*4);
137
138   XSetForeground(dpy, clip_gc, 1L);
139   XFillArc(dpy, clip_pm, clip_gc, radius , radius,
140            radius*2, radius*2, 0, 360*64);
141
142   /* set buffer's clip mask to the one we just made */
143   XSetClipMask(dpy, buffer_gc, clip_pm);
144
145   /* free everything */
146   XFreeGC(dpy, clip_gc);
147   XFreePixmap(dpy, clip_pm);
148
149   /* avoid remants */
150   max_x_speed = max_y_speed = radius;
151   
152   off = random();
153
154 #ifdef DEBUG
155   /* create GC with white fg */
156   gcv.foreground = fg;
157   white_gc = XCreateGC(dpy, window, gcflags, &gcv);
158 #endif
159 }
160
161
162 /*
163  * perform one iteration
164  */
165 static void
166 onestep (Display *dpy, Window window, Bool first_p)
167 {
168     long now;
169
170     /* clear buffer */
171     XFillRectangle(dpy, buffer, buffer_gc, x, y, s, s);
172
173     
174 #define nrnd(x) (random() % (x))
175
176     oldx = x;
177     oldy = y;
178
179     s = radius *4 ;   /* s = width of buffer */
180
181     now = currentTimeInMs() + off;
182
183     /* find new x,y */
184     x = ((1 + sin(((float)now) / X_PERIOD * 2. * M_PI))/2.0) 
185       * (sizex - s/2) -s/4  + MINX;
186     y = ((1 + sin(((float)now) / Y_PERIOD * 2. * M_PI))/2.0) 
187       * (sizey - s/2) -s/4  + MINY;
188     
189     if (!first_p)
190       {
191         /* limit change in x and y to buffer width */
192         if ( x < (oldx - max_x_speed) ) x = oldx - max_x_speed;
193         if ( x > (oldx + max_x_speed) ) x = oldx + max_x_speed;
194         if ( y < (oldy - max_y_speed) ) y = oldy - max_y_speed;
195         if ( y > (oldy + max_y_speed) ) y = oldy + max_y_speed;
196       }
197
198     /* copy area of screen image (pm) to buffer
199        Clip to a circle */
200     XSetClipOrigin(dpy, buffer_gc, x,y);
201     XCopyArea(dpy, pm, buffer, buffer_gc, x, y, s, s, x, y);
202     /* copy buffer to screen (window) */
203     XCopyArea(dpy, buffer, window, window_gc, x , y, s, s, x, y);
204
205 #ifdef DEBUG
206     /* draw a box around the buffer */
207     XDrawLine(dpy, window, white_gc, x, y, x+s, y);
208     XDrawLine(dpy, window, white_gc, x, y, x, y+s);
209     XDrawLine(dpy, window, white_gc, x+s, y, x+s, y+s);
210     XDrawLine(dpy, window, white_gc, x, y+s, x+s, y+s);
211 #endif
212
213 }
214
215 \f
216 char *progclass = "Spotlight";
217
218 char *defaults [] = {
219   ".background:                 black",
220   ".foreground:                 white",
221   "*dontClearRoot:              True",
222
223 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
224   "*visualID:                   Best",
225 #endif
226
227   "*delay:                      10000",
228   "*radius:                     125",
229   0
230 };
231
232 XrmOptionDescRec options [] = {
233   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
234   { "-radius",          ".radius",              XrmoptionSepArg, 0 },
235   { 0, 0, 0, 0 }
236 };
237
238 void
239 screenhack (Display *dpy, Window window)
240 {
241   Bool first_p = True;
242   init_hack (dpy, window);
243   while (1) {
244     onestep(dpy, window, first_p);
245     first_p = False;
246     XSync(dpy, False);
247     if (delay) usleep (delay);
248     screenhack_handle_events (dpy);
249   }
250 }
251