f910dbe7eb9ac15b13dec02d8d5ff3e7b139fafd
[xscreensaver] / hacks / slidescreen.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997 
2  * Jamie Zawinski <jwz@netscape.com>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include "screenhack.h"
14
15 static int grid_size;
16 static int pix_inc;
17 static int hole_x, hole_y;
18 static int bitmap_w, bitmap_h;
19 static int xoff, yoff;
20 static int grid_w, grid_h;
21 static int delay, delay2;
22 static GC gc;
23 static int max_width, max_height;
24
25 static void
26 init_slide (Display *dpy, Window window)
27 {
28   int i;
29   XGCValues gcv;
30   XWindowAttributes xgwa;
31   long gcflags;
32   int border;
33   unsigned long fg, bg;
34   Drawable d;
35   Colormap cmap;
36   Visual *visual;
37
38   XGetWindowAttributes (dpy, window, &xgwa);
39   grab_screen_image (xgwa.screen, window);
40
41   XGetWindowAttributes (dpy, window, &xgwa);  /* re-retrieve colormap */
42   cmap = xgwa.colormap;
43   visual = xgwa.visual;
44   max_width = xgwa.width;
45   max_height = xgwa.height;
46
47   delay = get_integer_resource ("delay", "Integer");
48   delay2 = get_integer_resource ("delay2", "Integer");
49   grid_size = get_integer_resource ("gridSize", "Integer");
50   pix_inc = get_integer_resource ("pixelIncrement", "Integer");
51   border = get_integer_resource ("internalBorderWidth", "InternalBorderWidth");
52
53   {
54     XColor fgc, bgc;
55     char *fgs = get_string_resource("background", "Background");
56     char *bgs = get_string_resource("foreground", "Foreground");
57     Bool fg_ok, bg_ok;
58     if (!XParseColor (dpy, cmap, fgs, &fgc))
59       XParseColor (dpy, cmap, "black", &bgc);
60     if (!XParseColor (dpy, cmap, bgs, &bgc))
61       XParseColor (dpy, cmap, "gray", &fgc);
62
63     fg_ok = XAllocColor (dpy, cmap, &fgc);
64     bg_ok = XAllocColor (dpy, cmap, &bgc);
65
66     /* If we weren't able to allocate the two colors we want from the
67        colormap (which is likely if the screen has been grabbed on an
68        8-bit SGI visual -- don't ask) then just go through the map
69        and find the closest color to the ones we wanted, and use those
70        pixels without actually allocating them.
71      */
72     if (fg_ok)
73       fg = fgc.pixel;
74     else
75       fg = 0;
76
77     if (bg_ok)
78       bg = bgc.pixel;
79     else
80       bg = 1;
81
82     if (!fg_ok || bg_ok)
83       {
84         unsigned long fgd = ~0;
85         unsigned long bgd = ~0;
86         int max = visual_cells (xgwa.screen, visual);
87         XColor *all = (XColor *) calloc(sizeof (*all), max);
88         for (i = 0; i < max; i++)
89           {
90             all[i].flags = DoRed|DoGreen|DoBlue;
91             all[i].pixel = i;
92           }
93         XQueryColors (dpy, cmap, all, max);
94         for(i = 0; i < max; i++)
95           {
96             long rd, gd, bd;
97             unsigned long d;
98             if (!fg_ok)
99               {
100                 rd = (all[i].red   >> 8) - (fgc.red   >> 8);
101                 gd = (all[i].green >> 8) - (fgc.green >> 8);
102                 bd = (all[i].blue  >> 8) - (fgc.blue  >> 8);
103                 if (rd < 0) rd = -rd;
104                 if (gd < 0) gd = -gd;
105                 if (bd < 0) bd = -bd;
106                 d = (rd << 1) + (gd << 2) + bd;
107                 if (d < fgd)
108                   {
109                     fgd = d;
110                     fg = all[i].pixel;
111                     if (d == 0)
112                       fg_ok = True;
113                   }
114               }
115
116             if (!bg_ok)
117               {
118                 rd = (all[i].red   >> 8) - (bgc.red   >> 8);
119                 gd = (all[i].green >> 8) - (bgc.green >> 8);
120                 bd = (all[i].blue  >> 8) - (bgc.blue  >> 8);
121                 if (rd < 0) rd = -rd;
122                 if (gd < 0) gd = -gd;
123                 if (bd < 0) bd = -bd;
124                 d = (rd << 1) + (gd << 2) + bd;
125                 if (d < bgd)
126                   {
127                     bgd = d;
128                     bg = all[i].pixel;
129                     if (d == 0)
130                       bg_ok = True;
131                   }
132               }
133
134             if (fg_ok && bg_ok)
135               break;
136           }
137         XFree(all);
138       }
139   }
140
141
142   if (delay < 0) delay = 0;
143   if (delay2 < 0) delay2 = 0;
144   if (pix_inc < 1) pix_inc = 1;
145   if (grid_size < 1) grid_size = 1;
146
147   gcv.foreground = fg;
148   gcv.function = GXcopy;
149   gcv.subwindow_mode = IncludeInferiors;
150   gcflags = GCForeground |GCFunction;
151   if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
152     gcflags |= GCSubwindowMode;
153   gc = XCreateGC (dpy, window, gcflags, &gcv);
154
155   XGetWindowAttributes (dpy, window, &xgwa);
156   bitmap_w = xgwa.width;
157   bitmap_h = xgwa.height;
158
159   grid_w = bitmap_w / grid_size;
160   grid_h = bitmap_h / grid_size;
161   hole_x = random () % grid_w;
162   hole_y = random () % grid_h;
163   xoff = (bitmap_w - (grid_w * grid_size)) / 2;
164   yoff = (bitmap_h - (grid_h * grid_size)) / 2;
165
166   d = window;
167
168   if (border)
169     {
170       int half = border/2;
171       int half2 = (border & 1 ? half+1 : half);
172       XSetForeground(dpy, gc, bg);
173       for (i = 0; i < bitmap_w; i += grid_size)
174         {
175           int j;
176           for (j = 0; j < bitmap_h; j += grid_size)
177             XDrawRectangle (dpy, d, gc,
178                             xoff+i+half2, yoff+j+half2,
179                             grid_size-border-1, grid_size-border-1);
180         }
181
182       XSetForeground(dpy, gc, fg);
183       for (i = 0; i <= bitmap_w; i += grid_size)
184         XFillRectangle (dpy, d, gc, xoff+i-half, yoff, border, bitmap_h);
185       for (i = 0; i <= bitmap_h; i += grid_size)
186         XFillRectangle (dpy, d, gc, xoff, yoff+i-half, bitmap_w, border);
187     }
188
189   if (xoff)
190     {
191       XFillRectangle (dpy, d, gc, 0, 0, xoff, bitmap_h);
192       XFillRectangle (dpy, d, gc, bitmap_w - xoff, 0, xoff, bitmap_h);
193     }
194   if (yoff)
195     {
196       XFillRectangle (dpy, d, gc, 0, 0, bitmap_w, yoff);
197       XFillRectangle (dpy, d, gc, 0, bitmap_h - yoff, bitmap_w, yoff);
198     }
199
200   XSync (dpy, True);
201   if (delay2) usleep (delay2 * 2);
202  for (i = 0; i < grid_size; i += pix_inc)
203    {
204      XPoint points [3];
205      points[0].x = xoff + grid_size * hole_x;
206      points[0].y = yoff + grid_size * hole_y;
207      points[1].x = points[0].x + grid_size;
208      points[1].y = points[0].y;
209      points[2].x = points[0].x;
210      points[2].y = points[0].y + i;
211      XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin);
212
213      points[1].x = points[0].x;
214      points[1].y = points[0].y + grid_size;
215      points[2].x = points[0].x + i;
216      points[2].y = points[0].y + grid_size;
217      XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin);
218
219      points[0].x = points[1].x + grid_size;
220      points[0].y = points[1].y;
221      points[2].x = points[0].x;
222      points[2].y = points[0].y - i;
223      XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin);
224
225      points[1].x = points[0].x;
226      points[1].y = points[0].y - grid_size;
227      points[2].x = points[1].x - i;
228      points[2].y = points[1].y;
229      XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin);
230
231      XSync (dpy, True);
232      if (delay) usleep (delay);
233    }
234
235   XFillRectangle (dpy, window, gc,
236                   xoff + grid_size * hole_x,
237                   yoff + grid_size * hole_y,
238                   grid_size, grid_size);
239 }
240
241 static void
242 slide1 (Display *dpy, Window window)
243 {
244   /* this code is a total kludge, but who cares, it works... */
245  int i, x, y, ix, iy, dx, dy, dir, w, h, size, inc;
246  static int last = -1;
247  do {
248    dir = random () % 4;
249    switch (dir)
250      {
251      case 0: dx = 0,  dy = 1;  break;
252      case 1: dx = -1, dy = 0;  break;
253      case 2: dx = 0,  dy = -1; break;
254      case 3: dx = 1,  dy = 0;  break;
255      default: abort ();
256      }
257  } while (dir == last ||
258           hole_x + dx < 0 || hole_x + dx >= grid_w ||
259           hole_y + dy < 0 || hole_y + dy >= grid_h);
260  if (grid_w > 1 && grid_h > 1)
261    last = (dir == 0 ? 2 : dir == 2 ? 0 : dir == 1 ? 3 : 1);
262
263  switch (dir)
264    {
265    case 0: size = 1 + (random()%(grid_h - hole_y - 1)); h = size; w = 1; break;
266    case 1: size = 1 + (random()%hole_x);                w = size; h = 1; break;
267    case 2: size = 1 + (random()%hole_y);                h = size; w = 1; break;
268    case 3: size = 1 + (random()%(grid_w - hole_x - 1)); w = size; h = 1; break;
269    default: abort ();
270    }
271
272  if (dx == -1) hole_x -= (size - 1);
273  else if (dy == -1) hole_y -= (size - 1);
274
275  ix = x = xoff + (hole_x + dx) * grid_size;
276  iy = y = yoff + (hole_y + dy) * grid_size;
277  inc = pix_inc;
278  for (i = 0; i < grid_size; i += inc)
279    {
280      int fx, fy, tox, toy;
281      if (inc + i > grid_size)
282        inc = grid_size - i;
283      tox = x - dx * inc;
284      toy = y - dy * inc;
285
286      fx = (x < 0 ? 0 : x > max_width  ? max_width  : x);
287      fy = (y < 0 ? 0 : y > max_height ? max_height : y);
288      tox = (tox < 0 ? 0 : tox > max_width  ? max_width  : tox);
289      toy = (toy < 0 ? 0 : toy > max_height ? max_height : toy);
290
291      XCopyArea (dpy, window, window, gc,
292                 fx, fy,
293                 grid_size * w, grid_size * h,
294                 tox, toy);
295
296      x -= dx * inc;
297      y -= dy * inc;
298      switch (dir)
299        {
300        case 0: XFillRectangle (dpy, window, gc,
301                                ix, y + grid_size * h, grid_size * w, iy - y);
302          break;
303        case 1: XFillRectangle (dpy, window, gc, ix, iy, x - ix, grid_size * h);
304          break;
305        case 2: XFillRectangle (dpy, window, gc, ix, iy, grid_size * w, y - iy);
306          break;
307        case 3: XFillRectangle (dpy, window, gc,
308                                x + grid_size * w, iy, ix - x, grid_size * h);
309          break;
310        }
311
312      XSync (dpy, True);
313      if (delay) usleep (delay);
314    }
315  switch (dir)
316    {
317    case 0: hole_y += size; break;
318    case 1: hole_x--; break;
319    case 2: hole_y--; break;
320    case 3: hole_x += size; break;
321    }
322 }
323
324 \f
325 char *progclass = "SlidePuzzle";
326
327 char *defaults [] = {
328   "*dontClearRoot:              True",
329
330 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
331   "*visualID:                   Best",
332 #endif
333
334   "SlidePuzzle.background:      Black",
335   "SlidePuzzle.foreground:      Gray",
336   "*gridSize:                   70",
337   "*pixelIncrement:             10",
338   "*internalBorderWidth:        4",
339   "*delay:                      50000",
340   "*delay2:                     1000000",
341   0
342 };
343
344 XrmOptionDescRec options [] = {
345   { "-grid-size",       ".gridSize",            XrmoptionSepArg, 0 },
346   { "-ibw",             ".internalBorderWidth", XrmoptionSepArg, 0 },
347   { "-increment",       ".pixelIncrement",      XrmoptionSepArg, 0 },
348   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
349   { "-delay2",          ".delay2",              XrmoptionSepArg, 0 },
350   { 0, 0, 0, 0 }
351 };
352
353 void
354 screenhack (Display *dpy, Window window)
355 {
356   init_slide (dpy, window);
357   while (1)
358     {
359       slide1 (dpy, window);
360       if (delay2) usleep (delay2);
361     }
362 }