1 /* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@netscape.com>
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 "pieces/puzzle_a_h.xbm"
39 #include "pieces/puzzle_a_n_h.xbm"
40 #include "pieces/puzzle_a_ne_h.xbm"
41 #include "pieces/puzzle_a_e_h.xbm"
42 #include "pieces/puzzle_a_se_h.xbm"
43 #include "pieces/puzzle_a_s_h.xbm"
44 #include "pieces/puzzle_a_sw_h.xbm"
45 #include "pieces/puzzle_a_w_h.xbm"
46 #include "pieces/puzzle_a_nw_h.xbm"
48 #include "pieces/puzzle_b_h.xbm"
49 #include "pieces/puzzle_b_n_h.xbm"
50 #include "pieces/puzzle_b_ne_h.xbm"
51 #include "pieces/puzzle_b_e_h.xbm"
52 #include "pieces/puzzle_b_se_h.xbm"
53 #include "pieces/puzzle_b_s_h.xbm"
54 #include "pieces/puzzle_b_sw_h.xbm"
55 #include "pieces/puzzle_b_w_h.xbm"
56 #include "pieces/puzzle_b_nw_h.xbm"
58 #include "pieces/puzzle_a_f.xbm"
59 #include "pieces/puzzle_a_n_f.xbm"
60 #include "pieces/puzzle_a_ne_f.xbm"
61 #include "pieces/puzzle_a_e_f.xbm"
62 #include "pieces/puzzle_a_se_f.xbm"
63 #include "pieces/puzzle_a_s_f.xbm"
64 #include "pieces/puzzle_a_sw_f.xbm"
65 #include "pieces/puzzle_a_w_f.xbm"
66 #include "pieces/puzzle_a_nw_f.xbm"
68 #include "pieces/puzzle_b_f.xbm"
69 #include "pieces/puzzle_b_n_f.xbm"
70 #include "pieces/puzzle_b_ne_f.xbm"
71 #include "pieces/puzzle_b_e_f.xbm"
72 #include "pieces/puzzle_b_se_f.xbm"
73 #include "pieces/puzzle_b_s_f.xbm"
74 #include "pieces/puzzle_b_sw_f.xbm"
75 #include "pieces/puzzle_b_w_f.xbm"
76 #include "pieces/puzzle_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 = puzzle_##NAME##_x_hot; \
113 PIECE.y = puzzle_##NAME##_y_hot; \
115 XCreatePixmapFromBitmapData(dpy, window, \
116 (char *) puzzle_##NAME##_bits, \
117 puzzle_##NAME##_width, \
118 puzzle_##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 puzzle_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;
194 state = (XPoint *) malloc(width * height * sizeof(XPoint));
195 gc = XCreateGC (dpy, window, 0, &gcv);
199 char *fgs = get_string_resource("foreground", "Foreground");
200 char *bgs = get_string_resource("background", "Background");
202 if (!XParseColor (dpy, cmap, fgs, &fgc))
203 XParseColor (dpy, cmap, "gray", &fgc);
204 if (!XParseColor (dpy, cmap, bgs, &bgc))
205 XParseColor (dpy, cmap, "black", &bgc);
207 fg_ok = XAllocColor (dpy, cmap, &fgc);
208 bg_ok = XAllocColor (dpy, cmap, &bgc);
210 /* If we weren't able to allocate the two colors we want from the
211 colormap (which is likely if the screen has been grabbed on an
212 8-bit SGI visual -- don't ask) then just go through the map
213 and find the closest color to the ones we wanted, and use those
214 pixels without actually allocating them.
229 unsigned long fgd = ~0;
230 unsigned long bgd = ~0;
231 int max = visual_cells (xgwa.screen, xgwa.visual);
232 XColor *all = (XColor *) calloc(sizeof (*all), max);
233 for (i = 0; i < max; i++)
235 all[i].flags = DoRed|DoGreen|DoBlue;
238 XQueryColors (dpy, cmap, all, max);
239 for(i = 0; i < max; i++)
245 rd = (all[i].red >> 8) - (fgc.red >> 8);
246 gd = (all[i].green >> 8) - (fgc.green >> 8);
247 bd = (all[i].blue >> 8) - (fgc.blue >> 8);
248 if (rd < 0) rd = -rd;
249 if (gd < 0) gd = -gd;
250 if (bd < 0) bd = -bd;
251 d = (rd << 1) + (gd << 2) + bd;
263 rd = (all[i].red >> 8) - (bgc.red >> 8);
264 gd = (all[i].green >> 8) - (bgc.green >> 8);
265 bd = (all[i].blue >> 8) - (bgc.blue >> 8);
266 if (rd < 0) rd = -rd;
267 if (gd < 0) gd = -gd;
268 if (bd < 0) bd = -bd;
269 d = (rd << 1) + (gd << 2) + bd;
286 /* Reset the window's background color... */
287 XSetWindowBackground (dpy, window, bg);
288 XClearWindow(dpy, window);
290 for (y = 0; y < height; y++)
291 for (x = 0; x < width; x++)
293 state[y * width + x].x = x;
294 state[y * width + x].y = y;
300 get_piece(int x, int y, struct piece **hollow, struct piece **filled)
303 Bool which = (x & 1) == (y & 1);
305 if (x == 0 && y == 0) p = NORTHWEST;
306 else if (x == width-1 && y == 0) p = NORTHEAST;
307 else if (x == width-1 && y == height-1) p = SOUTHEAST;
308 else if (x == 0 && y == height-1) p = SOUTHWEST;
309 else if (y == 0) p = NORTH;
310 else if (x == width-1) p = EAST;
311 else if (y == height-1) p = SOUTH;
312 else if (x == 0) p = WEST;
315 if (tweak) which = !which;
318 ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
319 : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
322 ? &all_pieces[PIECE_A_FILLED].pieces[p]
323 : &all_pieces[PIECE_B_FILLED].pieces[p]);
328 draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
330 struct piece *hollow, *filled;
331 int from_x = state[y * width + x].x;
332 int from_y = state[y * width + x].y;
334 get_piece(x, y, &hollow, &filled);
336 XSetClipMask(dpy, gc, filled->pixmap);
337 XSetClipOrigin(dpy, gc,
338 x_border + (x * GRID_WIDTH) - filled->x - 1,
339 y_border + (y * GRID_WIDTH) - filled->y - 1);
343 XSetForeground(dpy, gc, bg);
344 XFillRectangle(dpy, window, gc,
345 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
346 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
347 GRID_WIDTH*2, GRID_HEIGHT*2);
350 XCopyArea(dpy, source, window, gc,
351 x_border + (from_x * GRID_WIDTH) - GRID_WIDTH/2,
352 y_border + (from_y * GRID_HEIGHT) - GRID_HEIGHT/2,
353 GRID_WIDTH*2, GRID_HEIGHT*2,
354 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
355 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2);
360 XSetForeground(dpy, gc, fg);
361 XSetClipMask(dpy, gc, hollow->pixmap);
362 XSetClipOrigin(dpy, gc,
363 x_border + (x * GRID_WIDTH) - hollow->x - 1,
364 y_border + (y * GRID_WIDTH) - hollow->y - 1);
365 XFillRectangle(dpy, window, gc,
366 x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
367 y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
368 GRID_WIDTH*2, GRID_HEIGHT*2);
372 /* If the pieces lined up right, we could do this by just not drawing
373 the outline -- but that doesn't look right, since it eats the outlines
374 of the adjascent pieces. So draw the outline, then chop off the outer
375 edge if this is a border piece.
377 XSetForeground(dpy, gc, bg);
379 XFillRectangle(dpy, window, gc,
381 y_border + (y * GRID_HEIGHT),
383 else if (x == width-1)
384 XFillRectangle(dpy, window, gc,
385 x_border + ((x+1) * GRID_WIDTH) - 2,
386 y_border + (y * GRID_HEIGHT),
390 XFillRectangle(dpy, window, gc,
391 x_border + (x * GRID_WIDTH),
394 else if (y == height-1)
395 XFillRectangle(dpy, window, gc,
396 x_border + (x * GRID_WIDTH),
397 y_border + ((y+1) * GRID_HEIGHT) - 2,
404 swap_pieces(Display *dpy, Window window,
405 int src_x, int src_y, int dst_x, int dst_y,
411 for (i = 0; i < 3; i++)
413 draw_piece(dpy, window, src_x, src_y, 1);
414 draw_piece(dpy, window, dst_x, dst_y, 1);
417 draw_piece(dpy, window, src_x, src_y, 0);
418 draw_piece(dpy, window, dst_x, dst_y, 0);
423 swap = state[src_y * width + src_x];
424 state[src_y * width + src_x] = state[dst_y * width + dst_x];
425 state[dst_y * width + dst_x] = swap;
429 draw_piece(dpy, window, src_x, src_y, 0);
430 draw_piece(dpy, window, dst_x, dst_y, 0);
437 shuffle(Display *dpy, Window window, Bool draw_p)
439 struct piece *p1, *p2;
440 int src_x, src_y, dst_x = -1, dst_y = -1;
444 src_x = random() % width;
445 src_y = random() % height;
447 get_piece(src_x, src_y, &p1, 0);
449 /* Pick random coordinates until we find one that has the same kind of
450 piece as the first one we picked. Note that it's possible for there
451 to be only one piece of a particular shape on the board (this commonly
452 happens with the corner pieces.)
456 dst_x = random() % width;
457 dst_y = random() % height;
458 get_piece(dst_x, dst_y, &p2, 0);
461 if (src_x == dst_x && src_y == dst_y)
464 swap_pieces(dpy, window, src_x, src_y, dst_x, dst_y, draw_p);
469 shuffle_all(Display *dpy, Window window)
471 int i = (width * height * 10);
474 shuffle(dpy, window, False);
480 unshuffle(Display *dpy, Window window)
483 for (i = 0; i < width * height * 4; i++)
485 int x = random() % width;
486 int y = random() % height;
487 int x2 = state[y * width + x].x;
488 int y2 = state[y * width + x].y;
489 if (x != x2 || y != y2)
491 swap_pieces(dpy, window, x, y, x2, y2, True);
498 clear_all(Display *dpy, Window window)
500 int n = width * height;
503 int x = random() % width;
504 int y = random() % height;
505 XPoint *p = &state[y * width + x];
508 draw_piece(dpy, window, p->x, p->y, 2);
520 for (y = 0; y < height; y++)
521 for (x = 0; x < width; x++)
523 int x2 = state[y * width + x].x;
524 int y2 = state[y * width + x].y;
525 if (x != x2 || y != y2)
533 char *progclass = "Puzzle";
535 char *defaults [] = {
536 "Puzzle.background: Black", /* to placate SGI */
537 "Puzzle.foreground: Gray40",
543 XrmOptionDescRec options [] = {
544 { "-delay", ".delay", XrmoptionSepArg, 0 },
545 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
550 screenhack (Display *dpy, Window window)
552 int delay = get_integer_resource("delay", "Integer");
553 int delay2 = get_integer_resource("delay2", "Integer");
555 init_images(dpy, window);
560 puzzle_init (dpy, window);
561 shuffle_all(dpy, window);
563 for (y = 0; y < height; y++)
564 for (x = 0; x < width; x++)
565 draw_piece(dpy, window, x, y, 0);
569 unshuffle(dpy, window);
571 if (delay) usleep (delay);
575 usleep (delay2 * 1000000);
577 clear_all(dpy, window);