1 /* xscreensaver, Copyright (c) 1997, 1998, 2001, 2003
2 * Jamie Zawinski <jwz@jwz.org>
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
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.
22 = Rotate the pieces as well, so that we can swap the corner
23 and edge pieces with each other.
25 = The shapes of the piece bitmaps still aren't quite right.
26 They should line up with no overlap. They don't...
28 = Have it drop all pieces to the "floor" then pick them up to
29 reassemble the picture.
31 = As a joke, maybe sometimes have one piece that doesn't fit?
35 #include "screenhack.h"
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"
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"
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"
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"
80 #define GRID_HEIGHT 66
99 struct piece pieces[9];
102 #define PIECE_A_HOLLOW 0
103 #define PIECE_A_FILLED 1
104 #define PIECE_B_HOLLOW 2
105 #define PIECE_B_FILLED 3
107 static struct set all_pieces[4];
110 init_images(Display *dpy, Window window)
112 # define LOAD_PIECE(PIECE,NAME) \
113 PIECE.x = jigsaw_##NAME##_x_hot; \
114 PIECE.y = jigsaw_##NAME##_y_hot; \
116 XCreatePixmapFromBitmapData(dpy, window, \
117 (char *) jigsaw_##NAME##_bits, \
118 jigsaw_##NAME##_width, \
119 jigsaw_##NAME##_height, \
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)
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);
143 read_screen (Display *dpy, Window window, int *widthP, int *heightP)
146 XWindowAttributes xgwa;
147 XGetWindowAttributes (dpy, window, &xgwa);
148 *widthP = xgwa.width;
149 *heightP = xgwa.height;
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);
160 static int width, height;
161 static int x_border, y_border;
162 static Pixmap source;
166 static XPoint *state = 0;
169 jigsaw_init(Display *dpy, Window window)
171 XWindowAttributes xgwa;
175 int source_w, source_h;
179 source = read_screen (dpy, window, &source_w, &source_h);
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;
188 if (width < 4 || height < 4)
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);
197 state = (XPoint *) malloc(width * height * sizeof(XPoint));
198 gc = XCreateGC (dpy, window, 0, &gcv);
202 char *fgs = get_string_resource("foreground", "Foreground");
203 char *bgs = get_string_resource("background", "Background");
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);
210 fg_ok = XAllocColor (dpy, cmap, &fgc);
211 bg_ok = XAllocColor (dpy, cmap, &bgc);
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.
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++)
238 all[i].flags = DoRed|DoGreen|DoBlue;
241 XQueryColors (dpy, cmap, all, max);
242 for(i = 0; i < max; i++)
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;
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;
289 /* Reset the window's background color... */
290 XSetWindowBackground (dpy, window, bg);
291 XClearWindow(dpy, window);
293 for (y = 0; y < height; y++)
294 for (x = 0; x < width; x++)
296 state[y * width + x].x = x;
297 state[y * width + x].y = y;
303 get_piece(int x, int y, struct piece **hollow, struct piece **filled)
306 Bool which = (x & 1) == (y & 1);
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;
318 if (tweak) which = !which;
321 ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
322 : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
325 ? &all_pieces[PIECE_A_FILLED].pieces[p]
326 : &all_pieces[PIECE_B_FILLED].pieces[p]);
331 draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
333 struct piece *hollow, *filled;
334 int from_x = state[y * width + x].x;
335 int from_y = state[y * width + x].y;
337 get_piece(x, y, &hollow, &filled);
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);
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);
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);
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);
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.
380 XSetForeground(dpy, gc, bg);
382 XFillRectangle(dpy, window, gc,
384 y_border + (y * 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),
393 XFillRectangle(dpy, window, gc,
394 x_border + (x * GRID_WIDTH),
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,
407 swap_pieces(Display *dpy, Window window,
408 int src_x, int src_y, int dst_x, int dst_y,
414 for (i = 0; i < 3; i++)
416 draw_piece(dpy, window, src_x, src_y, 1);
417 draw_piece(dpy, window, dst_x, dst_y, 1);
420 draw_piece(dpy, window, src_x, src_y, 0);
421 draw_piece(dpy, window, dst_x, dst_y, 0);
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;
432 draw_piece(dpy, window, src_x, src_y, 0);
433 draw_piece(dpy, window, dst_x, dst_y, 0);
440 shuffle(Display *dpy, Window window, Bool draw_p)
442 struct piece *p1, *p2;
443 int src_x, src_y, dst_x = -1, dst_y = -1;
447 src_x = random() % width;
448 src_y = random() % height;
450 get_piece(src_x, src_y, &p1, 0);
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.)
459 dst_x = random() % width;
460 dst_y = random() % height;
461 get_piece(dst_x, dst_y, &p2, 0);
464 if (src_x == dst_x && src_y == dst_y)
467 swap_pieces(dpy, window, src_x, src_y, dst_x, dst_y, draw_p);
472 shuffle_all(Display *dpy, Window window)
474 int i = (width * height * 10);
477 shuffle(dpy, window, False);
483 unshuffle(Display *dpy, Window window)
486 for (i = 0; i < width * height * 4; i++)
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)
494 swap_pieces(dpy, window, x, y, x2, y2, True);
501 clear_all(Display *dpy, Window window)
503 int n = width * height;
506 int x = random() % width;
507 int y = random() % height;
508 XPoint *p = &state[y * width + x];
511 draw_piece(dpy, window, p->x, p->y, 2);
523 for (y = 0; y < height; y++)
524 for (x = 0; x < width; x++)
526 int x2 = state[y * width + x].x;
527 int y2 = state[y * width + x].y;
528 if (x != x2 || y != y2)
536 char *progclass = "Jigsaw";
538 char *defaults [] = {
539 ".background: Black",
540 ".foreground: Gray40",
543 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
549 XrmOptionDescRec options [] = {
550 { "-delay", ".delay", XrmoptionSepArg, 0 },
551 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
556 screenhack (Display *dpy, Window window)
558 int delay = get_integer_resource("delay", "Integer");
559 int delay2 = get_integer_resource("delay2", "Integer");
561 init_images(dpy, window);
566 jigsaw_init (dpy, window);
567 shuffle_all(dpy, window);
569 for (y = 0; y < height; y++)
570 for (x = 0; x < width; x++)
571 draw_piece(dpy, window, x, y, 0);
575 unshuffle(dpy, window);
577 screenhack_handle_events (dpy);
578 if (delay) usleep (delay);
581 screenhack_handle_events (dpy);
583 usleep (delay2 * 1000000);
585 clear_all(dpy, window);