1 /******************************************************************************
4 * modified: [ 8-11-95 ] Ed James <james@mml.mmc.com>
5 * added fill of dead-end box to solve_maze while loop.
6 * modified: [ 3-7-93 ] Jamie Zawinski <jwz@netscape.com>
7 * added the XRoger logo, cleaned up resources, made
8 * grid size a parameter.
9 * modified: [ 3-3-93 ] Jim Randell <jmr@mddjmr.fc.hp.com>
10 * Added the colour stuff and integrated it with jwz's
11 * screenhack stuff. There's still some work that could
12 * be done on this, particularly allowing a resource to
13 * specify how big the squares are.
14 * modified: [ 10-4-88 ] Richard Hess ...!uunet!cimshop!rhess
15 * [ Revised primary execution loop within main()...
16 * [ Extended X event handler, check_events()...
17 * modified: [ 1-29-88 ] Dave Lemke lemke@sun.com
19 * [ Note the word "hacked" -- this is extremely ugly, but at
20 * [ least it does the job. NOT a good programming example
22 * original: [ 6/21/85 ] Martin Weiss Sun Microsystems [ SunView ]
24 ******************************************************************************
25 Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
29 Permission to use, copy, modify, and distribute this software and its
30 documentation for any purpose and without fee is hereby granted,
31 provided that the above copyright notice appear in all copies and that
32 both that copyright notice and this permission notice appear in
33 supporting documentation, and that the names of Sun or MIT not be
34 used in advertising or publicity pertaining to distribution of the
35 software without specific prior written permission. Sun and M.I.T.
36 make no representations about the suitability of this software for
37 any purpose. It is provided "as is" without any express or implied warranty.
39 SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
41 PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
42 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
43 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
44 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
45 OR PERFORMANCE OF THIS SOFTWARE.
46 *****************************************************************************/
48 #include "screenhack.h"
52 static int solve_delay, pre_solve_delay, post_solve_delay;
56 #include <X11/Xutil.h>
58 # include <X11/bitmaps/gray1>
60 # include "sys$common:[decw$include.bitmaps]gray1.xbm"
63 #define MAX_MAZE_SIZE_X 500
64 #define MAX_MAZE_SIZE_Y 500
66 #define MOVE_LIST_SIZE (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)
68 #define WALL_TOP 0x8000
69 #define WALL_RIGHT 0x4000
70 #define WALL_BOTTOM 0x2000
71 #define WALL_LEFT 0x1000
73 #define DOOR_IN_TOP 0x800
74 #define DOOR_IN_RIGHT 0x400
75 #define DOOR_IN_BOTTOM 0x200
76 #define DOOR_IN_LEFT 0x100
77 #define DOOR_IN_ANY 0xF00
79 #define DOOR_OUT_TOP 0x80
80 #define DOOR_OUT_RIGHT 0x40
81 #define DOOR_OUT_BOTTOM 0x20
82 #define DOOR_OUT_LEFT 0x10
84 #define START_SQUARE 0x2
85 #define END_SQUARE 0x1
90 #define get_random(x) (random() % (x))
92 static int logo_x, logo_y;
95 # define logo_width 128
96 # define logo_height 128
98 # include <X11/bitmaps/xlogo64>
99 # define logo_width xlogo64_width
100 # define logo_height xlogo64_height
101 # define logo_bits xlogo64_bits
104 static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];
110 } move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];
112 static int maze_size_x, maze_size_y;
113 static int sqnum, cur_sq_x, cur_sq_y, path_length;
114 static int start_x, start_y, start_dir, end_x, end_y, end_dir;
115 static int grid_width, grid_height;
119 static GC gc, cgc, tgc, logo_gc;
120 static Pixmap logo_map;
122 static int x = 0, y = 0, restart = 0, stop = 1, state = 1;
125 check_events (void) /* X event handler [ rhess ] */
134 switch (e.xbutton.button) {
140 if (state == 5) state = 4 ;
153 case ConfigureNotify:
158 XClearWindow (dpy, win);
172 set_maze_sizes (int width, int height)
174 maze_size_x = width / grid_width;
175 maze_size_y = height / grid_height;
180 initialize_maze (void) /* draw the surrounding wall and start/end squares */
182 register int i, j, wall;
184 /* initialize all squares */
185 for ( i=0; i<maze_size_x; i++) {
186 for ( j=0; j<maze_size_y; j++) {
192 for ( i=0; i<maze_size_x; i++ ) {
193 maze[i][0] |= WALL_TOP;
197 for ( j=0; j<maze_size_y; j++ ) {
198 maze[maze_size_x-1][j] |= WALL_RIGHT;
202 for ( i=0; i<maze_size_x; i++ ) {
203 maze[i][maze_size_y-1] |= WALL_BOTTOM;
207 for ( j=0; j<maze_size_y; j++ ) {
208 maze[0][j] |= WALL_LEFT;
211 /* set start square */
212 wall = get_random(4);
215 i = get_random(maze_size_x);
220 j = get_random(maze_size_y);
223 i = get_random(maze_size_x);
228 j = get_random(maze_size_y);
231 maze[i][j] |= START_SQUARE;
232 maze[i][j] |= ( DOOR_IN_TOP >> wall );
233 maze[i][j] &= ~( WALL_TOP >> wall );
245 i = get_random(maze_size_x);
250 j = get_random(maze_size_y);
253 i = get_random(maze_size_x);
258 j = get_random(maze_size_y);
261 maze[i][j] |= END_SQUARE;
262 maze[i][j] |= ( DOOR_OUT_TOP >> wall );
263 maze[i][j] &= ~( WALL_TOP >> wall );
269 if ((maze_size_x > 15) && (maze_size_y > 15))
271 int logow = 1 + logo_width / grid_width;
272 int logoh = 1 + logo_height / grid_height;
273 /* not closer than 3 grid units from a wall */
274 logo_x = get_random (maze_size_x - logow - 6) + 3;
275 logo_y = get_random (maze_size_y - logoh - 6) + 3;
276 for (i=0; i<logow; i++)
277 for (j=0; j<logoh; j++)
278 maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
281 logo_y = logo_x = -1;
284 static int choose_door (void);
285 static int backup (void);
286 static void draw_wall (int, int, int);
287 static void draw_solid_square (int, int, int, GC);
288 static void enter_square (int);
291 create_maze (void) /* create a maze layout given the intiialized maze */
293 register int i, newdoor = 0;
296 move_list[sqnum].x = cur_sq_x;
297 move_list[sqnum].y = cur_sq_y;
298 move_list[sqnum].dir = newdoor;
299 while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
300 if ( backup() == -1 ) { /* no more doors ... backup */
301 return; /* done ... return */
305 /* mark the out door */
306 maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
320 /* mark the in door */
321 maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
323 /* if end square set path length and save path */
324 if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
326 for ( i=0; i<path_length; i++) {
327 save_path[i].x = move_list[i].x;
328 save_path[i].y = move_list[i].y;
329 save_path[i].dir = move_list[i].dir;
339 choose_door (void) /* pick a new path */
342 register int num_candidates;
347 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
349 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
351 if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
353 if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
354 maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
355 maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
356 draw_wall(cur_sq_x, cur_sq_y, 0);
359 candidates[num_candidates++] = 0;
363 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
365 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
367 if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
369 if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
370 maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
371 maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
372 draw_wall(cur_sq_x, cur_sq_y, 1);
375 candidates[num_candidates++] = 1;
379 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
381 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
383 if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
385 if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
386 maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
387 maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
388 draw_wall(cur_sq_x, cur_sq_y, 2);
391 candidates[num_candidates++] = 2;
395 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
397 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
399 if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
401 if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
402 maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
403 maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
404 draw_wall(cur_sq_x, cur_sq_y, 3);
407 candidates[num_candidates++] = 3;
410 if (num_candidates == 0)
412 if (num_candidates == 1)
413 return ( candidates[0] );
414 return ( candidates[ get_random(num_candidates) ] );
420 backup (void) /* back up a move */
423 cur_sq_x = move_list[sqnum].x;
424 cur_sq_y = move_list[sqnum].y;
430 draw_maze_border (void) /* draw the maze outline */
435 for ( i=0; i<maze_size_x; i++) {
436 if ( maze[i][0] & WALL_TOP ) {
437 XDrawLine(dpy, win, gc,
438 border_x + grid_width * i,
440 border_x + grid_width * (i+1) - 1,
443 if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
444 XDrawLine(dpy, win, gc,
445 border_x + grid_width * i,
446 border_y + grid_height * (maze_size_y) - 1,
447 border_x + grid_width * (i+1) - 1,
448 border_y + grid_height * (maze_size_y) - 1);
451 for ( j=0; j<maze_size_y; j++) {
452 if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
453 XDrawLine(dpy, win, gc,
454 border_x + grid_width * maze_size_x - 1,
455 border_y + grid_height * j,
456 border_x + grid_width * maze_size_x - 1,
457 border_y + grid_height * (j+1) - 1);
459 if ( maze[0][j] & WALL_LEFT ) {
460 XDrawLine(dpy, win, gc,
462 border_y + grid_height * j,
464 border_y + grid_height * (j+1) - 1);
472 unsigned int w, h, bw, d;
473 XGetGeometry (dpy, logo_map, &r, &x, &y, &w, &h, &bw, &d);
474 XCopyPlane (dpy, logo_map, win, logo_gc,
476 border_x + 3 + grid_width * logo_x,
477 border_y + 3 + grid_height * logo_y, 1);
479 draw_solid_square (start_x, start_y, start_dir, tgc);
480 draw_solid_square (end_x, end_y, end_dir, tgc);
485 draw_wall(int i, int j, int dir) /* draw a single wall */
489 XDrawLine(dpy, win, gc,
490 border_x + grid_width * i,
491 border_y + grid_height * j,
492 border_x + grid_width * (i+1),
493 border_y + grid_height * j);
496 XDrawLine(dpy, win, gc,
497 border_x + grid_width * (i+1),
498 border_y + grid_height * j,
499 border_x + grid_width * (i+1),
500 border_y + grid_height * (j+1));
503 XDrawLine(dpy, win, gc,
504 border_x + grid_width * i,
505 border_y + grid_height * (j+1),
506 border_x + grid_width * (i+1),
507 border_y + grid_height * (j+1));
510 XDrawLine(dpy, win, gc,
511 border_x + grid_width * i,
512 border_y + grid_height * j,
513 border_x + grid_width * i,
514 border_y + grid_height * (j+1));
522 draw_solid_square(int i, int j, /* draw a solid square in a square */
526 case 0: XFillRectangle(dpy, win, gc,
527 border_x + bw + grid_width * i,
528 border_y - bw + grid_height * j,
529 grid_width - (bw+bw), grid_height);
531 case 1: XFillRectangle(dpy, win, gc,
532 border_x + bw + grid_width * i,
533 border_y + bw + grid_height * j,
534 grid_width, grid_height - (bw+bw));
536 case 2: XFillRectangle(dpy, win, gc,
537 border_x + bw + grid_width * i,
538 border_y + bw + grid_height * j,
539 grid_width - (bw+bw), grid_height);
541 case 3: XFillRectangle(dpy, win, gc,
542 border_x - bw + grid_width * i,
543 border_y + bw + grid_height * j,
544 grid_width, grid_height - (bw+bw));
552 solve_maze (void) /* solve it with graphical feedback */
557 /* plug up the surrounding wall */
558 maze[start_x][start_y] |= (WALL_TOP >> start_dir);
559 maze[end_x][end_y] |= (WALL_TOP >> end_dir);
561 /* initialize search path */
569 if ( ++path[i].dir >= 4 ) {
570 XFillRectangle(dpy, win, cgc,
571 border_x + bw + grid_width * (int)(path[i].x),
572 border_y + bw + grid_height * (int)(path[i].y),
573 grid_width - (bw+bw), grid_height - (bw+bw));
575 draw_solid_square( (int)(path[i].x), (int)(path[i].y),
576 (int)(path[i].dir), cgc);
578 else if ( ! (maze[path[i].x][path[i].y] &
579 (WALL_TOP >> path[i].dir)) &&
580 ( (i == 0) || ( (path[i].dir !=
581 (int)(path[i-1].dir+2)%4) ) ) ) {
584 if ( maze[path[i].x][path[i].y] & START_SQUARE ) {
588 if (check_events()) return;
589 /* Abort solve on expose - cheapo repaint strategy */
590 if (solve_delay) usleep (solve_delay);
596 enter_square (int n) /* move into a neighboring square */
598 draw_solid_square( (int)path[n].x, (int)path[n].y,
599 (int)path[n].dir, tgc);
602 switch (path[n].dir) {
603 case 0: path[n+1].x = path[n].x;
604 path[n+1].y = path[n].y - 1;
606 case 1: path[n+1].x = path[n].x + 1;
607 path[n+1].y = path[n].y;
609 case 2: path[n+1].x = path[n].x;
610 path[n+1].y = path[n].y + 1;
612 case 3: path[n+1].x = path[n].x - 1;
613 path[n+1].y = path[n].y;
620 * jmr additions for Jamie Zawinski's <jwz@netscape.com> screensaver stuff,
621 * note that the code above this has probably been hacked about in some
625 char *progclass = "Maze";
628 "Maze.background: black", /* to placate SGI */
629 "Maze.foreground: white", /* to placate SGI */
632 "*preDelay: 2000000",
633 "*postDelay: 4000000",
642 XrmOptionDescRec options[] = {
643 { "-grid-size", ".gridSize", XrmoptionSepArg, 0 },
644 { "-solve-delay", ".solveDelay", XrmoptionSepArg, 0 },
645 { "-pre-delay", ".preDelay", XrmoptionSepArg, 0 },
646 { "-post-delay", ".postDelay", XrmoptionSepArg, 0 },
647 { "-live-color", ".liveColor", XrmoptionSepArg, 0 },
648 { "-dead-color", ".deadColor", XrmoptionSepArg, 0 },
653 extern void skull (Display *, Window, GC, GC, int, int, int, int);
657 screenhack(Display *display, Window window)
661 XWindowAttributes xgwa;
662 unsigned long bg, fg, pfg, pbg, lfg;
664 size = get_integer_resource ("gridSize", "Dimension");
665 root = get_boolean_resource("root", "Boolean");
666 solve_delay = get_integer_resource ("solveDelay", "Integer");
667 pre_solve_delay = get_integer_resource ("preDelay", "Integer");
668 post_solve_delay = get_integer_resource ("postDelay", "Integer");
670 if (size < 2) size = 7 + (random () % 30);
671 grid_width = grid_height = size;
672 bw = (size > 6 ? 3 : (size-1)/2);
674 dpy = display; win = window; /* the maze stuff uses global variables */
676 XGetWindowAttributes (dpy, win, &xgwa);
681 set_maze_sizes (xgwa.width, xgwa.height);
684 XSelectInput (dpy, win, ExposureMask|ButtonPressMask|StructureNotifyMask);
686 gc = XCreateGC(dpy, win, 0, 0);
687 cgc = XCreateGC(dpy, win, 0, 0);
688 tgc = XCreateGC(dpy,win,0,0);
689 logo_gc = XCreateGC(dpy, win, 0, 0);
691 gray = XCreateBitmapFromData (dpy,win,gray1_bits,gray1_width,gray1_height);
693 bg = get_pixel_resource ("background","Background", dpy, xgwa.colormap);
694 fg = get_pixel_resource ("foreground","Foreground", dpy, xgwa.colormap);
695 lfg = get_pixel_resource ("logoColor", "Foreground", dpy, xgwa.colormap);
696 pfg = get_pixel_resource ("liveColor", "Foreground", dpy, xgwa.colormap);
697 pbg = get_pixel_resource ("deadColor", "Foreground", dpy, xgwa.colormap);
698 if (mono_p) lfg = pfg = fg;
701 lfg = ((bg == WhitePixel (dpy, DefaultScreen (dpy)))
702 ? BlackPixel (dpy, DefaultScreen (dpy))
703 : WhitePixel (dpy, DefaultScreen (dpy)));
705 XSetForeground (dpy, gc, fg);
706 XSetBackground (dpy, gc, bg);
707 XSetForeground (dpy, cgc, pbg);
708 XSetBackground (dpy, cgc, bg);
709 XSetForeground (dpy, tgc, pfg);
710 XSetBackground (dpy, tgc, bg);
711 XSetForeground (dpy, logo_gc, lfg);
712 XSetBackground (dpy, logo_gc, bg);
714 XSetStipple (dpy, cgc, gray);
715 XSetFillStyle (dpy, cgc, FillOpaqueStippled);
721 GC draw_gc, erase_gc;
722 /* round up to grid size */
723 w = ((logo_width / grid_width) + 1) * grid_width;
724 h = ((logo_height / grid_height) + 1) * grid_height;
725 logo_map = XCreatePixmap (dpy, win, w, h, 1);
727 draw_gc = XCreateGC (dpy, logo_map, GCForeground, &gcv);
729 erase_gc= XCreateGC (dpy, logo_map, GCForeground, &gcv);
730 XFillRectangle (dpy, logo_map, erase_gc, 0, 0, w, h);
731 skull (dpy, logo_map, draw_gc, erase_gc, 5, 0, w-10, h-10);
732 XFreeGC (dpy, draw_gc);
733 XFreeGC (dpy, erase_gc);
736 if (!(logo_map = XCreateBitmapFromData (dpy, win, logo_bits,
737 logo_width, logo_height)))
739 fprintf (stderr, "Can't create logo pixmap\n");
743 XMapRaised(dpy, win);
748 while (1) { /* primary execution loop [ rhess ] */
749 if (check_events()) continue ;
750 if (restart || stop) goto pop;
756 XClearWindow(dpy, win);
764 usleep (pre_solve_delay);
771 usleep (post_solve_delay);
781 static XWindowAttributes wattr;
785 XGetWindowAttributes (dpy, win, &wattr);
786 set_maze_sizes (wattr.width, wattr.height);
787 XClearWindow (dpy, win);