source: trunk/third/xscreensaver/hacks/jigsaw.c @ 20148

Revision 20148, 14.8 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* xscreensaver, Copyright (c) 1997, 1998, 2001, 2003
2 *  Jamie Zawinski <jwz@jwz.org>
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/*
14  TODO:
15
16    =  Rather than just flickering the pieces before swapping them,
17       show them lifting up and moving to their new positions.
18       The path on which they move shouldn't be a straight line;
19       try to avoid having them cross each other by moving them in
20       oppositely-positioned arcs.
21
22    =  Rotate the pieces as well, so that we can swap the corner
23       and edge pieces with each other.
24
25    =  The shapes of the piece bitmaps still aren't quite right.
26       They should line up with no overlap.  They don't...
27   
28    =  Have it drop all pieces to the "floor" then pick them up to
29       reassemble the picture.
30
31    =  As a joke, maybe sometimes have one piece that doesn't fit?
32       Or lose a piece?
33 */
34
35#include "screenhack.h"
36
37#define DEBUG
38
39#include "images/jigsaw/jigsaw_a_h.xbm"
40#include "images/jigsaw/jigsaw_a_n_h.xbm"
41#include "images/jigsaw/jigsaw_a_ne_h.xbm"
42#include "images/jigsaw/jigsaw_a_e_h.xbm"
43#include "images/jigsaw/jigsaw_a_se_h.xbm"
44#include "images/jigsaw/jigsaw_a_s_h.xbm"
45#include "images/jigsaw/jigsaw_a_sw_h.xbm"
46#include "images/jigsaw/jigsaw_a_w_h.xbm"
47#include "images/jigsaw/jigsaw_a_nw_h.xbm"
48
49#include "images/jigsaw/jigsaw_b_h.xbm"
50#include "images/jigsaw/jigsaw_b_n_h.xbm"
51#include "images/jigsaw/jigsaw_b_ne_h.xbm"
52#include "images/jigsaw/jigsaw_b_e_h.xbm"
53#include "images/jigsaw/jigsaw_b_se_h.xbm"
54#include "images/jigsaw/jigsaw_b_s_h.xbm"
55#include "images/jigsaw/jigsaw_b_sw_h.xbm"
56#include "images/jigsaw/jigsaw_b_w_h.xbm"
57#include "images/jigsaw/jigsaw_b_nw_h.xbm"
58
59#include "images/jigsaw/jigsaw_a_f.xbm"
60#include "images/jigsaw/jigsaw_a_n_f.xbm"
61#include "images/jigsaw/jigsaw_a_ne_f.xbm"
62#include "images/jigsaw/jigsaw_a_e_f.xbm"
63#include "images/jigsaw/jigsaw_a_se_f.xbm"
64#include "images/jigsaw/jigsaw_a_s_f.xbm"
65#include "images/jigsaw/jigsaw_a_sw_f.xbm"
66#include "images/jigsaw/jigsaw_a_w_f.xbm"
67#include "images/jigsaw/jigsaw_a_nw_f.xbm"
68
69#include "images/jigsaw/jigsaw_b_f.xbm"
70#include "images/jigsaw/jigsaw_b_n_f.xbm"
71#include "images/jigsaw/jigsaw_b_ne_f.xbm"
72#include "images/jigsaw/jigsaw_b_e_f.xbm"
73#include "images/jigsaw/jigsaw_b_se_f.xbm"
74#include "images/jigsaw/jigsaw_b_s_f.xbm"
75#include "images/jigsaw/jigsaw_b_sw_f.xbm"
76#include "images/jigsaw/jigsaw_b_w_f.xbm"
77#include "images/jigsaw/jigsaw_b_nw_f.xbm"
78
79#define GRID_WIDTH  66
80#define GRID_HEIGHT 66
81
82#define CENTER    0
83#define NORTH     1
84#define NORTHEAST 2
85#define EAST      3
86#define SOUTHEAST 4
87#define SOUTH     5
88#define SOUTHWEST 6
89#define WEST      7
90#define NORTHWEST 8
91
92struct piece {
93  int width, height;
94  int x, y;
95  Pixmap pixmap;
96};
97
98struct set {
99  struct piece pieces[9];
100};
101
102#define PIECE_A_HOLLOW 0
103#define PIECE_A_FILLED 1
104#define PIECE_B_HOLLOW 2
105#define PIECE_B_FILLED 3
106
107static struct set all_pieces[4];
108
109static void
110init_images(Display *dpy, Window window)
111{
112# define LOAD_PIECE(PIECE,NAME)                                 \
113    PIECE.x = jigsaw_##NAME##_x_hot;                            \
114    PIECE.y = jigsaw_##NAME##_y_hot;                            \
115    PIECE.pixmap =                                              \
116    XCreatePixmapFromBitmapData(dpy, window,                    \
117                                (char *) jigsaw_##NAME##_bits,  \
118                                jigsaw_##NAME##_width,          \
119                                jigsaw_##NAME##_height,         \
120                                1, 0, 1)
121
122# define LOAD_PIECES(SET,PREFIX,SUFFIX)                         \
123    LOAD_PIECE(SET.pieces[CENTER],    PREFIX##_##SUFFIX);       \
124    LOAD_PIECE(SET.pieces[NORTH],     PREFIX##_n_##SUFFIX);     \
125    LOAD_PIECE(SET.pieces[NORTHEAST], PREFIX##_ne_##SUFFIX);    \
126    LOAD_PIECE(SET.pieces[EAST],      PREFIX##_e_##SUFFIX);     \
127    LOAD_PIECE(SET.pieces[SOUTHEAST], PREFIX##_se_##SUFFIX);    \
128    LOAD_PIECE(SET.pieces[SOUTH],     PREFIX##_s_##SUFFIX);     \
129    LOAD_PIECE(SET.pieces[SOUTHWEST], PREFIX##_sw_##SUFFIX);    \
130    LOAD_PIECE(SET.pieces[WEST],      PREFIX##_w_##SUFFIX);     \
131    LOAD_PIECE(SET.pieces[NORTHWEST], PREFIX##_nw_##SUFFIX)
132
133  LOAD_PIECES(all_pieces[PIECE_A_HOLLOW],a,h);
134  LOAD_PIECES(all_pieces[PIECE_A_FILLED],a,f);
135  LOAD_PIECES(all_pieces[PIECE_B_HOLLOW],b,h);
136  LOAD_PIECES(all_pieces[PIECE_B_FILLED],b,f);
137
138# undef LOAD_PIECE
139# undef LOAD_PIECES
140}
141
142static Pixmap
143read_screen (Display *dpy, Window window, int *widthP, int *heightP)
144{
145  Pixmap p;
146  XWindowAttributes xgwa;
147  XGetWindowAttributes (dpy, window, &xgwa);
148  *widthP = xgwa.width;
149  *heightP = xgwa.height;
150
151  p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
152  XClearWindow(dpy, window);
153  load_random_image (xgwa.screen, window, p, NULL);
154  XClearWindow(dpy, window);
155
156  return p;
157}
158
159
160static int width, height;
161static int x_border, y_border;
162static Pixmap source;
163static GC gc;
164static Bool tweak;
165static int fg, bg;
166static XPoint *state = 0;
167
168static void
169jigsaw_init(Display *dpy, Window window)
170{
171  XWindowAttributes xgwa;
172  int x, y;
173  XGCValues gcv;
174  Colormap cmap;
175  int source_w, source_h;
176
177  tweak = random()&1;
178
179  source = read_screen (dpy, window, &source_w, &source_h);
180
181  XGetWindowAttributes (dpy, window, &xgwa);
182  cmap = xgwa.colormap;
183  width  = xgwa.width  / GRID_WIDTH;
184  height = xgwa.height / GRID_HEIGHT;
185  x_border = (xgwa.width  - (width  * GRID_WIDTH)) / 2;
186  y_border = (xgwa.height - (height * GRID_WIDTH)) / 2;
187
188  if (width < 4 || height < 4)
189    {
190      fprintf (stderr, "%s: window too small: %dx%d (need at least %dx%d)\n",
191               progname, xgwa.width, xgwa.height,
192               GRID_WIDTH * 4, GRID_HEIGHT * 4);
193      exit (1);
194    }
195
196  if (!state)
197    state = (XPoint *) malloc(width * height * sizeof(XPoint));
198  gc = XCreateGC (dpy, window, 0, &gcv);
199
200  {
201    XColor fgc, bgc;
202    char *fgs = get_string_resource("foreground", "Foreground");
203    char *bgs = get_string_resource("background", "Background");
204    Bool fg_ok, bg_ok;
205    if (!XParseColor (dpy, cmap, fgs, &fgc))
206      XParseColor (dpy, cmap, "gray", &fgc);
207    if (!XParseColor (dpy, cmap, bgs, &bgc))
208      XParseColor (dpy, cmap, "black", &bgc);
209
210    fg_ok = XAllocColor (dpy, cmap, &fgc);
211    bg_ok = XAllocColor (dpy, cmap, &bgc);
212
213    /* If we weren't able to allocate the two colors we want from the
214       colormap (which is likely if the screen has been grabbed on an
215       8-bit SGI visual -- don't ask) then just go through the map
216       and find the closest color to the ones we wanted, and use those
217       pixels without actually allocating them.
218     */
219    if (fg_ok)
220      fg = fgc.pixel;
221    else
222      fg = 0;
223
224    if (bg_ok)
225      bg = bgc.pixel;
226    else
227      bg = 1;
228
229    if (!fg_ok || bg_ok)
230      {
231        int i;
232        unsigned long fgd = ~0;
233        unsigned long bgd = ~0;
234        int max = visual_cells (xgwa.screen, xgwa.visual);
235        XColor *all = (XColor *) calloc(sizeof (*all), max);
236        for (i = 0; i < max; i++)
237          {
238            all[i].flags = DoRed|DoGreen|DoBlue;
239            all[i].pixel = i;
240          }
241        XQueryColors (dpy, cmap, all, max);
242        for(i = 0; i < max; i++)
243          {
244            long rd, gd, bd;
245            unsigned long d;
246            if (!fg_ok)
247              {
248                rd = (all[i].red   >> 8) - (fgc.red   >> 8);
249                gd = (all[i].green >> 8) - (fgc.green >> 8);
250                bd = (all[i].blue  >> 8) - (fgc.blue  >> 8);
251                if (rd < 0) rd = -rd;
252                if (gd < 0) gd = -gd;
253                if (bd < 0) bd = -bd;
254                d = (rd << 1) + (gd << 2) + bd;
255                if (d < fgd)
256                  {
257                    fgd = d;
258                    fg = all[i].pixel;
259                    if (d == 0)
260                      fg_ok = True;
261                  }
262              }
263
264            if (!bg_ok)
265              {
266                rd = (all[i].red   >> 8) - (bgc.red   >> 8);
267                gd = (all[i].green >> 8) - (bgc.green >> 8);
268                bd = (all[i].blue  >> 8) - (bgc.blue  >> 8);
269                if (rd < 0) rd = -rd;
270                if (gd < 0) gd = -gd;
271                if (bd < 0) bd = -bd;
272                d = (rd << 1) + (gd << 2) + bd;
273                if (d < bgd)
274                  {
275                    bgd = d;
276                    bg = all[i].pixel;
277                    if (d == 0)
278                      bg_ok = True;
279                  }
280              }
281
282            if (fg_ok && bg_ok)
283              break;
284          }
285        XFree(all);
286      }
287  }
288
289  /* Reset the window's background color... */
290  XSetWindowBackground (dpy, window, bg);
291  XClearWindow(dpy, window);
292
293  for (y = 0; y < height; y++)
294    for (x = 0; x < width; x++)
295      {
296        state[y * width + x].x = x;
297        state[y * width + x].y = y;
298      }
299}
300
301
302static void
303get_piece(int x, int y, struct piece **hollow, struct piece **filled)
304{
305  int p;
306  Bool which = (x & 1) == (y & 1);
307
308  if      (x == 0       && y == 0)        p = NORTHWEST;
309  else if (x == width-1 && y == 0)        p = NORTHEAST;
310  else if (x == width-1 && y == height-1) p = SOUTHEAST;
311  else if (x == 0       && y == height-1) p = SOUTHWEST;
312  else if (y == 0)                        p = NORTH;
313  else if (x == width-1)                  p = EAST;
314  else if (y == height-1)                 p = SOUTH;
315  else if (x == 0)                        p = WEST;
316  else                                    p = CENTER;
317
318  if (tweak) which = !which;
319  if (hollow)
320    *hollow = (which
321               ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
322               : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
323  if (filled)
324    *filled = (which
325               ? &all_pieces[PIECE_A_FILLED].pieces[p]
326               : &all_pieces[PIECE_B_FILLED].pieces[p]);
327}
328
329
330static void
331draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
332{
333  struct piece *hollow, *filled;
334  int from_x = state[y * width + x].x;
335  int from_y = state[y * width + x].y;
336
337  get_piece(x, y, &hollow, &filled);
338         
339  XSetClipMask(dpy, gc, filled->pixmap);
340  XSetClipOrigin(dpy, gc,
341                 x_border + (x * GRID_WIDTH) - filled->x - 1,
342                 y_border + (y * GRID_WIDTH) - filled->y - 1);
343
344  if (clear_p)
345    {
346      XSetForeground(dpy, gc, bg);
347      XFillRectangle(dpy, window, gc,
348                     x_border + (x * GRID_WIDTH)  - GRID_WIDTH/2,
349                     y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
350                     GRID_WIDTH*2, GRID_HEIGHT*2);
351    }
352  else
353    XCopyArea(dpy, source, window, gc,
354              x_border + (from_x * GRID_WIDTH)  - GRID_WIDTH/2,
355              y_border + (from_y * GRID_HEIGHT) - GRID_HEIGHT/2,
356              GRID_WIDTH*2, GRID_HEIGHT*2,
357              x_border + (x * GRID_WIDTH)  - GRID_WIDTH/2,
358              y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2);
359
360  if (clear_p > 1)
361    return;
362
363  XSetForeground(dpy, gc, fg);
364  XSetClipMask(dpy, gc, hollow->pixmap);
365  XSetClipOrigin(dpy, gc,
366                 x_border + (x * GRID_WIDTH) - hollow->x - 1,
367                 y_border + (y * GRID_WIDTH) - hollow->y - 1);
368  XFillRectangle(dpy, window, gc,
369                 x_border + (x * GRID_WIDTH)  - GRID_WIDTH/2,
370                 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
371                 GRID_WIDTH*2, GRID_HEIGHT*2);
372
373  if (clear_p)
374    {
375      /* If the pieces lined up right, we could do this by just not drawing
376         the outline -- but that doesn't look right, since it eats the outlines
377         of the adjascent pieces.  So draw the outline, then chop off the outer
378         edge if this is a border piece.
379       */
380      XSetForeground(dpy, gc, bg);
381      if (x == 0)
382        XFillRectangle(dpy, window, gc,
383                       x_border - 2,
384                       y_border + (y * GRID_HEIGHT),
385                       3, GRID_HEIGHT);
386      else if (x == width-1)
387        XFillRectangle(dpy, window, gc,
388                       x_border + ((x+1) * GRID_WIDTH) - 2,
389                       y_border + (y * GRID_HEIGHT),
390                       3, GRID_HEIGHT);
391
392      if (y == 0)
393        XFillRectangle(dpy, window, gc,
394                       x_border + (x * GRID_WIDTH),
395                       y_border - 2,
396                       GRID_WIDTH, 3);
397      else if (y == height-1)
398        XFillRectangle(dpy, window, gc,
399                       x_border + (x * GRID_WIDTH),
400                       y_border + ((y+1) * GRID_HEIGHT) - 2,
401                       GRID_WIDTH, 3);
402    }
403}
404
405
406static void
407swap_pieces(Display *dpy, Window window,
408            int src_x, int src_y, int dst_x, int dst_y,
409            Bool draw_p)
410{
411  XPoint swap;
412  int i;
413  if (draw_p)
414    for (i = 0; i < 3; i++)
415      {
416        draw_piece(dpy, window, src_x, src_y, 1);
417        draw_piece(dpy, window, dst_x, dst_y, 1);
418        XSync(dpy, False);
419        usleep(50000);
420        draw_piece(dpy, window, src_x, src_y, 0);
421        draw_piece(dpy, window, dst_x, dst_y, 0);
422        XSync(dpy, False);
423        usleep(50000);
424      }
425
426  swap = state[src_y * width + src_x];
427  state[src_y * width + src_x] = state[dst_y * width + dst_x];
428  state[dst_y * width + dst_x] = swap;
429
430  if (draw_p)
431    {
432      draw_piece(dpy, window, src_x, src_y, 0);
433      draw_piece(dpy, window, dst_x, dst_y, 0);
434      XSync(dpy, False);
435    }
436}
437
438
439static void
440shuffle(Display *dpy, Window window, Bool draw_p)
441{
442  struct piece *p1, *p2;
443  int src_x, src_y, dst_x = -1, dst_y = -1;
444
445 AGAIN:
446  p1 = p2 = 0;
447  src_x = random() % width;
448  src_y = random() % height;
449
450  get_piece(src_x, src_y, &p1, 0);
451
452  /* Pick random coordinates until we find one that has the same kind of
453     piece as the first one we picked.  Note that it's possible for there
454     to be only one piece of a particular shape on the board (this commonly
455     happens with the corner pieces.)
456   */
457  while (p1 != p2)
458    {
459      dst_x = random() % width;
460      dst_y = random() % height;
461      get_piece(dst_x, dst_y, &p2, 0);
462    }
463
464  if (src_x == dst_x && src_y == dst_y)
465    goto AGAIN;
466
467  swap_pieces(dpy, window, src_x, src_y, dst_x, dst_y, draw_p);
468}
469
470
471static void
472shuffle_all(Display *dpy, Window window)
473{
474  int i = (width * height * 10);
475  while (i > 0)
476    {
477      shuffle(dpy, window, False);
478      i--;
479    }
480}
481
482static void
483unshuffle(Display *dpy, Window window)
484{
485  int i;
486  for (i = 0; i < width * height * 4; i++)
487    {
488      int x = random() % width;
489      int y = random() % height;
490      int x2 = state[y * width + x].x;
491      int y2 = state[y * width + x].y;
492      if (x != x2 || y != y2)
493        {
494          swap_pieces(dpy, window, x, y, x2, y2, True);
495          break;
496        }
497    }
498}
499
500static void
501clear_all(Display *dpy, Window window)
502{
503  int n = width * height;
504  while (n > 0)
505    {
506      int x = random() % width;
507      int y = random() % height;
508      XPoint *p = &state[y * width + x];
509      if (p->x == -1)
510        continue;
511      draw_piece(dpy, window, p->x, p->y, 2);
512      XSync(dpy, False);
513      usleep(1000);
514      p->x = p->y = -1;
515      n--;
516    }
517}
518
519static Bool
520done(void)
521{
522  int x, y;
523  for (y = 0; y < height; y++)
524    for (x = 0; x < width; x++)
525      {
526        int x2 = state[y * width + x].x;
527        int y2 = state[y * width + x].y;
528        if (x != x2 || y != y2)
529          return False;
530      }
531  return True;
532}
533
534
535
536char *progclass = "Jigsaw";
537
538char *defaults [] = {
539  ".background:         Black",
540  ".foreground:         Gray40",
541  "*delay:              70000",
542  "*delay2:             5",
543#ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
544  "*visualID:           Best",
545#endif
546  0
547};
548
549XrmOptionDescRec options [] = {
550  { "-delay",           ".delay",               XrmoptionSepArg, 0 },
551  { "-delay2",          ".delay2",              XrmoptionSepArg, 0 },
552  { 0, 0, 0, 0 }
553};
554
555void
556screenhack (Display *dpy, Window window)
557{
558  int delay = get_integer_resource("delay", "Integer");
559  int delay2 = get_integer_resource("delay2", "Integer");
560
561  init_images(dpy, window);
562
563  while (1)
564    {
565      int x, y;
566      jigsaw_init (dpy, window);
567      shuffle_all(dpy, window);
568
569      for (y = 0; y < height; y++)
570        for (x = 0; x < width; x++)
571          draw_piece(dpy, window, x, y, 0);
572
573      while (!done())
574        {
575          unshuffle(dpy, window);
576          XSync (dpy, False);
577          screenhack_handle_events (dpy);
578          if (delay) usleep (delay);
579        }
580
581      screenhack_handle_events (dpy);
582      if (delay2)
583        usleep (delay2 * 1000000);
584
585      clear_all(dpy, window);
586    }
587}
Note: See TracBrowser for help on using the repository browser.