1 /* xscreensaver, Copyright (c) 1997, 1998, 2001 Jamie Zawinski <jwz@jwz.org>
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
15 = Rather than just flickering the pieces before swapping them,
16 show them lifting up and moving to their new positions.
17 The path on which they move shouldn't be a straight line;
18 try to avoid having them cross each other by moving them in
19 oppositely-positioned arcs.
21 = Rotate the pieces as well, so that we can swap the corner
22 and edge pieces with each other.
24 = The shapes of the piece bitmaps still aren't quite right.
25 They should line up with no overlap. They don't...
27 = Have it drop all pieces to the "floor" then pick them up to
28 reassemble the picture.
30 = As a joke, maybe sometimes have one piece that doesn't fit?
34 #include "screenhack.h"
38 #include "images/jigsaw/jigsaw_a_h.xbm"
39 #include "images/jigsaw/jigsaw_a_n_h.xbm"
40 #include "images/jigsaw/jigsaw_a_ne_h.xbm"
41 #include "images/jigsaw/jigsaw_a_e_h.xbm"
42 #include "images/jigsaw/jigsaw_a_se_h.xbm"
43 #include "images/jigsaw/jigsaw_a_s_h.xbm"
44 #include "images/jigsaw/jigsaw_a_sw_h.xbm"
45 #include "images/jigsaw/jigsaw_a_w_h.xbm"
46 #include "images/jigsaw/jigsaw_a_nw_h.xbm"
48 #include "images/jigsaw/jigsaw_b_h.xbm"
49 #include "images/jigsaw/jigsaw_b_n_h.xbm"
50 #include "images/jigsaw/jigsaw_b_ne_h.xbm"
51 #include "images/jigsaw/jigsaw_b_e_h.xbm"
52 #include "images/jigsaw/jigsaw_b_se_h.xbm"
53 #include "images/jigsaw/jigsaw_b_s_h.xbm"
54 #include "images/jigsaw/jigsaw_b_sw_h.xbm"
55 #include "images/jigsaw/jigsaw_b_w_h.xbm"
56 #include "images/jigsaw/jigsaw_b_nw_h.xbm"
58 #include "images/jigsaw/jigsaw_a_f.xbm"
59 #include "images/jigsaw/jigsaw_a_n_f.xbm"
60 #include "images/jigsaw/jigsaw_a_ne_f.xbm"
61 #include "images/jigsaw/jigsaw_a_e_f.xbm"
62 #include "images/jigsaw/jigsaw_a_se_f.xbm"
63 #include "images/jigsaw/jigsaw_a_s_f.xbm"
64 #include "images/jigsaw/jigsaw_a_sw_f.xbm"
65 #include "images/jigsaw/jigsaw_a_w_f.xbm"
66 #include "images/jigsaw/jigsaw_a_nw_f.xbm"
68 #include "images/jigsaw/jigsaw_b_f.xbm"
69 #include "images/jigsaw/jigsaw_b_n_f.xbm"
70 #include "images/jigsaw/jigsaw_b_ne_f.xbm"
71 #include "images/jigsaw/jigsaw_b_e_f.xbm"
72 #include "images/jigsaw/jigsaw_b_se_f.xbm"
73 #include "images/jigsaw/jigsaw_b_s_f.xbm"
74 #include "images/jigsaw/jigsaw_b_sw_f.xbm"
75 #include "images/jigsaw/jigsaw_b_w_f.xbm"
76 #include "images/jigsaw/jigsaw_b_nw_f.xbm"
79 #define GRID_HEIGHT 66
98 struct piece pieces[9];
101 #define PIECE_A_HOLLOW 0
102 #define PIECE_A_FILLED 1
103 #define PIECE_B_HOLLOW 2
104 #define PIECE_B_FILLED 3
106 static struct set all_pieces[4];
109 init_images(Display *dpy, Window window)
111 # define LOAD_PIECE(PIECE,NAME) \
112 PIECE.x = jigsaw_##NAME##_x_hot; \
113 PIECE.y = jigsaw_##NAME##_y_hot; \
115 XCreatePixmapFromBitmapData(dpy, window, \
116 (char *) jigsaw_##NAME##_bits, \
117 jigsaw_##NAME##_width, \
118 jigsaw_##NAME##_height, \
121 # define LOAD_PIECES(SET,PREFIX,SUFFIX) \
122 LOAD_PIECE(SET.pieces[CENTER], PREFIX##_##SUFFIX); \
123 LOAD_PIECE(SET.pieces[NORTH], PREFIX##_n_##SUFFIX); \
124 LOAD_PIECE(SET.pieces[NORTHEAST], PREFIX##_ne_##SUFFIX); \
125 LOAD_PIECE(SET.pieces[EAST], PREFIX##_e_##SUFFIX); \
126 LOAD_PIECE(SET.pieces[SOUTHEAST], PREFIX##_se_##SUFFIX); \
127 LOAD_PIECE(SET.pieces[SOUTH], PREFIX##_s_##SUFFIX); \
128 LOAD_PIECE(SET.pieces[SOUTHWEST], PREFIX##_sw_##SUFFIX); \
129 LOAD_PIECE(SET.pieces[WEST], PREFIX##_w_##SUFFIX); \
130 LOAD_PIECE(SET.pieces[NORTHWEST], PREFIX##_nw_##SUFFIX)
132 LOAD_PIECES(all_pieces[PIECE_A_HOLLOW],a,h);
133 LOAD_PIECES(all_pieces[PIECE_A_FILLED],a,f);
134 LOAD_PIECES(all_pieces[PIECE_B_HOLLOW],b,h);
135 LOAD_PIECES(all_pieces[PIECE_B_FILLED],b,f);
142 read_screen (Display *dpy, Window window, int *widthP, int *heightP)
145 XWindowAttributes xgwa;
148 XGetWindowAttributes (dpy, window, &xgwa);
149 *widthP = xgwa.width;
150 *heightP = xgwa.height;
152 XClearWindow(dpy, window);
153 grab_screen_image(xgwa.screen, window);
154 p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
155 gcv.function = GXcopy;
156 gc = XCreateGC (dpy, window, GCFunction, &gcv);
157 XCopyArea (dpy, window, p, gc, 0, 0, *widthP, *heightP, 0, 0);
165 static int width, height;
166 static int x_border, y_border;
167 static Pixmap source;
171 static XPoint *state = 0;
174 jigsaw_init(Display *dpy, Window window)
176 XWindowAttributes xgwa;
180 int source_w, source_h;
184 source = read_screen (dpy, window, &source_w, &source_h);
186 XGetWindowAttributes (dpy, window, &xgwa);
187 cmap = xgwa.colormap;
188 width = xgwa.width / GRID_WIDTH;
189 height = xgwa.height / GRID_HEIGHT;
190 x_border = (xgwa.width - (width * GRID_WIDTH)) / 2;
191 y_border = (xgwa.height - (height * GRID_WIDTH)) / 2;
193 if (width < 4 || height < 4)
195 fprintf (stderr, "%s: window too small: %dx%d (need at least %dx%d)\n",
196 progname, xgwa.width, xgwa.height,
197 GRID_WIDTH * 4, GRID_HEIGHT * 4);
202 state = (XPoint *) malloc(width * height * sizeof(XPoint));
203 gc = XCreateGC (dpy, window, 0, &gcv);
207 char *fgs = get_string_resource("foreground", "Foreground");
208 char *bgs = get_string_resource("background", "Background");
210 if (!XParseColor (dpy, cmap, fgs, &fgc))
211 XParseColor (dpy, cmap, "gray", &fgc);
212 if (!XParseColor (dpy, cmap, bgs, &bgc))
213 XParseColor (dpy, cmap, "black", &bgc);
215 fg_ok = XAllocColor (dpy, cmap, &fgc);
216 bg_ok = XAllocColor (dpy, cmap, &bgc);
218 /* If we weren't able to allocate the two colors we want from the
219 colormap (which is likely if the screen has been grabbed on an
220 8-bit SGI visual -- don't ask) then just go through the map
221 and find the closest color to the ones we wanted, and use those
222 pixels without actually allocating them.
237 unsigned long fgd = ~0;
238 unsigned long bgd = ~0;
239 int max = visual_cells (xgwa.screen, xgwa.visual);
240 XColor *all = (XColor *) calloc(sizeof (*all), max);
241 for (i = 0; i < max; i++)
243 all[i].flags = DoRed|DoGreen|DoBlue;
246 XQueryColors (dpy, cmap, all, max);
247 for(i = 0; i < max; i++)
253 rd = (all[i].red >> 8) - (fgc.red >> 8);
254 gd = (all[i].green >> 8) - (fgc.green >> 8);
255 bd = (all[i].blue >> 8) - (fgc.blue >> 8);
256 if (rd < 0) rd = -rd;
257 if (gd < 0) gd = -gd;
258 if (bd < 0) bd = -bd;
259 d = (rd << 1) + (gd << 2) + bd;
271 rd = (all[i].red >> 8) - (bgc.red >> 8);
272 gd = (all[i].green >> 8) - (bgc.green >> 8);
273 bd = (all[i].blue >> 8) - (bgc.blue >> 8);
274 if (rd < 0) rd = -rd;
275 if (gd < 0) gd = -gd;
276 if (bd < 0) bd = -bd;
277 d = (rd << 1) + (gd << 2) + bd;
294 /* Reset the window's background color... */
295 XSetWindowBackground (dpy, window, bg);
296 XClearWindow(dpy, window);
298 for (y = 0; y < height; y++)
299 for (x = 0; x < width; x++)
301 state[y * width + x].x = x;
302 state[y * width + x].y = y;
308 get_piece(int x, int y, struct piece **hollow, struct piece **filled)
311 Bool which = (x & 1) == (y & 1);
313 if (x == 0 && y == 0) p = NORTHWEST;
314 else if (x == width-1 && y == 0) p = NORTHEAST;
315 else if (x == width-1 && y == height-1) p = SOUTHEAST;
316 else if (x == 0 && y == height-1) p = SOUTHWEST;
317 else if (y == 0) p = NORTH;
318 else if (x == width-1) p = EAST;
319 else if (y == height-1) p = SOUTH;
320 else if (x == 0) p = WEST;
323 if (tweak) which = !which;
326 ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
327 : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
330 ? &all_pieces[PIECE_A_FILLED].pieces[p]
331 : &all_pieces[PIECE_B_FILLED].pieces[p]);
336 draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
338 struct piece *hollow, *filled;
339 int from_x = state[y * width + x].x;
340 int from_y = state[y * width + x].y;
342 get_piece(x, y, &hollow, &filled);
344 XSetClipMask(dpy, gc, filled->pixmap);
345 XSetClipOrigin(dpy, gc,
346 x_border + (x * GRID_WIDTH) - filled->x - 1,
347 y_border + (y * GRID_WIDTH) - filled->y - 1);
351 XSetForeground(dpy, gc, bg);
352 XFillRectangle(dpy, window, gc,
353 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
354 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
355 GRID_WIDTH*2, GRID_HEIGHT*2);
358 XCopyArea(dpy, source, window, gc,
359 x_border + (from_x * GRID_WIDTH) - GRID_WIDTH/2,
360 y_border + (from_y * GRID_HEIGHT) - GRID_HEIGHT/2,
361 GRID_WIDTH*2, GRID_HEIGHT*2,
362 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
363 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2);
368 XSetForeground(dpy, gc, fg);
369 XSetClipMask(dpy, gc, hollow->pixmap);
370 XSetClipOrigin(dpy, gc,
371 x_border + (x * GRID_WIDTH) - hollow->x - 1,
372 y_border + (y * GRID_WIDTH) - hollow->y - 1);
373 XFillRectangle(dpy, window, gc,
374 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
375 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
376 GRID_WIDTH*2, GRID_HEIGHT*2);
380 /* If the pieces lined up right, we could do this by just not drawing
381 the outline -- but that doesn't look right, since it eats the outlines
382 of the adjascent pieces. So draw the outline, then chop off the outer
383 edge if this is a border piece.
385 XSetForeground(dpy, gc, bg);
387 XFillRectangle(dpy, window, gc,
389 y_border + (y * GRID_HEIGHT),
391 else if (x == width-1)
392 XFillRectangle(dpy, window, gc,
393 x_border + ((x+1) * GRID_WIDTH) - 2,
394 y_border + (y * GRID_HEIGHT),
398 XFillRectangle(dpy, window, gc,
399 x_border + (x * GRID_WIDTH),
402 else if (y == height-1)
403 XFillRectangle(dpy, window, gc,
404 x_border + (x * GRID_WIDTH),
405 y_border + ((y+1) * GRID_HEIGHT) - 2,
412 swap_pieces(Display *dpy, Window window,
413 int src_x, int src_y, int dst_x, int dst_y,
419 for (i = 0; i < 3; i++)
421 draw_piece(dpy, window, src_x, src_y, 1);
422 draw_piece(dpy, window, dst_x, dst_y, 1);
425 draw_piece(dpy, window, src_x, src_y, 0);
426 draw_piece(dpy, window, dst_x, dst_y, 0);
431 swap = state[src_y * width + src_x];
432 state[src_y * width + src_x] = state[dst_y * width + dst_x];
433 state[dst_y * width + dst_x] = swap;
437 draw_piece(dpy, window, src_x, src_y, 0);
438 draw_piece(dpy, window, dst_x, dst_y, 0);
445 shuffle(Display *dpy, Window window, Bool draw_p)
447 struct piece *p1, *p2;
448 int src_x, src_y, dst_x = -1, dst_y = -1;
452 src_x = random() % width;
453 src_y = random() % height;
455 get_piece(src_x, src_y, &p1, 0);
457 /* Pick random coordinates until we find one that has the same kind of
458 piece as the first one we picked. Note that it's possible for there
459 to be only one piece of a particular shape on the board (this commonly
460 happens with the corner pieces.)
464 dst_x = random() % width;
465 dst_y = random() % height;
466 get_piece(dst_x, dst_y, &p2, 0);
469 if (src_x == dst_x && src_y == dst_y)
472 swap_pieces(dpy, window, src_x, src_y, dst_x, dst_y, draw_p);
477 shuffle_all(Display *dpy, Window window)
479 int i = (width * height * 10);
482 shuffle(dpy, window, False);
488 unshuffle(Display *dpy, Window window)
491 for (i = 0; i < width * height * 4; i++)
493 int x = random() % width;
494 int y = random() % height;
495 int x2 = state[y * width + x].x;
496 int y2 = state[y * width + x].y;
497 if (x != x2 || y != y2)
499 swap_pieces(dpy, window, x, y, x2, y2, True);
506 clear_all(Display *dpy, Window window)
508 int n = width * height;
511 int x = random() % width;
512 int y = random() % height;
513 XPoint *p = &state[y * width + x];
516 draw_piece(dpy, window, p->x, p->y, 2);
528 for (y = 0; y < height; y++)
529 for (x = 0; x < width; x++)
531 int x2 = state[y * width + x].x;
532 int y2 = state[y * width + x].y;
533 if (x != x2 || y != y2)
541 char *progclass = "Jigsaw";
543 char *defaults [] = {
544 ".background: Black",
545 ".foreground: Gray40",
548 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
554 XrmOptionDescRec options [] = {
555 { "-delay", ".delay", XrmoptionSepArg, 0 },
556 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
561 screenhack (Display *dpy, Window window)
563 int delay = get_integer_resource("delay", "Integer");
564 int delay2 = get_integer_resource("delay2", "Integer");
566 init_images(dpy, window);
571 jigsaw_init (dpy, window);
572 shuffle_all(dpy, window);
574 for (y = 0; y < height; y++)
575 for (x = 0; x < width; x++)
576 draw_piece(dpy, window, x, y, 0);
580 unshuffle(dpy, window);
582 screenhack_handle_events (dpy);
583 if (delay) usleep (delay);
586 screenhack_handle_events (dpy);
588 usleep (delay2 * 1000000);
590 clear_all(dpy, window);