eebe5f3775ec749b89c534ab8947726a200195f6
[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 oldx, oldy, max_x_speed, max_y_speed;
54                          /* used to keep the new buffer position
55                             over the old spotlight image to make sure 
56                             the old image is completely erased */
57
58 /* The path the spotlight follows around the screen is sinusoidal.
59    This function is fed to sin() to get the x & y coords */
60 static long
61 currentTimeInMs(void)
62 {
63   struct timeval curTime;
64 #ifdef GETTIMEOFDAY_TWO_ARGS
65   struct timezone tz = {0,0};
66   gettimeofday(&curTime, &tz);
67 #else
68   gettimeofday(&curTime);
69 #endif
70   return curTime.tv_sec*1000 + curTime.tv_usec/1000.0;
71 }
72
73
74 static void
75 init_hack (Display *dpy, Window window)
76 {
77   XGCValues gcv;
78   XWindowAttributes xgwa;
79   long gcflags;
80   Colormap cmap;
81   unsigned long fg, bg;
82
83   XGetWindowAttributes (dpy, window, &xgwa);
84   sizex = xgwa.width;
85   sizey = xgwa.height;
86   cmap = xgwa.colormap;
87   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
88   bg = get_pixel_resource ("background", "Background", dpy, cmap);
89
90   /* read parameters, keep em sane */
91   delay = get_integer_resource ("delay", "Integer");
92   if (delay < 1) delay = 1;
93   radius = get_integer_resource ("radius", "Integer");
94   if (radius < 0) radius = 125;
95
96   /* Don't let the spotlight be bigger than 1/4 of the window */
97   if (radius > xgwa.width  / 4) radius = xgwa.width  / 4;
98   if (radius > xgwa.height / 4) radius = xgwa.height / 4;
99
100   /* do the dance */
101   gcv.function = GXcopy;
102   gcv.subwindow_mode = IncludeInferiors;
103   gcflags = GCForeground | GCFunction;
104   gcv.foreground = bg;
105
106 #ifdef NOPE
107   if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
108     gcflags |= GCSubwindowMode;
109 #endif
110   window_gc = XCreateGC(dpy, window, gcflags, &gcv);
111
112   /* grab screen to pixmap */
113   pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
114   load_random_image (xgwa.screen, window, pm, NULL);
115   XClearWindow(dpy, window);
116   XFlush (dpy);
117
118   /* create buffer to reduce flicker */
119   buffer = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
120   buffer_gc = XCreateGC(dpy, buffer, gcflags, &gcv);
121   XFillRectangle(dpy, buffer, buffer_gc, 0, 0, sizex, sizey);
122
123   /* blank out screen */
124   XFillRectangle(dpy, window, window_gc, 0, 0, sizex, sizey);
125   XSetWindowBackground (dpy, window, bg);
126
127   /* create clip mask (so it's a circle, not a square) */
128   clip_pm = XCreatePixmap(dpy, window, radius*4, radius*4, 1);
129
130   gcv.foreground = 0L;
131   clip_gc = XCreateGC(dpy, clip_pm, gcflags, &gcv);
132   XFillRectangle(dpy, clip_pm, clip_gc, 0, 0, radius*4, radius*4);
133
134   XSetForeground(dpy, clip_gc, 1L);
135   XFillArc(dpy, clip_pm, clip_gc, radius , radius,
136            radius*2, radius*2, 0, 360*64);
137
138   /* set buffer's clip mask to the one we just made */
139   XSetClipMask(dpy, buffer_gc, clip_pm);
140
141   /* free everything */
142   XFreeGC(dpy, clip_gc);
143   XFreePixmap(dpy, clip_pm);
144
145   /* avoid remants */
146   max_x_speed = max_y_speed = radius;
147
148 #ifdef DEBUG
149   /* create GC with white fg */
150   gcv.foreground = fg;
151   white_gc = XCreateGC(dpy, window, gcflags, &gcv);
152 #endif
153   
154   /* initialize x and y to avoid initial `jump' across screen */
155   x = ((1 + sin(((float)currentTimeInMs()) / X_PERIOD * 2. * M_PI))/2.0) 
156     * (sizex - s/2) -s/4  + MINX;
157   y = ((1 + sin(((float)currentTimeInMs()) / Y_PERIOD * 2. * M_PI))/2.0) 
158     * (sizey - s/2) -s/4  + MINY;
159
160 }
161
162
163 /*
164  * perform one iteration
165  */
166 static void
167 onestep (Display *dpy, Window window)
168 {
169     long now;
170
171     /* clear buffer */
172     XFillRectangle(dpy, buffer, buffer_gc, x, y, s, s);
173
174     
175 #define nrnd(x) (random() % (x))
176
177     oldx = x;
178     oldy = y;
179
180     s = radius *4 ;   /* s = width of buffer */
181
182     now = currentTimeInMs();
183
184     /* find new x,y */
185     x = ((1 + sin(((float)now) / X_PERIOD * 2. * M_PI))/2.0) 
186       * (sizex - s/2) -s/4  + MINX;
187     y = ((1 + sin(((float)now) / Y_PERIOD * 2. * M_PI))/2.0) 
188       * (sizey - s/2) -s/4  + MINY;
189     
190     /* limit change in x and y to buffer width */
191     if ( x < (oldx - max_x_speed) ) x = oldx - max_x_speed;
192     if ( x > (oldx + max_x_speed) ) x = oldx + max_x_speed;
193     if ( y < (oldy - max_y_speed) ) y = oldy - max_y_speed;
194     if ( y > (oldy + max_y_speed) ) y = oldy + max_y_speed;
195
196     /* copy area of screen image (pm) to buffer
197        Clip to a circle */
198     XSetClipOrigin(dpy, buffer_gc, x,y);
199     XCopyArea(dpy, pm, buffer, buffer_gc, x, y, s, s, x, y);
200     /* copy buffer to screen (window) */
201     XCopyArea(dpy, buffer, window, window_gc, x , y, s, s, x, y);
202
203 #ifdef DEBUG
204     /* draw a box around the buffer */
205     XDrawLine(dpy, window, white_gc, x, y, x+s, y);
206     XDrawLine(dpy, window, white_gc, x, y, x, y+s);
207     XDrawLine(dpy, window, white_gc, x+s, y, x+s, y+s);
208     XDrawLine(dpy, window, white_gc, x, y+s, x+s, y+s);
209 #endif
210
211 }
212
213 \f
214 char *progclass = "Spotlight";
215
216 char *defaults [] = {
217   ".background:                 black",
218   ".foreground:                 white",
219   "*dontClearRoot:              True",
220
221 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
222   "*visualID:                   Best",
223 #endif
224
225   "*delay:                      10000",
226   "*radius:                     125",
227   0
228 };
229
230 XrmOptionDescRec options [] = {
231   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
232   { "-radius",          ".radius",              XrmoptionSepArg, 0 },
233   { 0, 0, 0, 0 }
234 };
235
236 void
237 screenhack (Display *dpy, Window window)
238 {
239   init_hack (dpy, window);
240   while (1) {
241     onestep(dpy, window);
242     XSync(dpy, False);
243     if (delay) usleep (delay);
244     screenhack_handle_events (dpy);
245   }
246 }
247