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