-/* xscreensaver, Copyright (c) 1997, 1998, 2001, 2003
- * Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1997-2006 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
= 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.
*/
#include "screenhack.h"
+#include "spline.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
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
#define CENTER 0
#define NORTH 1
#define PIECE_B_HOLLOW 2
#define PIECE_B_FILLED 3
-static struct set all_pieces[4];
+struct swap_state {
+ int flashing;
+ int x1, y1, x2, y2;
+ Bool draw_p;
+};
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ struct set all_pieces[4];
+
+ int piece_width, piece_height;
+ int width, height;
+ int x_border, y_border;
+ Pixmap source;
+ GC gc;
+ int fg, bg;
+ int border_width;
+ XPoint *state;
+ int delay, delay2;
+
+ int jigstate;
+ struct swap_state swap;
+ int clearing;
+
+ async_load_state *img_loader;
+};
+
+
+/* Returns a spline describing one edge of a puzzle piece of the given length.
+ */
+static spline *
+make_puzzle_curve (int pixels)
+{
+ double x0 = 0.0000, y0 = 0.0000;
+ double x1 = 0.3333, y1 = 0.1000;
+ double x2 = 0.4333, y2 = 0.0333;
+ double x3 = 0.4666, y3 = -0.0666;
+ double x4 = 0.3333, y4 = -0.1666;
+ double x5 = 0.3666, y5 = -0.2900;
+ double x6 = 0.5000, y6 = -0.3333;
+
+ spline *s = make_spline(20);
+ s->n_controls = 0;
+
+# define PT(x,y) \
+ s->control_x[s->n_controls] = pixels * (x); \
+ s->control_y[s->n_controls] = pixels * (y); \
+ s->n_controls++
+ PT ( x0, y0);
+ PT ( x1, y1);
+ PT ( x2, y2);
+ PT ( x3, y3);
+ PT ( x4, y4);
+ PT ( x5, y5);
+ PT ( x6, y6);
+ PT (1-x5, y5);
+ PT (1-x4, y4);
+ PT (1-x3, y3);
+ PT (1-x2, y2);
+ PT (1-x1, y1);
+ PT (1-x0, y0);
+# undef PT
+
+ compute_spline (s);
+ return s;
+}
+
+
+/* Draws a puzzle piece. The top/right/bottom/left_type args
+ indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
+ */
static void
-init_images(Display *dpy, Window window)
+draw_puzzle_shape (Display *dpy, Drawable d, GC gc,
+ int x, int y, int size, int bw,
+ int top_type, int right_type,
+ int bottom_type, int left_type,
+ Bool fill_p)
{
-# 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
+ spline *s = make_puzzle_curve (size);
+ XPoint *pts = (XPoint *) malloc (s->n_points * 4 * sizeof(*pts));
+ int i, o;
+
+ /* The border is twice as wide for "flat" edges, otherwise it looks funny. */
+ if (fill_p)
+ bw = 0;
+ else
+ bw /= 2;
+
+ o = 0;
+ if (top_type == 0) {
+ pts[o].x = x; pts[o].y = y + bw; o++;
+ pts[o].x = x + size; pts[o].y = y + bw; o++;
+ } else {
+ for (i = 0; i < s->n_points; i++) {
+ pts[o].x = x + s->points[i].x;
+ pts[o].y = y + s->points[i].y * top_type;
+ o++;
+ }
+ }
+
+ if (right_type == 0) {
+ pts[o-1].x -= bw;
+ pts[o].x = x + size - bw; pts[o].y = y + size; o++;
+ } else {
+ for (i = 1; i < s->n_points; i++) {
+ pts[o].x = x + size + s->points[i].y * (-right_type);
+ pts[o].y = y + s->points[i].x;
+ o++;
+ }
+ }
+
+ if (bottom_type == 0) {
+ pts[o-1].y -= bw;
+ pts[o].x = x; pts[o].y = y + size - bw; o++;
+ } else {
+ for (i = 1; i < s->n_points; i++) {
+ pts[o].x = x + s->points[s->n_points-i-1].x;
+ pts[o].y = y + size + s->points[s->n_points-i-1].y * (-bottom_type);
+ o++;
+ }
+ }
+
+ if (left_type == 0) {
+ pts[o-1].x += bw;
+ pts[o].x = x + bw; pts[o].y = y; o++;
+ } else {
+ for (i = 1; i < s->n_points; i++) {
+ pts[o].x = x + s->points[s->n_points-i-1].y * left_type;
+ pts[o].y = y + s->points[s->n_points-i-1].x;
+ o++;
+ }
+ }
+
+ free_spline (s);
+
+ if (fill_p)
+ XFillPolygon (dpy, d, gc, pts, o, Complex, CoordModeOrigin);
+ else
+ XDrawLines (dpy, d, gc, pts, o, CoordModeOrigin);
+
+ free (pts);
}
-static Pixmap
-read_screen (Display *dpy, Window window, int *widthP, int *heightP)
+
+/* Creates two pixmaps for a puzzle piece:
+ - The first is a solid bit-mask with 1 for each pixel inside the piece;
+ - The second is an outline of the piece, where all drawn pixels are
+ contained within the mask.
+
+ The top/right/bottom/left_type args indicate the direction the
+ tabs point: 1 for out, -1 for in, 0 for flat.
+
+ Size is how big the piece should be, from origin to origin.
+
+ Returned x/y is the origin within the pixmaps.
+ */
+static void
+make_puzzle_pixmap_pair (Display *dpy, Drawable d, int size, int bw,
+ int top_type, int right_type,
+ int bottom_type, int left_type,
+ int *x_ret, int *y_ret,
+ Pixmap *mask_ret, Pixmap *outline_ret)
{
- Pixmap p;
- XWindowAttributes xgwa;
- XGetWindowAttributes (dpy, window, &xgwa);
- *widthP = xgwa.width;
- *heightP = xgwa.height;
+ int w = size * 3;
+ int h = w;
+ int x = size;
+ int y = size;
+ Pixmap p0 = XCreatePixmap (dpy, d, w, h, 1);
+ Pixmap p1 = XCreatePixmap (dpy, d, w, h, 1);
+ XGCValues gcv;
+ GC gc;
+ gcv.foreground = 0;
+ gcv.background = 0;
+ gc = XCreateGC (dpy, p0, GCForeground|GCBackground, &gcv);
+ XFillRectangle (dpy, p0, gc, 0, 0, w, h);
+ XFillRectangle (dpy, p1, gc, 0, 0, w, h);
+ XSetForeground (dpy, gc, 0);
+
+# ifdef HAVE_COCOA
+ jwxyz_XSetAlphaAllowed (dpy, gc, False);
+# endif
+
+ /* To ensure that each pixel is drawn only once, we render the piece
+ such that it "owns" the left and top edges, but not the right and
+ bottom edges.
+
+ - - + "#" is this piece.
+ - # + It overlaps "-" and is overlapped by "+".
+ - + +
+
+ To accomplish this, we clear to black, draw "#" in white,
+ then draw "+" in black.
+ */
- p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
- XClearWindow(dpy, window);
- load_random_image (xgwa.screen, window, p);
- XClearWindow(dpy, window);
+ /* Center square */
+ XSetForeground (dpy, gc, 1);
+ draw_puzzle_shape (dpy, p0, gc, x, y, size, bw,
+ top_type, right_type, bottom_type, left_type,
+ True);
+
+ /* Top right square */
+ XSetForeground (dpy, gc, 0);
+ draw_puzzle_shape (dpy, p0, gc, x + size, y - size, size, bw,
+ 0, 0, -top_type, -left_type,
+ True);
+
+ /* Center right square */
+ draw_puzzle_shape (dpy, p0, gc, x + size, y, size, bw,
+ 0, 0, 0, -right_type,
+ True);
+
+ /* Bottom center square */
+ draw_puzzle_shape (dpy, p0, gc, x, y + size, size, bw,
+ -bottom_type, 0, 0, 0,
+ True);
+
+ /* And Charles Nelson Reilly in the bottom right square */
+ draw_puzzle_shape (dpy, p0, gc, x + size, y + size, size, bw,
+ -bottom_type, -right_type, 0, 0,
+ True);
+
+ /* Done with p0 (the mask).
+ To make p1 (the outline) draw an outlined piece through the mask.
+ */
+ if (bw < 0)
+ {
+ bw = size / 30;
+ if (bw < 1) bw = 1;
+ }
+
+ if (bw > 0)
+ {
+ XSetForeground (dpy, gc, 1);
+ XSetClipMask (dpy, gc, p0);
+ XSetLineAttributes (dpy, gc, bw, LineSolid, CapButt, JoinRound);
+ draw_puzzle_shape (dpy, p1, gc, x, y, size, bw,
+ top_type, right_type, bottom_type, left_type,
+ False);
+ }
- return p;
+ XFreeGC (dpy, gc);
+ *x_ret = x;
+ *y_ret = x;
+ *mask_ret = p0;
+ *outline_ret = p1;
}
-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
+make_puzzle_pixmaps (struct state *st)
+{
+ int i, j;
+
+ int edges[9][4] = {
+ { -1, 1, -1, 1 }, /* CENTER */
+ { 0, 1, -1, 1 }, /* NORTH */
+ { 0, 0, -1, 1 }, /* NORTHEAST */
+ { -1, 0, -1, 1 }, /* EAST */
+ { -1, 0, 0, 1 }, /* SOUTHEAST */
+ { -1, 1, 0, 1 }, /* SOUTH */
+ { -1, 1, 0, 0 }, /* SOUTHWEST */
+ { -1, 1, -1, 0 }, /* WEST */
+ { 0, 1, -1, 0 }, /* NORTHWEST */
+ };
+
+ /* sometimes swap direction of horizontal edges */
+ if (random() & 1)
+ for (j = 0; j < countof(edges); j++) {
+ edges[j][0] = -edges[j][0];
+ edges[j][2] = -edges[j][2];
+ }
+
+ /* sometimes swap direction of vertical edges */
+ if (random() & 1)
+ for (j = 0; j < countof(edges); j++) {
+ edges[j][1] = -edges[j][1];
+ edges[j][3] = -edges[j][3];
+ }
+
+ for (j = 0; j < 9; j++) {
+ for (i = 0; i < 2; i++) {
+ int x, y;
+ int top, right, bottom, left;
+ Pixmap mask, outline;
+ top = edges[j][0];
+ right = edges[j][1];
+ bottom = edges[j][2];
+ left = edges[j][3];
+ if (i) {
+ top = -top;
+ right = -right;
+ bottom = -bottom;
+ left = -left;
+ }
+ make_puzzle_pixmap_pair (st->dpy, st->window, st->piece_width,
+ st->border_width,
+ top, right, bottom, left,
+ &x, &y, &mask, &outline);
+
+ st->all_pieces[i*2].pieces[j].x = x;
+ st->all_pieces[i*2].pieces[j].y = y;
+ st->all_pieces[i*2].pieces[j].pixmap = outline;
+
+ st->all_pieces[i*2+1].pieces[j].x = x;
+ st->all_pieces[i*2+1].pieces[j].y = y;
+ st->all_pieces[i*2+1].pieces[j].pixmap = mask;
+ }
+ }
+}
+
+static void
+free_puzzle_pixmaps (struct state *st)
+{
+ int i, j;
+ for (i = 0; i < countof(st->all_pieces); i++)
+ for (j = 0; j < countof (st->all_pieces[i].pieces); j++)
+ if (st->all_pieces[i].pieces[j].pixmap) {
+ XFreePixmap (st->dpy, st->all_pieces[i].pieces[j].pixmap);
+ st->all_pieces[i].pieces[j].pixmap = 0;
+ }
+}
+
static void
-jigsaw_init(Display *dpy, Window window)
+jigsaw_init_1 (struct state *st)
{
XWindowAttributes xgwa;
int x, y;
XGCValues gcv;
Colormap cmap;
- int source_w, source_h;
- tweak = random()&1;
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+ st->piece_width = 40 + (random() % 100);
+ if (xgwa.width / st->piece_width < 4)
+ st->piece_width = xgwa.width / 4;
+ st->piece_height = st->piece_width;
- source = read_screen (dpy, window, &source_w, &source_h);
+ free_puzzle_pixmaps (st);
+ make_puzzle_pixmaps (st);
- 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;
+ st->width = xgwa.width / st->piece_width;
+ st->height = xgwa.height / st->piece_height;
+ st->x_border = (xgwa.width - (st->width * st->piece_width)) / 2;
+ st->y_border = (xgwa.height - (st->height * st->piece_width)) / 2;
- if (width < 4 || height < 4)
+ if (st->width < 4) st->width = 4, st->x_border = 0;
+ if (st->height < 4) st->height = 4, st->y_border = 0;
+
+ if (st->state) free (st->state);
+ st->state = (XPoint *) malloc (st->width * st->height * sizeof(*st->state));
+
+ if (!st->gc)
{
- fprintf (stderr, "%s: window too small: %dx%d (need at least %dx%d)\n",
- progname, xgwa.width, xgwa.height,
- GRID_WIDTH * 4, GRID_HEIGHT * 4);
- exit (1);
+ XColor fgc, bgc;
+ char *fgs = get_string_resource(st->dpy, "foreground", "Foreground");
+ char *bgs = get_string_resource(st->dpy, "background", "Background");
+ Bool fg_ok, bg_ok;
+
+ st->gc = XCreateGC (st->dpy, st->window, 0, &gcv);
+
+# ifdef HAVE_COCOA
+ jwxyz_XSetAlphaAllowed (st->dpy, st->gc, False);
+# endif
+
+ if (!XParseColor (st->dpy, cmap, fgs, &fgc))
+ XParseColor (st->dpy, cmap, "gray", &fgc);
+ if (!XParseColor (st->dpy, cmap, bgs, &bgc))
+ XParseColor (st->dpy, cmap, "black", &bgc);
+
+ free (fgs);
+ free (bgs);
+ fgs = bgs = 0;
+
+ fg_ok = XAllocColor (st->dpy, cmap, &fgc);
+ bg_ok = XAllocColor (st->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)
+ st->fg = fgc.pixel;
+ else
+ st->fg = 0;
+
+ if (bg_ok)
+ st->bg = bgc.pixel;
+ else
+ st->bg = 1;
+
+#ifndef HAVE_COCOA
+ 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 (st->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;
+ st->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;
+ st->bg = all[i].pixel;
+ if (d == 0)
+ bg_ok = True;
+ }
+ }
+
+ if (fg_ok && bg_ok)
+ break;
+ }
+ XFree(all);
+ }
+#endif /* HAVE_COCOA */
}
- 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;
+ /* Reset the window's background color... */
+ XSetWindowBackground (st->dpy, st->window, st->bg);
+ XClearWindow(st->dpy, st->window);
- if (!fg_ok || bg_ok)
+ for (y = 0; y < st->height; y++)
+ for (x = 0; x < st->width; x++)
{
- 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);
+ st->state[y * st->width + x].x = x;
+ st->state[y * st->width + x].y = y;
}
- }
- /* Reset the window's background color... */
- XSetWindowBackground (dpy, window, bg);
- XClearWindow(dpy, window);
+ if (st->source)
+ XFreePixmap (st->dpy, st->source);
+ st->source = XCreatePixmap (st->dpy, st->window, xgwa.width, xgwa.height,
+ xgwa.depth);
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- {
- state[y * width + x].x = x;
- state[y * width + x].y = y;
- }
+ st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
+ st->source, 0, 0);
}
static void
-get_piece(int x, int y, struct piece **hollow, struct piece **filled)
+get_piece (struct state *st,
+ 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 (x == 0 && y == 0) p = NORTHWEST;
+ else if (x == st->width-1 && y == 0) p = NORTHEAST;
+ else if (x == st->width-1 && y == st->height-1) p = SOUTHEAST;
+ else if (x == 0 && y == st->height-1) p = SOUTHWEST;
+ else if (y == 0) p = NORTH;
+ else if (x == st->width-1) p = EAST;
+ else if (y == st->height-1) p = SOUTH;
+ else if (x == 0) p = WEST;
+ else p = CENTER;
+
if (hollow)
*hollow = (which
- ? &all_pieces[PIECE_A_HOLLOW].pieces[p]
- : &all_pieces[PIECE_B_HOLLOW].pieces[p]);
+ ? &st->all_pieces[PIECE_A_HOLLOW].pieces[p]
+ : &st->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]);
+ ? &st->all_pieces[PIECE_A_FILLED].pieces[p]
+ : &st->all_pieces[PIECE_B_FILLED].pieces[p]);
}
static void
-draw_piece(Display *dpy, Window window, int x, int y, int clear_p)
+draw_piece (struct state *st, 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;
+ int from_x = st->state[y * st->width + x].x;
+ int from_y = st->state[y * st->width + x].y;
- get_piece(x, y, &hollow, &filled);
+ get_piece(st, 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);
+ XSetClipMask(st->dpy, st->gc, filled->pixmap);
+ XSetClipOrigin(st->dpy, st->gc,
+ st->x_border + (x * st->piece_width) - filled->x - 1,
+ st->y_border + (y * st->piece_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);
+ XSetForeground(st->dpy, st->gc, st->bg);
+ XFillRectangle(st->dpy, st->window, st->gc,
+ st->x_border + (x * st->piece_width) -st->piece_width/2,
+ st->y_border + (y * st->piece_height) -st->piece_height/2,
+ st->piece_width*2, st->piece_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);
+ XCopyArea(st->dpy, st->source, st->window, st->gc,
+ st->x_border + (from_x * st->piece_width) - st->piece_width/2,
+ st->y_border + (from_y * st->piece_height) - st->piece_height/2,
+ st->piece_width*2, st->piece_height*2,
+ st->x_border + (x * st->piece_width) - st->piece_width/2,
+ st->y_border + (y * st->piece_height) - st->piece_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);
- }
+ XSetForeground(st->dpy, st->gc, st->fg);
+ XSetClipMask(st->dpy, st->gc, hollow->pixmap);
+ XSetClipOrigin(st->dpy, st->gc,
+ st->x_border + (x * st->piece_width) - hollow->x - 1,
+ st->y_border + (y * st->piece_width) - hollow->y - 1);
+ XFillRectangle(st->dpy, st->window, st->gc,
+ st->x_border + (x * st->piece_width) - st->piece_width/2,
+ st->y_border + (y * st->piece_height) - st->piece_height/2,
+ st->piece_width*2, st->piece_height*2);
}
-static void
-swap_pieces(Display *dpy, Window window,
- int src_x, int src_y, int dst_x, int dst_y,
- Bool draw_p)
+static int
+animate_swap (struct state *st, struct swap_state *sw)
{
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 (sw->flashing > 1)
+ {
+ draw_piece(st, sw->x1, sw->y1, sw->flashing & 1);
+ draw_piece(st, sw->x2, sw->y2, sw->flashing & 1);
+ sw->flashing--;
+ return st->delay;
+ }
- if (draw_p)
+ swap = st->state[sw->y1 * st->width + sw->x1];
+ st->state[sw->y1 * st->width + sw->x1] =
+ st->state[sw->y2 * st->width + sw->x2];
+ st->state[sw->y2 * st->width + sw->x2] = swap;
+
+ if (sw->draw_p)
{
- draw_piece(dpy, window, src_x, src_y, 0);
- draw_piece(dpy, window, dst_x, dst_y, 0);
- XSync(dpy, False);
+ draw_piece(st, sw->x1, sw->y1, 0);
+ draw_piece(st, sw->x2, sw->y2, 0);
+ sw->flashing = 0;
}
+
+ return 0;
}
-static void
-shuffle(Display *dpy, Window window, Bool draw_p)
+static int
+swap_pieces (struct state *st,
+ int src_x, int src_y, int dst_x, int dst_y,
+ Bool draw_p)
+{
+ struct swap_state *sw = &st->swap;
+
+ sw->x1 = src_x;
+ sw->y1 = src_y;
+ sw->x2 = dst_x;
+ sw->y2 = dst_y;
+ sw->draw_p = draw_p;
+
+ /* if animating, plan to flash the pieces on and off a few times */
+ sw->flashing = sw->draw_p ? 7 : 0;
+
+ return animate_swap(st, sw);
+}
+
+
+static Bool
+done (struct state *st)
+{
+ int x, y;
+ for (y = 0; y < st->height; y++)
+ for (x = 0; x < st->width; x++)
+ {
+ int x2 = st->state[y * st->width + x].x;
+ int y2 = st->state[y * st->width + x].y;
+ if (x != x2 || y != y2)
+ return False;
+ }
+ return True;
+}
+
+
+static int
+shuffle (struct state *st, 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;
+ src_x = random() % st->width;
+ src_y = random() % st->height;
- get_piece(src_x, src_y, &p1, 0);
+ get_piece(st, 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.)
+ to be only one piece of a particular shape on the board (this always
+ happens with the four corner pieces.)
*/
while (p1 != p2)
{
- dst_x = random() % width;
- dst_y = random() % height;
- get_piece(dst_x, dst_y, &p2, 0);
+ dst_x = random() % st->width;
+ dst_y = random() % st->height;
+ get_piece(st, 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);
+ return swap_pieces(st, src_x, src_y, dst_x, dst_y, draw_p);
}
static void
-shuffle_all(Display *dpy, Window window)
+shuffle_all (struct state *st)
{
- int i = (width * height * 10);
- while (i > 0)
- {
- shuffle(dpy, window, False);
- i--;
- }
+ int j;
+ for (j = 0; j < 5; j++) {
+ /* swap each piece with another 5x */
+ int i = (st->width * st->height * 5);
+ while (--i > 0)
+ shuffle (st, False);
+
+ /* and do that whole process up to 5x if we ended up with a solved
+ board (this often happens with 4x4 boards.) */
+ if (!done(st))
+ break;
+ }
}
-static void
-unshuffle(Display *dpy, Window window)
+
+static int
+unshuffle (struct state *st)
{
int i;
- for (i = 0; i < width * height * 4; i++)
+ for (i = 0; i < st->width * st->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;
+ int x = random() % st->width;
+ int y = random() % st->height;
+ int x2 = st->state[y * st->width + x].x;
+ int y2 = st->state[y * st->width + x].y;
if (x != x2 || y != y2)
{
- swap_pieces(dpy, window, x, y, x2, y2, True);
- break;
+ return swap_pieces(st, x, y, x2, y2, True);
}
}
+ return 0;
}
-static void
-clear_all(Display *dpy, Window window)
+
+static int
+animate_clear (struct state *st)
{
- int n = width * height;
- while (n > 0)
+ while (st->clearing > 0)
{
- int x = random() % width;
- int y = random() % height;
- XPoint *p = &state[y * width + x];
+ int x = random() % st->width;
+ int y = random() % st->height;
+ XPoint *p = &st->state[y * st->width + x];
if (p->x == -1)
continue;
- draw_piece(dpy, window, p->x, p->y, 2);
- XSync(dpy, False);
- usleep(1000);
+ draw_piece(st, p->x, p->y, 2);
p->x = p->y = -1;
- n--;
+ st->clearing--;
+ return st->delay;
}
+ return 0;
}
-static Bool
-done(void)
+
+static int
+clear_all (struct state *st)
+{
+ st->clearing = st->width * st->height;
+ return animate_clear(st);
+}
+
+
+static void *
+jigsaw_init (Display *dpy, Window window)
{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ st->dpy = dpy;
+ st->window = window;
+ st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+ st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer") * 1000000;
+ st->border_width = get_integer_resource (st->dpy, "pieceBorderWidth",
+ "Integer");
+ if (st->delay == 0) st->delay = 1; /* kludge */
+ return st;
+}
+
+
+static unsigned long
+jigsaw_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
int x, y;
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
+ int delay = 0;
+
+ if (st->img_loader) /* still loading */
+ {
+ st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
+ if (! st->img_loader) { /* just finished */
+ shuffle_all (st);
+ for (y = 0; y < st->height; y++)
+ for (x = 0; x < st->width; x++)
+ draw_piece(st, x, y, 0);
+ }
+ return st->delay;
+ }
+
+ if (st->swap.flashing)
+ delay = animate_swap (st, &st->swap);
+ else if (st->clearing)
+ delay = animate_clear (st);
+
+ if (!delay) {
+ if (st->jigstate == 0)
{
- int x2 = state[y * width + x].x;
- int y2 = state[y * width + x].y;
- if (x != x2 || y != y2)
- return False;
+ jigsaw_init_1 (st);
+ st->jigstate = 1;
}
- return True;
+ else if (st->jigstate == 1)
+ {
+ if (done(st))
+ {
+ st->jigstate = 2;
+ delay = st->delay2;
+ }
+ else
+ {
+ delay = unshuffle(st);
+ }
+ }
+ else if (st->jigstate == 2)
+ {
+ st->jigstate = 0;
+ delay = clear_all(st);
+ }
+ else
+ abort();
+ }
+
+ if (delay == 1) delay = 0; /* kludge */
+ return (delay ? delay : st->delay * 10);
+}
+
+static void
+jigsaw_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ /* window size is checked each time a new puzzle begins */
}
+static Bool
+jigsaw_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+jigsaw_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free_puzzle_pixmaps (st);
+ if (st->state) free (st->state);
+ if (st->gc) XFreeGC (dpy, st->gc);
+ if (st->source) XFreePixmap (dpy, st->source);
+ free (st);
+}
\f
-char *progclass = "Jigsaw";
-char *defaults [] = {
+static const char *jigsaw_defaults [] = {
".background: Black",
- ".foreground: Gray40",
+ ".foreground: #AAAAAA",
"*delay: 70000",
"*delay2: 5",
+ "*pieceBorderWidth: -1",
#ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
"*visualID: Best",
#endif
0
};
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec jigsaw_options [] = {
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-delay2", ".delay2", XrmoptionSepArg, 0 },
+ { "-bw", ".pieceBorderWidth", XrmoptionSepArg, 0 },
+ { "-border-width", ".pieceBorderWidth", 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);
- }
-}
+XSCREENSAVER_MODULE ("Jigsaw", jigsaw)