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