From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / slip.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* slip --- lots of slipping blits */
3
4 #if 0
5 static const char sccsid[] = "@(#)slip.c        5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1992 by Scott Draves <spot@cs.cmu.edu>
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * 01-Nov-2000: Allocation checks
24  * 10-May-1997: Jamie Zawinski <jwz@jwz.org> compatible with xscreensaver
25  * 01-Dec-1995: Patched for VMS <joukj@hrem.stm.tudelft.nl>
26  */
27
28 #ifdef STANDALONE
29 # define MODE_slip
30 # define DEFAULTS       "*delay: 50000 \n" \
31                                         "*count: 35 \n" \
32                                         "*cycles: 50 \n" \
33                                         "*ncolors: 200 \n" \
34                                         "*fpsSolid:     true \n" \
35                                         "*ignoreRotation: True \n" \
36
37 # define refresh_slip 0
38 # define release_slip 0
39 # include "xlockmore.h"         /* in xscreensaver distribution */
40 #else /* STANDALONE */
41 # include "xlock.h"             /* in xlockmore distribution */
42 #endif /* STANDALONE */
43
44 #ifdef MODE_slip
45
46 ENTRYPOINT ModeSpecOpt slip_opts =
47 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
48
49 #ifdef USE_MODULES
50 ModStruct   slip_description =
51 {"slip", "init_slip", "draw_slip", (char *) NULL,
52  "init_slip", "init_slip", (char *) NULL, &slip_opts,
53  50000, 35, 50, 1, 64, 1.0, "",
54  "Shows slipping blits", 0, NULL};
55
56 #endif
57
58 typedef struct {
59         int         width, height;
60         int         nblits_remaining;
61         int         blit_width, blit_height;
62         int         mode;
63         int         first_time;
64         int         backwards;
65         short       lasthalf;
66         int         stage;
67         unsigned long r;
68     Bool image_loading_p;
69 } slipstruct;
70 static slipstruct *slips = (slipstruct *) NULL;
71
72 static short
73 halfrandom(slipstruct *sp, int mv)
74 {
75         unsigned long r;
76
77         if (sp->lasthalf) {
78                 r = sp->lasthalf;
79                 sp->lasthalf = 0;
80         } else {
81                 r = LRAND();
82                 sp->lasthalf = (short) (r >> 16);
83         }
84         return r % mv;
85 }
86
87 static int
88 erandom(slipstruct *sp, int mv)
89 {
90         int         res;
91
92         if (0 == sp->stage) {
93                 sp->r = LRAND();
94                 sp->stage = 7;
95         }
96         res = (int) (sp->r & 0xf);
97         sp->r = sp->r >> 4;
98         sp->stage--;
99         if (res & 8)
100                 return res & mv;
101         else
102                 return -(res & mv);
103 }
104
105 #ifdef STANDALONE
106 static void
107 image_loaded_cb (Screen *screen, Window w, Drawable d,
108                  const char *name, XRectangle *geom,
109                  void *closure)
110 {
111   ModeInfo *mi = (ModeInfo *) closure;
112   slipstruct *sp = &slips[MI_SCREEN(mi)];
113   Display *dpy = DisplayOfScreen (screen);
114   XCopyArea (dpy, d, w, mi->gc, 0, 0,
115              sp->width, sp->height, 0, 0);
116   XFreePixmap (dpy, d);
117   sp->image_loading_p = False;
118 }
119 #endif /* STANDALONE */
120
121 static void
122 prepare_screen(ModeInfo * mi, slipstruct * sp)
123 {
124
125         Display    *display = MI_DISPLAY(mi);
126         GC          gc = MI_GC(mi);
127         int         i, n, w = sp->width / 20;
128         int         not_solid = halfrandom(sp, 10);
129
130         sp->backwards = (int) (LRAND() & 1);    /* jwz: go the other way sometimes */
131
132         if (sp->first_time || !halfrandom(sp, 10)) {
133                 MI_CLEARWINDOW(mi);
134                 n = 300;
135         } else {
136                 if (halfrandom(sp, 5))
137                         return;
138                 if (halfrandom(sp, 5))
139                         n = 100;
140                 else
141                         n = 2000;
142         }
143
144         if (MI_NPIXELS(mi) > 2)
145                 XSetForeground(display, gc, MI_PIXEL(mi, halfrandom(sp, MI_NPIXELS(mi))));
146         else if (halfrandom(sp, 2))
147                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
148         else
149                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
150
151         for (i = 0; i < n; i++) {
152                 int         ww = ((w / 2) + halfrandom(sp, MAX(w, 1)));
153
154                 if (not_solid) {
155                         if (MI_NPIXELS(mi) > 2)
156                                 XSetForeground(display, gc, MI_PIXEL(mi, halfrandom(sp, MI_NPIXELS(mi))));
157                         else if (halfrandom(sp, 2))
158                                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
159                         else
160                                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
161                 }
162                 XFillRectangle(display, MI_WINDOW(mi), gc,
163                                halfrandom(sp, MAX(sp->width - ww, 1)),
164                                halfrandom(sp, MAX(sp->height - ww, 1)),
165                                ww, ww);
166         }
167         sp->first_time = 0;
168
169
170 #ifdef STANDALONE                         /* jwz -- sometimes hack the desktop image! */
171         if (!sp->image_loading_p &&
172         (1||halfrandom(sp, 2) == 0)) {
173       /* Load it into a pixmap so that the "Loading" message and checkerboard
174          don't show up on the window -- we keep running while the image is
175          in progress... */
176       Pixmap p = XCreatePixmap (MI_DISPLAY(mi), MI_WINDOW(mi),
177                                 sp->width, sp->height, mi->xgwa.depth);
178       sp->image_loading_p = True;
179       load_image_async (ScreenOfDisplay (MI_DISPLAY(mi), 0/*####MI_SCREEN(mi)*/),
180                         MI_WINDOW(mi), p, image_loaded_cb, mi);
181         }
182 #endif
183 }
184
185 static int
186 quantize(double d)
187 {
188         int         i = (int) floor(d);
189         double      f = d - i;
190
191         if ((LRAND() & 0xff) < f * 0xff)
192                 i++;
193         return i;
194 }
195
196 ENTRYPOINT void
197 reshape_slip (ModeInfo * mi, int w, int h)
198 {
199   slipstruct *sp = &slips[MI_SCREEN(mi)];
200   sp->width = w;
201   sp->height = h;
202   sp->blit_width = sp->width / 25;
203   sp->blit_height = sp->height / 25;
204
205   sp->mode = 0;
206   sp->nblits_remaining = 0;
207 }
208
209 ENTRYPOINT void
210 init_slip (ModeInfo * mi)
211 {
212         slipstruct *sp;
213
214         MI_INIT (mi, slips, 0);
215         sp = &slips[MI_SCREEN(mi)];
216
217         sp->nblits_remaining = 0;
218         sp->mode = 0;
219         sp->first_time = 1;
220
221         /* no "NoExpose" events from XCopyArea wanted */
222         XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
223
224     reshape_slip (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
225 }
226
227 ENTRYPOINT void
228 draw_slip (ModeInfo * mi)
229 {
230         Display    *display = MI_DISPLAY(mi);
231         Window      window = MI_WINDOW(mi);
232         GC          gc = MI_GC(mi);
233         int         timer;
234         slipstruct *sp;
235
236         if (slips == NULL)
237                 return;
238         sp = &slips[MI_SCREEN(mi)];
239
240         timer = MI_COUNT(mi) * MI_CYCLES(mi);
241
242         MI_IS_DRAWN(mi) = True;
243
244         while (timer--) {
245                 int         xi = halfrandom(sp, MAX(sp->width - sp->blit_width, 1));
246                 int         yi = halfrandom(sp, MAX(sp->height - sp->blit_height, 1));
247                 double      x, y, dx = 0, dy = 0, t, s1, s2;
248
249                 if (0 == sp->nblits_remaining--) {
250                         static const int lut[] = {0, 0, 0, 1, 1, 1, 2};
251
252                         prepare_screen(mi, sp);
253                         sp->nblits_remaining = MI_COUNT(mi) *
254                                 (2000 + halfrandom(sp, 1000) + halfrandom(sp, 1000));
255                         if (sp->mode == 2)
256                                 sp->mode = halfrandom(sp, 2);
257                         else
258                                 sp->mode = lut[halfrandom(sp, 7)];
259                 }
260                 x = (2 * xi + sp->blit_width) / (double) sp->width - 1;
261                 y = (2 * yi + sp->blit_height) / (double) sp->height - 1;
262
263                 /* (x,y) is in biunit square */
264                 switch (sp->mode) {
265                         case 0: /* rotor */
266                                 dx = x;
267                                 dy = y;
268
269                                 if (dy < 0) {
270                                         dy += 0.04;
271                                         if (dy > 0)
272                                                 dy = 0.00;
273                                 }
274                                 if (dy > 0) {
275                                         dy -= 0.04;
276                                         if (dy < 0)
277                                                 dy = 0.00;
278                                 }
279                                 t = dx * dx + dy * dy + 1e-10;
280                                 s1 = 2 * dx * dx / t - 1;
281                                 s2 = 2 * dx * dy / t;
282                                 dx = s1 * 5;
283                                 dy = s2 * 5;
284
285                                 if (sp->backwards) {    /* jwz: go the other way sometimes */
286                                         dx = -dx;
287                                         dy = -dy;
288                                 }
289                                 break;
290                         case 1: /* shuffle */
291                                 dx = erandom(sp, 3);
292                                 dy = erandom(sp, 3);
293                                 break;
294                         case 2: /* explode */
295                                 dx = x * 3;
296                                 dy = y * 3;
297                                 break;
298                 }
299                 {
300                         int         qx = xi + quantize(dx), qy = yi + quantize(dy);
301                         int         wrap;
302
303                         if (qx < 0 || qy < 0 ||
304                             qx >= sp->width - sp->blit_width ||
305                             qy >= sp->height - sp->blit_height)
306                                 continue;
307
308 /*-
309 Seems to cause problems using Exceed
310 with PseudoColor
311 X Error of failed request:  BadGC (invalid GC parameter)
312 with TrueColor
313 X Error of failed request:  BadDrawable (invalid Pixmap or Window parameter)
314   Major opcode of failed request:  62 (X_CopyArea)
315  */
316                         XCopyArea(display, window, window, gc, xi, yi,
317                                   sp->blit_width, sp->blit_height,
318                                   qx, qy);
319                         switch (sp->mode) {
320                                 case 0:
321                                         /* wrap */
322                                         wrap = sp->width - (2 * sp->blit_width);
323                                         if (qx > wrap ) {
324                                                 XCopyArea(display, window, window, gc, qx, qy,
325                                                 sp->blit_width, sp->blit_height,
326                                                           qx - wrap, qy);
327                                         }
328                                         if (qx < 2 * sp->blit_width) {
329                                                 XCopyArea(display, window, window, gc, qx, qy,
330                                                 sp->blit_width, sp->blit_height,
331                                                           qx + wrap, qy);
332                                         }
333                                         wrap = sp->height - (2 * sp->blit_height);
334                                         if (qy > wrap) {
335                                                 XCopyArea(display, window, window, gc, qx, qy,
336                                                 sp->blit_width, sp->blit_height,
337                                                           qx, qy - wrap);
338                                         }
339                                         if (qy < 2 * sp->blit_height) {
340                                                 XCopyArea(display, window, window, gc, qx, qy,
341                                                 sp->blit_width, sp->blit_height,
342                                                           qx, qy + wrap);
343                                         }
344                                         break;
345                                 case 1:
346                                 case 2:
347                                         break;
348                         }
349                 }
350         }
351 }
352
353 ENTRYPOINT Bool
354 slip_handle_event (ModeInfo *mi, XEvent *event)
355 {
356   slipstruct *sp = &slips[MI_SCREEN(mi)];
357   if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
358     {
359       sp->first_time = 1;
360       sp->nblits_remaining = 0;
361       return True;
362     }
363   return False;
364 }
365
366 XSCREENSAVER_MODULE ("Slip", slip)
367
368 #endif /* MODE_slip */