+++ /dev/null
-/* xscreensaver, Copyright (c) 1997, 1998 Jamie Zawinski <jwz@jwz.org>
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- */
-
-/*
- TODO:
-
- = Rather than just flickering the pieces before swapping them,
- show them lifting up and moving to their new positions.
- The path on which they move shouldn't be a straight line;
- try to avoid having them cross each other by moving them in
- oppositely-positioned arcs.
-
- = Rotate the pieces as well, so that we can swap the corner
- and edge pieces with each other.
-
- = The shapes of the piece bitmaps still aren't quite right.
- They should line up with no overlap. They don't...
-
- = Have it drop all pieces to the "floor" then pick them up to
- reassemble the picture.
-
- = As a joke, maybe sometimes have one piece that doesn't fit?
- Or lose a piece?
- */
-
-#include "screenhack.h"
-
-#define DEBUG
-
-#include "images/jigsaw/jigsaw_a_h.xbm"
-#include "images/jigsaw/jigsaw_a_n_h.xbm"
-#include "images/jigsaw/jigsaw_a_ne_h.xbm"
-#include "images/jigsaw/jigsaw_a_e_h.xbm"
-#include "images/jigsaw/jigsaw_a_se_h.xbm"
-#include "images/jigsaw/jigsaw_a_s_h.xbm"
-#include "images/jigsaw/jigsaw_a_sw_h.xbm"
-#include "images/jigsaw/jigsaw_a_w_h.xbm"
-#include "images/jigsaw/jigsaw_a_nw_h.xbm"
-
-#include "images/jigsaw/jigsaw_b_h.xbm"
-#include "images/jigsaw/jigsaw_b_n_h.xbm"
-#include "images/jigsaw/jigsaw_b_ne_h.xbm"
-#include "images/jigsaw/jigsaw_b_e_h.xbm"
-#include "images/jigsaw/jigsaw_b_se_h.xbm"
-#include "images/jigsaw/jigsaw_b_s_h.xbm"
-#include "images/jigsaw/jigsaw_b_sw_h.xbm"
-#include "images/jigsaw/jigsaw_b_w_h.xbm"
-#include "images/jigsaw/jigsaw_b_nw_h.xbm"
-
-#include "images/jigsaw/jigsaw_a_f.xbm"
-#include "images/jigsaw/jigsaw_a_n_f.xbm"
-#include "images/jigsaw/jigsaw_a_ne_f.xbm"
-#include "images/jigsaw/jigsaw_a_e_f.xbm"
-#include "images/jigsaw/jigsaw_a_se_f.xbm"
-#include "images/jigsaw/jigsaw_a_s_f.xbm"
-#include "images/jigsaw/jigsaw_a_sw_f.xbm"
-#include "images/jigsaw/jigsaw_a_w_f.xbm"
-#include "images/jigsaw/jigsaw_a_nw_f.xbm"
-
-#include "images/jigsaw/jigsaw_b_f.xbm"
-#include "images/jigsaw/jigsaw_b_n_f.xbm"
-#include "images/jigsaw/jigsaw_b_ne_f.xbm"
-#include "images/jigsaw/jigsaw_b_e_f.xbm"
-#include "images/jigsaw/jigsaw_b_se_f.xbm"
-#include "images/jigsaw/jigsaw_b_s_f.xbm"
-#include "images/jigsaw/jigsaw_b_sw_f.xbm"
-#include "images/jigsaw/jigsaw_b_w_f.xbm"
-#include "images/jigsaw/jigsaw_b_nw_f.xbm"
-
-#define GRID_WIDTH 66
-#define GRID_HEIGHT 66
-
-#define CENTER 0
-#define NORTH 1
-#define NORTHEAST 2
-#define EAST 3
-#define SOUTHEAST 4
-#define SOUTH 5
-#define SOUTHWEST 6
-#define WEST 7
-#define NORTHWEST 8
-
-struct piece {
- int width, height;
- int x, y;
- Pixmap pixmap;
-};
-
-struct set {
- struct piece pieces[9];
-};
-
-#define PIECE_A_HOLLOW 0
-#define PIECE_A_FILLED 1
-#define PIECE_B_HOLLOW 2
-#define PIECE_B_FILLED 3
-
-static struct set all_pieces[4];
-
-static void
-init_images(Display *dpy, Window window)
-{
-# define LOAD_PIECE(PIECE,NAME) \
- PIECE.x = jigsaw_##NAME##_x_hot; \
- PIECE.y = jigsaw_##NAME##_y_hot; \
- PIECE.pixmap = \
- XCreatePixmapFromBitmapData(dpy, window, \
- (char *) jigsaw_##NAME##_bits, \
- jigsaw_##NAME##_width, \
- jigsaw_##NAME##_height, \
- 1, 0, 1)
-
-# define LOAD_PIECES(SET,PREFIX,SUFFIX) \
- LOAD_PIECE(SET.pieces[CENTER], PREFIX##_##SUFFIX); \
- LOAD_PIECE(SET.pieces[NORTH], PREFIX##_n_##SUFFIX); \
- LOAD_PIECE(SET.pieces[NORTHEAST], PREFIX##_ne_##SUFFIX); \
- LOAD_PIECE(SET.pieces[EAST], PREFIX##_e_##SUFFIX); \
- LOAD_PIECE(SET.pieces[SOUTHEAST], PREFIX##_se_##SUFFIX); \
- LOAD_PIECE(SET.pieces[SOUTH], PREFIX##_s_##SUFFIX); \
- LOAD_PIECE(SET.pieces[SOUTHWEST], PREFIX##_sw_##SUFFIX); \
- LOAD_PIECE(SET.pieces[WEST], PREFIX##_w_##SUFFIX); \
- LOAD_PIECE(SET.pieces[NORTHWEST], PREFIX##_nw_##SUFFIX)
-
- LOAD_PIECES(all_pieces[PIECE_A_HOLLOW],a,h);
- LOAD_PIECES(all_pieces[PIECE_A_FILLED],a,f);
- LOAD_PIECES(all_pieces[PIECE_B_HOLLOW],b,h);
- LOAD_PIECES(all_pieces[PIECE_B_FILLED],b,f);
-
-# undef LOAD_PIECE
-# undef LOAD_PIECES
-}
-
-static Pixmap
-read_screen (Display *dpy, Window window, int *widthP, int *heightP)
-{
- Pixmap p;
- XWindowAttributes xgwa;
- XGCValues gcv;
- GC gc;
- XGetWindowAttributes (dpy, window, &xgwa);
- *widthP = xgwa.width;
- *heightP = xgwa.height;
-
- XClearWindow(dpy, window);
- grab_screen_image(xgwa.screen, window);
- p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
- gcv.function = GXcopy;
- gc = XCreateGC (dpy, window, GCFunction, &gcv);
- XCopyArea (dpy, window, p, gc, 0, 0, *widthP, *heightP, 0, 0);
-
- XFreeGC (dpy, gc);
-
- return p;
-}
-
-
-static int width, height;
-static int x_border, y_border;
-static Pixmap source;
-static GC gc;
-static Bool tweak;
-static int fg, bg;
-static XPoint *state = 0;
-
-static void
-jigsaw_init(Display *dpy, Window window)
-{
- XWindowAttributes xgwa;
- int x, y;
- XGCValues gcv;
- Colormap cmap;
- int source_w, source_h;
-
- tweak = random()&1;
-
- source = read_screen (dpy, window, &source_w, &source_h);
-
- XGetWindowAttributes (dpy, window, &xgwa);
- cmap = xgwa.colormap;
- width = xgwa.width / GRID_WIDTH;
- height = xgwa.height / GRID_HEIGHT;
- x_border = (xgwa.width - (width * GRID_WIDTH)) / 2;
- y_border = (xgwa.height - (height * GRID_WIDTH)) / 2;
-
- if (!state)
- state = (XPoint *) malloc(width * height * sizeof(XPoint));
- gc = XCreateGC (dpy, window, 0, &gcv);
-
- {
- XColor fgc, bgc;
- char *fgs = get_string_resource("foreground", "Foreground");
- char *bgs = get_string_resource("background", "Background");
- Bool fg_ok, bg_ok;
- if (!XParseColor (dpy, cmap, fgs, &fgc))
- XParseColor (dpy, cmap, "gray", &fgc);
- if (!XParseColor (dpy, cmap, bgs, &bgc))
- XParseColor (dpy, cmap, "black", &bgc);
-
- fg_ok = XAllocColor (dpy, cmap, &fgc);
- bg_ok = XAllocColor (dpy, cmap, &bgc);
-
- /* If we weren't able to allocate the two colors we want from the
- colormap (which is likely if the screen has been grabbed on an
- 8-bit SGI visual -- don't ask) then just go through the map
- and find the closest color to the ones we wanted, and use those
- pixels without actually allocating them.
- */
- if (fg_ok)
- fg = fgc.pixel;
- else
- fg = 0;
-
- if (bg_ok)
- bg = bgc.pixel;
- else
- bg = 1;
-
- if (!fg_ok || bg_ok)
- {
- int i;
- unsigned long fgd = ~0;
- unsigned long bgd = ~0;
- int max = visual_cells (xgwa.screen, xgwa.visual);
- XColor *all = (XColor *) calloc(sizeof (*all), max);
- for (i = 0; i < max; i++)
- {
- all[i].flags = DoRed|DoGreen|DoBlue;
- all[i].pixel = i;
- }
- XQueryColors (dpy, cmap, all, max);
- for(i = 0; i < max; i++)
- {
- long rd, gd, bd;
- unsigned long d;
- if (!fg_ok)
- {
- rd = (all[i].red >> 8) - (fgc.red >> 8);
- gd = (all[i].green >> 8) - (fgc.green >> 8);
- bd = (all[i].blue >> 8) - (fgc.blue >> 8);
- if (rd < 0) rd = -rd;
- if (gd < 0) gd = -gd;
- if (bd < 0) bd = -bd;
- d = (rd << 1) + (gd << 2) + bd;
- if (d < fgd)
- {
- fgd = d;
- fg = all[i].pixel;
- if (d == 0)
- fg_ok = True;
- }
- }
-
- if (!bg_ok)
- {
- rd = (all[i].red >> 8) - (bgc.red >> 8);
- gd = (all[i].green >> 8) - (bgc.green >> 8);
- bd = (all[i].blue >> 8) - (bgc.blue >> 8);
- if (rd < 0) rd = -rd;
- if (gd < 0) gd = -gd;
- if (bd < 0) bd = -bd;
- d = (rd << 1) + (gd << 2) + bd;
- if (d < bgd)
- {
- bgd = d;
- bg = all[i].pixel;
- if (d == 0)
- bg_ok = True;
- }
- }
-
- if (fg_ok && bg_ok)
- break;
- }
- XFree(all);
- }
- }
-
- /* Reset the window's background color... */
- XSetWindowBackground (dpy, window, bg);
- XClearWindow(dpy, window);
-
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- {
- state[y * width + x].x = x;
- state[y * width + x].y = y;
- }
-}
-
-
-static void
-get_piece(int x, int y, struct piece **hollow, struct piece **filled)
-{
- int p;
- Bool which = (x & 1) == (y & 1);
-
- if (x == 0 && y == 0) p = NORTHWEST;
- else if (x == width-1 && y == 0) p = NORTHEAST;
- else if (x == width-1 && y == height-1) p = SOUTHEAST;
- else if (x == 0 && y == height-1) p = SOUTHWEST;
- else if (y == 0) p = NORTH;
- else if (x == width-1) p = EAST;
- else if (y == height-1) p = SOUTH;
- else if (x == 0) p = WEST;
- else p = CENTER;
-
- if (tweak) which = !which;
- if (hollow)
- *hollow = (which
- ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
- : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
- if (filled)
- *filled = (which
- ? &all_pieces[PIECE_A_FILLED].pieces[p]
- : &all_pieces[PIECE_B_FILLED].pieces[p]);
-}
-
-
-static void
-draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
-{
- struct piece *hollow, *filled;
- int from_x = state[y * width + x].x;
- int from_y = state[y * width + x].y;
-
- get_piece(x, y, &hollow, &filled);
-
- XSetClipMask(dpy, gc, filled->pixmap);
- XSetClipOrigin(dpy, gc,
- x_border + (x * GRID_WIDTH) - filled->x - 1,
- y_border + (y * GRID_WIDTH) - filled->y - 1);
-
- if (clear_p)
- {
- XSetForeground(dpy, gc, bg);
- XFillRectangle(dpy, window, gc,
- x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
- y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
- GRID_WIDTH*2, GRID_HEIGHT*2);
- }
- else
- XCopyArea(dpy, source, window, gc,
- x_border + (from_x * GRID_WIDTH) - GRID_WIDTH/2,
- y_border + (from_y * GRID_HEIGHT) - GRID_HEIGHT/2,
- GRID_WIDTH*2, GRID_HEIGHT*2,
- x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
- y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2);
-
- if (clear_p > 1)
- return;
-
- XSetForeground(dpy, gc, fg);
- XSetClipMask(dpy, gc, hollow->pixmap);
- XSetClipOrigin(dpy, gc,
- x_border + (x * GRID_WIDTH) - hollow->x - 1,
- y_border + (y * GRID_WIDTH) - hollow->y - 1);
- XFillRectangle(dpy, window, gc,
- x_border + (x * GRID_WIDTH) - GRID_WIDTH/2,
- y_border + (y * GRID_HEIGHT) - GRID_HEIGHT/2,
- GRID_WIDTH*2, GRID_HEIGHT*2);
-
- if (clear_p)
- {
- /* If the pieces lined up right, we could do this by just not drawing
- the outline -- but that doesn't look right, since it eats the outlines
- of the adjascent pieces. So draw the outline, then chop off the outer
- edge if this is a border piece.
- */
- XSetForeground(dpy, gc, bg);
- if (x == 0)
- XFillRectangle(dpy, window, gc,
- x_border - 2,
- y_border + (y * GRID_HEIGHT),
- 3, GRID_HEIGHT);
- else if (x == width-1)
- XFillRectangle(dpy, window, gc,
- x_border + ((x+1) * GRID_WIDTH) - 2,
- y_border + (y * GRID_HEIGHT),
- 3, GRID_HEIGHT);
-
- if (y == 0)
- XFillRectangle(dpy, window, gc,
- x_border + (x * GRID_WIDTH),
- y_border - 2,
- GRID_WIDTH, 3);
- else if (y == height-1)
- XFillRectangle(dpy, window, gc,
- x_border + (x * GRID_WIDTH),
- y_border + ((y+1) * GRID_HEIGHT) - 2,
- GRID_WIDTH, 3);
- }
-}
-
-
-static void
-swap_pieces(Display *dpy, Window window,
- int src_x, int src_y, int dst_x, int dst_y,
- Bool draw_p)
-{
- XPoint swap;
- int i;
- if (draw_p)
- for (i = 0; i < 3; i++)
- {
- draw_piece(dpy, window, src_x, src_y, 1);
- draw_piece(dpy, window, dst_x, dst_y, 1);
- XSync(dpy, False);
- usleep(50000);
- draw_piece(dpy, window, src_x, src_y, 0);
- draw_piece(dpy, window, dst_x, dst_y, 0);
- XSync(dpy, False);
- usleep(50000);
- }
-
- swap = state[src_y * width + src_x];
- state[src_y * width + src_x] = state[dst_y * width + dst_x];
- state[dst_y * width + dst_x] = swap;
-
- if (draw_p)
- {
- draw_piece(dpy, window, src_x, src_y, 0);
- draw_piece(dpy, window, dst_x, dst_y, 0);
- XSync(dpy, False);
- }
-}
-
-
-static void
-shuffle(Display *dpy, Window window, Bool draw_p)
-{
- struct piece *p1, *p2;
- int src_x, src_y, dst_x = -1, dst_y = -1;
-
- AGAIN:
- p1 = p2 = 0;
- src_x = random() % width;
- src_y = random() % height;
-
- get_piece(src_x, src_y, &p1, 0);
-
- /* Pick random coordinates until we find one that has the same kind of
- piece as the first one we picked. Note that it's possible for there
- to be only one piece of a particular shape on the board (this commonly
- happens with the corner pieces.)
- */
- while (p1 != p2)
- {
- dst_x = random() % width;
- dst_y = random() % height;
- get_piece(dst_x, dst_y, &p2, 0);
- }
-
- if (src_x == dst_x && src_y == dst_y)
- goto AGAIN;
-
- swap_pieces(dpy, window, src_x, src_y, dst_x, dst_y, draw_p);
-}
-
-
-static void
-shuffle_all(Display *dpy, Window window)
-{
- int i = (width * height * 10);
- while (i > 0)
- {
- shuffle(dpy, window, False);
- i--;
- }
-}
-
-static void
-unshuffle(Display *dpy, Window window)
-{
- int i;
- for (i = 0; i < width * height * 4; i++)
- {
- int x = random() % width;
- int y = random() % height;
- int x2 = state[y * width + x].x;
- int y2 = state[y * width + x].y;
- if (x != x2 || y != y2)
- {
- swap_pieces(dpy, window, x, y, x2, y2, True);
- break;
- }
- }
-}
-
-static void
-clear_all(Display *dpy, Window window)
-{
- int n = width * height;
- while (n > 0)
- {
- int x = random() % width;
- int y = random() % height;
- XPoint *p = &state[y * width + x];
- if (p->x == -1)
- continue;
- draw_piece(dpy, window, p->x, p->y, 2);
- XSync(dpy, False);
- usleep(1000);
- p->x = p->y = -1;
- n--;
- }
-}
-
-static Bool
-done(void)
-{
- int x, y;
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- {
- int x2 = state[y * width + x].x;
- int y2 = state[y * width + x].y;
- if (x != x2 || y != y2)
- return False;
- }
- return True;
-}
-
-
-\f
-char *progclass = "Jigsaw";
-
-char *defaults [] = {
- ".background: Black",
- ".foreground: Gray40",
- "*delay: 70000",
- "*delay2: 5",
- 0
-};
-
-XrmOptionDescRec options [] = {
- { "-delay", ".delay", XrmoptionSepArg, 0 },
- { "-delay2", ".delay2", XrmoptionSepArg, 0 },
- { 0, 0, 0, 0 }
-};
-
-void
-screenhack (Display *dpy, Window window)
-{
- int delay = get_integer_resource("delay", "Integer");
- int delay2 = get_integer_resource("delay2", "Integer");
-
- init_images(dpy, window);
-
- while (1)
- {
- int x, y;
- jigsaw_init (dpy, window);
- shuffle_all(dpy, window);
-
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- draw_piece(dpy, window, x, y, 0);
-
- while (!done())
- {
- unshuffle(dpy, window);
- XSync (dpy, False);
- screenhack_handle_events (dpy);
- if (delay) usleep (delay);
- }
-
- screenhack_handle_events (dpy);
- if (delay2)
- usleep (delay2 * 1000000);
-
- clear_all(dpy, window);
- }
-}