1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* puzzle --- the familiar Sam Loyd puzzle */
3 
4 #if 0
5 static const char sccsid[] = "@(#)puzzle.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1995 by Heath Rice <rice@asl.dl.nec.com>.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 20-Mar-1998: Ideas from slip.c and eyes.c, problems with XGetImage when
27  *              image moved off screen (-inwindow or -debug).
28  * 10-May-1997: Compatible with xscreensaver
29  * 15-Mar-1996: cleaned up, NUMBERED compile-time switch is now broken.
30  * Feb-1996: combined with rastering.  Jouk Jansen <joukj AT hrem.nano.tudelft.nl>.
31  * Feb-1995: written.  Heath Rice <hrice@netcom.com>
32  */
33 
34 /*-
35  * Chops up the screen into squares and randomly slides them around
36  * like that game where you try to rearrange the little tiles in their
37  * original order.  After it scrambles the tiles for awhile, it reverses
38  * itself and puts them back like they started.  This mode looks the coolest
39  * if you have a picture on your background.
40  */
41 
42 #ifdef STANDALONE
43 #define MODE_puzzle
44 #define DEFAULTS "*delay: 10000 \n" \
45 	"*count: 250 \n" \
46 	"*ncolors: 64 \n" \
47 	"*bitmap: \n" \
48 
49 # define free_puzzle 0
50 # define reshape_puzzle 0
51 # define puzzle_handle_event 0
52 # define UNIFORM_COLORS
53 #include "xlockmore.h"		/* in xscreensaver distribution */
54 static XImage blogo =
55 {
56 	0, 0,			/* width, height */
57 	0, XYBitmap, 0,		/* xoffset, format, data */
58 	LSBFirst, 8,		/* byte-order, bitmap-unit */
59 	LSBFirst, 8, 1		/* bitmap-bit-order, bitmap-pad, depth */
60 };
61 #else /* STANDALONE */
62 #include "xlock.h"		/* in xlockmore distribution */
63 #include "color.h"
64 #include "iostuff.h"
65 #endif /* STANDALONE */
66 
67 #ifdef MODE_puzzle
68 
69 ENTRYPOINT ModeSpecOpt puzzle_opts =
70 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
71 
72 #ifdef USE_MODULES
73 ModStruct   puzzle_description =
74 {"puzzle", "init_puzzle", "draw_puzzle", "release_puzzle",
75  "init_puzzle", "init_puzzle", (char *) NULL, &puzzle_opts,
76  10000, 250, 1, 1, 64, 1.0, "",
77  "Shows a puzzle being scrambled and then solved", 0, NULL};
78 
79 #endif
80 
81 #ifdef STANDALONE
82 #define PUZZLE_WIDTH	xscreensaver_width
83 #define PUZZLE_HEIGHT	xscreensaver_height
84 #define PUZZLE_BITS	xscreensaver_bits
85 #include "bitmaps/xscreensaver.xbm"
86 #else
87 #define PUZZLE_WIDTH	image_width
88 #define PUZZLE_HEIGHT	image_height
89 #define PUZZLE_BITS	image_bits
90 #include "puzzle.xbm"
91 #endif
92 
93 #ifdef HAVE_XPM
94 #ifdef STANDALONE
95 #include <X11/xpm.h>
96 #define PUZZLE_NAME	xscreensaver
97 #include "pixmaps/xscreensaver.xpm"
98 #else
99 #define PUZZLE_NAME	image_name
100 #include "puzzle.xpm"
101 #endif
102 #define DEFAULT_XPM 1
103 #endif
104 
105 #define NOWAY 255
106 
107   /*int storedmoves, *moves, *position, space;  To keep track of puzzle */
108 typedef struct {
109 	Bool        painted;	/* For debouncing */
110 	int         excount;
111 	int        *fixbuff;
112 	XPoint      count, boxsize, windowsize;
113 	XPoint      usablewindow, offsetwindow;
114 	XPoint      randompos, randpos;
115 	unsigned long black;
116 	int         row, col, nextrow, nextcol, nextbox;
117 	int         incrementOfMove;
118 	int         lastbox;
119 	int         forward;
120 	int         prev;
121 	int         moves;
122 
123 	/* Delta move stuff */
124 	int         movingBox;
125 	Pixmap      bufferBox;
126 	int         cfactor, rfactor;
127 	int         Lp;
128 	int         cbs, cbw, rbs, rbw;
129 	int         lengthOfMove;
130 
131 #ifdef NUMBERED
132 	XImage     *image;
133 	GC          gc;
134 	/* Font stuff */
135 	int         done;
136 	int         ascent, fontWidth, fontHeight;
137 #else
138 	XImage     *logo;
139 	GC          backGC;
140 	Colormap    cmap;
141 	int         graphics_format;
142 #endif
143 } puzzlestruct;
144 
145 static puzzlestruct *puzzles = (puzzlestruct *) NULL;
146 
147 static void
free_stuff(Display * display,puzzlestruct * pp)148 free_stuff(Display * display, puzzlestruct * pp)
149 {
150 	if (pp->cmap != None) {
151 		XFreeColormap(display, pp->cmap);
152 		if (pp->backGC != None) {
153 			XFreeGC(display, pp->backGC);
154 			pp->backGC = None;
155 		}
156 		pp->cmap = None;
157 	} else
158 		pp->backGC = None;
159 }
160 
161 static void
free_puzzle_screen(Display * display,puzzlestruct * pp)162 free_puzzle_screen(Display * display, puzzlestruct * pp)
163 {
164 	if (pp == NULL) {
165 		return;
166 	}
167 	if (pp->fixbuff != NULL) {
168 		free(pp->fixbuff);
169 		pp->fixbuff = (int *) NULL;
170 	}
171 	if (pp->bufferBox != None) {
172 		XFreePixmap(display, pp->bufferBox);
173 		pp->bufferBox = None;
174 	}
175 	free_stuff(display, pp);
176 #ifndef STANDALONE
177 /* this used to work there I think */
178 
179 	if (pp->logo != None) {
180 		destroyImage(&pp->logo, &pp->graphics_format);
181 		pp->logo = None;
182 	}
183 #endif
184 	pp = NULL;
185 }
186 
187 #ifdef NUMBERED
188 extern XFontStruct *getFont(Display * display);
189 
190 #define font_height(f) (f->ascent + f->descent)
191 static XFontStruct *mode_font = None;
192 
193 static int
font_width(XFontStruct * font,char ch)194 font_width(XFontStruct * font, char ch)
195 {
196 	int         dummy;
197 	XCharStruct xcs;
198 
199 	(void) XTextExtents(font, &ch, 1, &dummy, &dummy, &dummy, &xcs);
200 	return xcs.width;
201 }
202 
203 static Bool
NumberScreen(ModeInfo * mi)204 NumberScreen(ModeInfo * mi)
205 {
206 	Display    *display = MI_DISPLAY(mi);
207 	Window      window = MI_WINDOW(mi);
208 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
209 
210 	if (mode_font == None)
211 		mode_font = getFont(display);
212 	if (!pp->done) {
213 		XGCValues   gcv;
214 
215 		pp->done = 1;
216 		gcv.font = mode_font->fid;
217 		gcv.graphics_exposures = False;
218 		gcv.foreground = MI_WHITE_PIXEL(mi);
219 		gcv.background = MI_BLACK_PIXEL(mi);
220 		if ((pp->gc = XCreateGC(display, window,
221 				GCForeground | GCBackground | GCGraphicsExposures | GCFont,
222 				&gcv)) == None) {
223 			free_puzzle_screen(display, pp);
224 			return False;
225 		}
226 		pp->ascent = mode_font->ascent;
227 		pp->fontHeight = font_height(mode_font);
228 		pp->fontWidth = font_width(mode_font, '5');
229 	}
230 	XSetForeground(display, pp->gc, MI_WHITE_PIXEL(mi));
231 
232 	{
233 		XPoint      pos, letter;
234 		int         count = 1, digitOffset = 1, temp, letterOffset;
235 		int         i, j, mult = pp->count.x * pp->count.y;
236 		char        buf[16];
237 
238 		letter.x = pp->boxsize.x / 2 - 3;
239 		letter.y = pp->boxsize.y / 2 + pp->ascent / 2 - 1;
240 		letterOffset = pp->fontWidth / 2;
241 		pos.y = 0;
242 		for (j = 0; j < pp->count.y; j++) {
243 			pos.x = 0;
244 			for (i = 0; i < pp->count.x; i++) {
245 				if (count < mult) {
246 					if (pp->boxsize.x > 2 * pp->fontWidth &&
247 					    pp->boxsize.y > pp->fontHeight) {
248 						(void) sprintf(buf, "%d", count);
249 						(void) XDrawString(display, window, pp->gc,
250 								   pos.x + letter.x - letterOffset * digitOffset +
251 							     pp->randompos.x,
252 								   pos.y + letter.y + pp->randompos.y, buf, digitOffset);
253 					}
254 					XDrawRectangle(display, window, pp->gc,
255 						       pos.x + 1 + pp->randompos.x, pos.y + 1 + pp->randompos.y,
256 					pp->boxsize.x - 3, pp->boxsize.y - 3);
257 					count++;
258 					digitOffset = 0;
259 					temp = count;
260 					while (temp >= 1) {
261 						temp /= 10;
262 						digitOffset++;
263 					}
264 				}
265 				pos.x += pp->boxsize.x;
266 			}
267 			pos.y += pp->boxsize.y;
268 		}
269 	}
270 	return True;
271 }
272 #endif
273 
274 static int
setupmove(ModeInfo * mi)275 setupmove(ModeInfo * mi)
276 {
277 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
278 
279 	if ((pp->prev == pp->excount) && (pp->excount > 0) && (pp->forward == 1)) {
280 		pp->lastbox = -1;
281 		pp->forward = 0;
282 		pp->prev--;
283 	} else if ((pp->prev == -1) && (pp->excount > 0) && (pp->forward == 0)) {
284 		pp->lastbox = -1;
285 		pp->forward = 1;
286 		pp->prev++;
287 	}
288 	if (pp->forward)
289 		pp->nextbox = NRAND(5);
290 	else
291 		pp->nextbox = pp->fixbuff[pp->prev];
292 
293 	switch (pp->nextbox) {
294 		case 0:
295 			if ((pp->row == 0) || (pp->lastbox == 2))
296 				pp->nextbox = NOWAY;
297 			else {
298 				pp->nextrow = pp->row - 1;
299 				pp->nextcol = pp->col;
300 			}
301 			break;
302 		case 1:
303 			if ((pp->col == pp->count.x - 1) || (pp->lastbox == 3))
304 				pp->nextbox = NOWAY;
305 			else {
306 				pp->nextrow = pp->row;
307 				pp->nextcol = pp->col + 1;
308 			}
309 			break;
310 		case 2:
311 			if ((pp->row == pp->count.y - 1) || (pp->lastbox == 0))
312 				pp->nextbox = NOWAY;
313 			else {
314 				pp->nextrow = pp->row + 1;
315 				pp->nextcol = pp->col;
316 			}
317 			break;
318 		case 3:
319 			if ((pp->col == 0) || (pp->lastbox == 1))
320 				pp->nextbox = NOWAY;
321 			else {
322 				pp->nextrow = pp->row;
323 				pp->nextcol = pp->col - 1;
324 			}
325 			break;
326 		default:
327 			pp->nextbox = NOWAY;
328 			break;
329 	}
330 
331 	if (pp->nextbox != NOWAY) {
332 		pp->lastbox = pp->nextbox;
333 		return True;
334 	} else
335 		return False;
336 }
337 
338 static void
setupmovedelta(ModeInfo * mi)339 setupmovedelta(ModeInfo * mi)
340 {
341 	Display    *display = MI_DISPLAY(mi);
342 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
343 
344 	if (pp->bufferBox != None) {
345 		XFreePixmap(display, pp->bufferBox);
346 		pp->bufferBox = None;
347 	}
348 	if ((pp->bufferBox = XCreatePixmap(display, MI_WINDOW(mi),
349 				 pp->boxsize.x, pp->boxsize.y, MI_DEPTH(mi))) == None) {
350 		free_puzzle_screen(display, pp);
351 		return;
352 	}
353 	XCopyArea(MI_DISPLAY(mi), MI_WINDOW(mi), pp->bufferBox, pp->backGC,
354 		  pp->nextcol * pp->boxsize.x + pp->randompos.x + 1,
355 		  pp->nextrow * pp->boxsize.y + pp->randompos.y + 1,
356 		  pp->boxsize.x - 2, pp->boxsize.y - 2, 0, 0);
357 	XFlush(MI_DISPLAY(mi));
358 
359 	if (pp->nextcol > pp->col) {
360 		pp->cfactor = -1;
361 		pp->cbs = pp->boxsize.x;
362 		pp->cbw = pp->incrementOfMove;
363 	} else if (pp->col > pp->nextcol) {
364 		pp->cfactor = 1;
365 		pp->cbs = -pp->incrementOfMove;
366 		pp->cbw = pp->incrementOfMove;
367 	} else {
368 		pp->cfactor = 0;
369 		pp->cbs = 0;
370 		pp->cbw = pp->boxsize.x;
371 	}
372 	if (pp->nextrow > pp->row) {
373 		pp->rfactor = -1;
374 		pp->rbs = pp->boxsize.y;
375 		pp->rbw = pp->incrementOfMove;
376 	} else if (pp->row > pp->nextrow) {
377 		pp->rfactor = 1;
378 		pp->rbs = -pp->incrementOfMove;
379 		pp->rbw = pp->incrementOfMove;
380 	} else {
381 		pp->rfactor = 0;
382 		pp->rbs = 0;
383 		pp->rbw = pp->boxsize.y;
384 	}
385 
386 	if (pp->cfactor == 0)
387 		pp->lengthOfMove = pp->boxsize.y;
388 	else if (pp->rfactor == 0)
389 		pp->lengthOfMove = pp->boxsize.x;
390 	else
391 		pp->lengthOfMove = MIN(pp->boxsize.x, pp->boxsize.y);
392 	pp->Lp = pp->incrementOfMove;
393 }
394 
395 static void
wrapupmove(ModeInfo * mi)396 wrapupmove(ModeInfo * mi)
397 {
398 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
399 
400 	if (pp->excount) {
401 		if (pp->forward) {
402 			pp->fixbuff[pp->prev] = (pp->nextbox + 2) % 4;
403 			pp->prev++;
404 		} else
405 			pp->prev--;
406 	}
407 }
408 
409 static void
wrapupmovedelta(ModeInfo * mi)410 wrapupmovedelta(ModeInfo * mi)
411 {
412 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
413 
414 	if (pp->bufferBox) {
415 
416 		XCopyArea(MI_DISPLAY(mi), pp->bufferBox, MI_WINDOW(mi), pp->backGC,
417 			  0, 0, pp->boxsize.x - 2, pp->boxsize.y - 2,
418 			  pp->col * pp->boxsize.x + pp->randompos.x + 1,
419 			  pp->row * pp->boxsize.y + pp->randompos.y + 1);
420 
421 		XFlush(MI_DISPLAY(mi));
422 
423 		pp->row = pp->nextrow;
424 		pp->col = pp->nextcol;
425 
426 		XFreePixmap(MI_DISPLAY(mi), pp->bufferBox);
427 		pp->bufferBox = None;
428 	}
429 }
430 
431 static int
moveboxdelta(ModeInfo * mi)432 moveboxdelta(ModeInfo * mi)
433 {
434 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
435 	int         cf = pp->nextcol * pp->boxsize.x +
436 	pp->Lp * pp->cfactor + pp->randompos.x;
437 	int         rf = pp->nextrow * pp->boxsize.y +
438 	pp->Lp * pp->rfactor + pp->randompos.y;
439 
440 	if (pp->Lp <= pp->lengthOfMove) {
441 		if (pp->bufferBox) {
442 			XCopyArea(MI_DISPLAY(mi), pp->bufferBox, MI_WINDOW(mi),
443 				  pp->backGC, 0, 0, pp->boxsize.x - 2, pp->boxsize.y - 2,
444 				  cf + 1, rf + 1);
445 			XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), pp->backGC,
446 				       cf + pp->cbs - 1, rf + pp->rbs - 1, pp->cbw + 2, pp->rbw + 2);
447 		}
448 		if ((pp->Lp + pp->incrementOfMove > pp->lengthOfMove) &&
449 		    (pp->Lp != pp->lengthOfMove))
450 			pp->Lp = pp->lengthOfMove - pp->incrementOfMove;
451 		pp->Lp += pp->incrementOfMove;
452 		return False;
453 	} else
454 		return True;
455 }
456 
457 static Bool
init_stuff(ModeInfo * mi)458 init_stuff(ModeInfo * mi)
459 {
460 	puzzlestruct *pp = &puzzles[MI_SCREEN(mi)];
461 #ifdef STANDALONE
462 #ifdef HAVE_XPM
463 	XpmAttributes attrib;
464 	attrib.visual = MI_VISUAL(mi);
465 	attrib.colormap = MI_COLORMAP(mi);
466 	attrib.depth = MI_DEPTH(mi);
467 	attrib.valuemask = XpmVisual | XpmColormap | XpmDepth;
468 	if (MI_NPIXELS(mi) > 2 &&
469 			(XpmSuccess == XpmCreateImageFromData(MI_DISPLAY(mi),
470 			PUZZLE_NAME, &(pp->logo), (XImage **) NULL, &attrib))) {
471 		pp->graphics_format = IS_XPM;
472 	} else
473 #endif
474 	{
475 
476 		int default_width = PUZZLE_WIDTH;
477 		int default_height = PUZZLE_HEIGHT;
478 		unsigned char * default_bits = PUZZLE_BITS;
479 		if (!blogo.data) {
480 			blogo.data = (char *) default_bits;
481 			blogo.width = default_width;
482 			blogo.height = default_height;
483 			blogo.bytes_per_line = (blogo.width + 7) / 8;
484 		}
485 		pp->logo = &blogo;
486 		pp->graphics_format = IS_XBM;
487 	}
488 #else
489 	Display    *display = MI_DISPLAY(mi);
490 
491 	if (pp->logo == None)
492 		getImage(mi, &pp->logo, PUZZLE_WIDTH, PUZZLE_HEIGHT, PUZZLE_BITS,
493 #ifdef HAVE_XPM
494 			 DEFAULT_XPM, PUZZLE_NAME,
495 #endif
496 			 &pp->graphics_format, &pp->cmap, &pp->black);
497 	if (pp->logo == None) {
498 		free_puzzle_screen(display, pp);
499 		return False;
500 	}
501 	if (pp->cmap != None) {
502 		Window      window = MI_WINDOW(mi);
503 		setColormap(display, window, pp->cmap, MI_IS_INWINDOW(mi));
504 		if (pp->backGC == None) {
505 			XGCValues   xgcv;
506 
507 			xgcv.background = pp->black;
508 			if ((pp->backGC = XCreateGC(display, window, GCBackground,
509 					 &xgcv)) == None) {
510 				free_puzzle_screen(display, pp);
511 				return False;
512 			}
513 		}
514 	} else
515 #endif /* STANDALONE */
516 	{
517 		pp->black = MI_BLACK_PIXEL(mi);
518 		pp->backGC = MI_GC(mi);
519 	}
520 	return True;
521 }
522 
523 ENTRYPOINT void
init_puzzle(ModeInfo * mi)524 init_puzzle(ModeInfo * mi)
525 {
526 	Display    *display = MI_DISPLAY(mi);
527 	Window      window = MI_WINDOW(mi);
528 	puzzlestruct *pp;
529 	int         x, y;
530 	XPoint      size;
531 
532 	MI_INIT(mi, puzzles);
533 	pp = &puzzles[MI_SCREEN(mi)];
534 
535 	if (pp->painted && pp->windowsize.x == MI_WIDTH(mi) &&
536 	    pp->windowsize.y == MI_HEIGHT(mi))
537 		return;		/* Debounce since refresh_puzzle is init_puzzle */
538 
539 #ifdef HAVE_XPM
540 	if (pp->graphics_format >= IS_XPM) {
541         	/* This is needed when another program changes the colormap. */
542         	free_puzzle_screen(display, pp);
543 	}
544 #endif
545 	if (!init_stuff(mi))
546 		return;
547 	pp->excount = MI_COUNT(mi);
548 	if (pp->excount < 0) {
549 		if (pp->fixbuff != NULL) {
550 			free(pp->fixbuff);
551 			pp->fixbuff = (int *) NULL;
552 		}
553 		pp->excount = NRAND(-pp->excount) + 1;
554 	}
555 	pp->lastbox = -1;
556 	pp->moves = 0;
557 	pp->movingBox = False;
558 
559 	pp->windowsize.x = MI_WIDTH(mi);
560 	pp->windowsize.y = MI_HEIGHT(mi);
561 	if (pp->windowsize.x < 7)
562 		pp->windowsize.x = 7;
563 	if (pp->windowsize.y < 7)
564 		pp->windowsize.y = 7;
565 	pp->forward = 1;
566 	pp->prev = 0;
567 	/* don't want any exposure events from XCopyArea */
568 	XSetGraphicsExposures(display, pp->backGC, False);
569 
570 	MI_CLEARWINDOWCOLORMAP(mi, pp->backGC, pp->black);
571 
572 	if (pp->logo == None) {
573 		size.x = pp->windowsize.x;
574 		size.y = pp->windowsize.y;
575 	} else {
576 		size.x = (pp->logo->width < pp->windowsize.x) ?
577 			pp->logo->width : pp->windowsize.x;
578 		size.y = (pp->logo->height < pp->windowsize.y) ?
579 			pp->logo->height : pp->windowsize.y;
580 	}
581 	pp->boxsize.y = NRAND(1 + size.y / 4) + 6;
582 	pp->boxsize.x = NRAND(1 + size.x / 4) + 6;
583 	if ((pp->boxsize.x > 4 * pp->boxsize.y) ||
584 	    pp->boxsize.y > 4 * pp->boxsize.x)
585 		pp->boxsize.x = pp->boxsize.y = 2 * MIN(pp->boxsize.x, pp->boxsize.y);
586 	pp->count.x = size.x / pp->boxsize.x;
587 	pp->count.y = size.y / pp->boxsize.y;
588 
589 	if (pp->bufferBox != None) {
590 		XFreePixmap(display, pp->bufferBox);
591 		pp->bufferBox = None;
592 	}
593 	pp->usablewindow.x = pp->count.x * pp->boxsize.x;
594 	pp->usablewindow.y = pp->count.y * pp->boxsize.y;
595 	pp->offsetwindow.x = (pp->windowsize.x - pp->usablewindow.x) / 2;
596 	pp->offsetwindow.y = (pp->windowsize.y - pp->usablewindow.y) / 2;
597 
598 	pp->incrementOfMove = MIN(pp->usablewindow.x, pp->usablewindow.y) / 20;
599 	pp->incrementOfMove = MAX(pp->incrementOfMove, 1);
600 
601 	if (pp->logo != None) {
602 		pp->randompos.x = NRAND(MAX((pp->windowsize.x - pp->logo->width),
603 					    2 * pp->offsetwindow.x + 1));
604 		pp->randompos.y = NRAND(MAX((pp->windowsize.y - pp->logo->height),
605 					    2 * pp->offsetwindow.y + 1));
606 		if (MI_NPIXELS(mi) <= 2)
607 			XSetForeground(display, pp->backGC, MI_WHITE_PIXEL(mi));
608 		else
609 			XSetForeground(display, pp->backGC, MI_PIXEL(mi, NRAND(MI_NPIXELS(mi))));
610 		(void) XPutImage(display, window, pp->backGC, pp->logo,
611 		(int) (NRAND(MAX(1, (pp->logo->width - pp->usablewindow.x)))),
612 				 (int) (NRAND(MAX(1, (pp->logo->height - pp->usablewindow.y)))),
613 				 pp->randompos.x, pp->randompos.y,
614 				 pp->usablewindow.x, pp->usablewindow.y);
615 		XSetForeground(display, pp->backGC, pp->black);
616 		for (x = 0; x <= pp->count.x; x++) {
617 			int         tempx = x * pp->boxsize.x;
618 
619 			XDrawLine(display, window, pp->backGC,
620 				  tempx + pp->randompos.x, pp->randompos.y,
621 				  tempx + pp->randompos.x, pp->usablewindow.y + pp->randompos.y);
622 			XDrawLine(display, window, pp->backGC,
623 				tempx + pp->randompos.x - 1, pp->randompos.y,
624 				  tempx + pp->randompos.x - 1, pp->usablewindow.y + pp->randompos.y);
625 		}
626 		for (y = 0; y <= pp->count.y; y++) {
627 			int         tempy = y * pp->boxsize.y;
628 
629 			XDrawLine(display, window, pp->backGC,
630 				  pp->randompos.x, tempy + pp->randompos.y,
631 				  pp->usablewindow.x + pp->randompos.x, tempy + pp->randompos.y);
632 			XDrawLine(display, window, pp->backGC,
633 				pp->randompos.x, tempy + pp->randompos.y - 1,
634 				  pp->usablewindow.x + pp->randompos.x, tempy + pp->randompos.y - 1);
635 		}
636 	}
637 #ifdef NUMBERED
638 	else {
639 		if (pp->image)
640 			(void) XDestroyImage(pp->image);
641 		pp->randompos.x = pp->offsetwindow.x;
642 		pp->randompos.y = pp->offsetwindow.y;
643 		if (!NumberScreen(mi)) {
644 			release_puzzles(mi);
645 			return;
646 		}
647 		if ((pp->image = XGetImage(display, window,
648 				      pp->offsetwindow.x, pp->offsetwindow.y,
649 				      pp->usablewindow.x, pp->usablewindow.y,
650 				      AllPlanes,
651 				 (MI_NPIXELS(mi) <= 2) ? XYPixmap : ZPixmap)) == None) {
652 			free_puzzle_screen(display, pp);
653 			return;
654 		}
655 	}
656 
657 	pp->row = pp->count.y - 1;
658 	pp->col = pp->count.x - 1;
659 #else
660 	pp->row = NRAND(pp->count.y);
661 	pp->col = NRAND(pp->count.x);
662 #endif
663 
664 	if ((pp->excount) && (pp->fixbuff == NULL))
665 		if ((pp->fixbuff = (int *) calloc(pp->excount,
666 				sizeof (int))) == NULL) {
667 			free_puzzle_screen(display, pp);
668 			return;
669 		}
670 	pp->painted = True;
671 }
672 
673 ENTRYPOINT void
draw_puzzle(ModeInfo * mi)674 draw_puzzle(ModeInfo * mi)
675 {
676 	puzzlestruct *pp;
677 
678 	if (puzzles == NULL)
679 		return;
680 	pp = &puzzles[MI_SCREEN(mi)];
681 	if (pp->fixbuff == NULL)
682 		return;
683 
684 	MI_IS_DRAWN(mi) = True;
685 	pp->painted = False;
686 	if (pp->movingBox) {
687 		if (moveboxdelta(mi)) {
688 			wrapupmovedelta(mi);
689 			wrapupmove(mi);
690 			pp->movingBox = False;
691 			if (pp->moves++ > 2 * MI_COUNT(mi))
692 				init_puzzle(mi);
693 		}
694 	} else {
695 		if (setupmove(mi)) {
696 			setupmovedelta(mi);
697 			pp->movingBox = True;
698 		}
699 	}
700 }
701 
702 ENTRYPOINT void
release_puzzle(ModeInfo * mi)703 release_puzzle(ModeInfo * mi)
704 {
705 	if (puzzles != NULL) {
706 		int         screen;
707 
708 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
709 			free_puzzle_screen(MI_DISPLAY(mi), &puzzles[screen]);
710 		free(puzzles);
711 		puzzles = (puzzlestruct *) NULL;
712 	}
713 #ifdef NUMBERED
714 	if (mode_font != None) {
715 		XFreeFont(MI_DISPLAY(mi), mode_font);
716 		mode_font = None;
717 	}
718 #endif
719 }
720 
721 XSCREENSAVER_MODULE ("Puzzle", puzzle)
722 
723 #endif /* MODE_puzzle */
724