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>
57 #include <X11/bitmaps/gray1>
59 #define MAX_MAZE_SIZE_X 500
60 #define MAX_MAZE_SIZE_Y 500
62 #define MOVE_LIST_SIZE (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)
64 #define WALL_TOP 0x8000
65 #define WALL_RIGHT 0x4000
66 #define WALL_BOTTOM 0x2000
67 #define WALL_LEFT 0x1000
69 #define DOOR_IN_TOP 0x800
70 #define DOOR_IN_RIGHT 0x400
71 #define DOOR_IN_BOTTOM 0x200
72 #define DOOR_IN_LEFT 0x100
73 #define DOOR_IN_ANY 0xF00
75 #define DOOR_OUT_TOP 0x80
76 #define DOOR_OUT_RIGHT 0x40
77 #define DOOR_OUT_BOTTOM 0x20
78 #define DOOR_OUT_LEFT 0x10
80 #define START_SQUARE 0x2
81 #define END_SQUARE 0x1
86 #define get_random(x) (random() % (x))
88 static int logo_x, logo_y;
91 # define logo_width 128
92 # define logo_height 128
94 # include <X11/bitmaps/xlogo64>
95 # define logo_width xlogo64_width
96 # define logo_height xlogo64_height
97 # define logo_bits xlogo64_bits
100 static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];
106 } move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];
108 static int maze_size_x, maze_size_y;
109 static int sqnum, cur_sq_x, cur_sq_y, path_length;
110 static int start_x, start_y, start_dir, end_x, end_y, end_dir;
111 static int grid_width, grid_height;
115 static GC gc, cgc, tgc, logo_gc;
116 static Pixmap logo_map;
118 static int x = 0, y = 0, restart = 0, stop = 1, state = 1;
121 check_events() /* X event handler [ rhess ] */
130 switch (e.xbutton.button) {
136 if (state == 5) state = 4 ;
149 case ConfigureNotify:
154 XClearWindow (dpy, win);
168 set_maze_sizes (width, height)
171 maze_size_x = width / grid_width;
172 maze_size_y = height / grid_height;
177 initialize_maze() /* draw the surrounding wall and start/end squares */
179 register int i, j, wall;
181 /* initialize all squares */
182 for ( i=0; i<maze_size_x; i++) {
183 for ( j=0; j<maze_size_y; j++) {
189 for ( i=0; i<maze_size_x; i++ ) {
190 maze[i][0] |= WALL_TOP;
194 for ( j=0; j<maze_size_y; j++ ) {
195 maze[maze_size_x-1][j] |= WALL_RIGHT;
199 for ( i=0; i<maze_size_x; i++ ) {
200 maze[i][maze_size_y-1] |= WALL_BOTTOM;
204 for ( j=0; j<maze_size_y; j++ ) {
205 maze[0][j] |= WALL_LEFT;
208 /* set start square */
209 wall = get_random(4);
212 i = get_random(maze_size_x);
217 j = get_random(maze_size_y);
220 i = get_random(maze_size_x);
225 j = get_random(maze_size_y);
228 maze[i][j] |= START_SQUARE;
229 maze[i][j] |= ( DOOR_IN_TOP >> wall );
230 maze[i][j] &= ~( WALL_TOP >> wall );
242 i = get_random(maze_size_x);
247 j = get_random(maze_size_y);
250 i = get_random(maze_size_x);
255 j = get_random(maze_size_y);
258 maze[i][j] |= END_SQUARE;
259 maze[i][j] |= ( DOOR_OUT_TOP >> wall );
260 maze[i][j] &= ~( WALL_TOP >> wall );
266 if ((maze_size_x > 15) && (maze_size_y > 15))
268 int logow = 1 + logo_width / grid_width;
269 int logoh = 1 + logo_height / grid_height;
270 /* not closer than 3 grid units from a wall */
271 logo_x = get_random (maze_size_x - logow - 6) + 3;
272 logo_y = get_random (maze_size_y - logoh - 6) + 3;
273 for (i=0; i<logow; i++)
274 for (j=0; j<logoh; j++)
275 maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
278 logo_y = logo_x = -1;
281 static int choose_door ();
282 static int backup ();
283 static void draw_wall ();
284 static void draw_solid_square ();
285 static void enter_square ();
288 create_maze() /* create a maze layout given the intiialized maze */
290 register int i, newdoor = 0;
293 move_list[sqnum].x = cur_sq_x;
294 move_list[sqnum].y = cur_sq_y;
295 move_list[sqnum].dir = newdoor;
296 while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
297 if ( backup() == -1 ) { /* no more doors ... backup */
298 return; /* done ... return */
302 /* mark the out door */
303 maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
317 /* mark the in door */
318 maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
320 /* if end square set path length and save path */
321 if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
323 for ( i=0; i<path_length; i++) {
324 save_path[i].x = move_list[i].x;
325 save_path[i].y = move_list[i].y;
326 save_path[i].dir = move_list[i].dir;
336 choose_door() /* pick a new path */
339 register int num_candidates;
344 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
346 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
348 if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
350 if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
351 maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
352 maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
353 draw_wall(cur_sq_x, cur_sq_y, 0);
356 candidates[num_candidates++] = 0;
360 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
362 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
364 if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
366 if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
367 maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
368 maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
369 draw_wall(cur_sq_x, cur_sq_y, 1);
372 candidates[num_candidates++] = 1;
376 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
378 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
380 if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
382 if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
383 maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
384 maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
385 draw_wall(cur_sq_x, cur_sq_y, 2);
388 candidates[num_candidates++] = 2;
392 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
394 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
396 if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
398 if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
399 maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
400 maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
401 draw_wall(cur_sq_x, cur_sq_y, 3);
404 candidates[num_candidates++] = 3;
407 if (num_candidates == 0)
409 if (num_candidates == 1)
410 return ( candidates[0] );
411 return ( candidates[ get_random(num_candidates) ] );
417 backup() /* back up a move */
420 cur_sq_x = move_list[sqnum].x;
421 cur_sq_y = move_list[sqnum].y;
427 draw_maze_border() /* draw the maze outline */
432 for ( i=0; i<maze_size_x; i++) {
433 if ( maze[i][0] & WALL_TOP ) {
434 XDrawLine(dpy, win, gc,
435 border_x + grid_width * i,
437 border_x + grid_width * (i+1) - 1,
440 if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
441 XDrawLine(dpy, win, gc,
442 border_x + grid_width * i,
443 border_y + grid_height * (maze_size_y) - 1,
444 border_x + grid_width * (i+1) - 1,
445 border_y + grid_height * (maze_size_y) - 1);
448 for ( j=0; j<maze_size_y; j++) {
449 if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
450 XDrawLine(dpy, win, gc,
451 border_x + grid_width * maze_size_x - 1,
452 border_y + grid_height * j,
453 border_x + grid_width * maze_size_x - 1,
454 border_y + grid_height * (j+1) - 1);
456 if ( maze[0][j] & WALL_LEFT ) {
457 XDrawLine(dpy, win, gc,
459 border_y + grid_height * j,
461 border_y + grid_height * (j+1) - 1);
469 unsigned int w, h, bw, d;
470 XGetGeometry (dpy, logo_map, &r, &x, &y, &w, &h, &bw, &d);
471 XCopyPlane (dpy, logo_map, win, logo_gc,
473 border_x + 3 + grid_width * logo_x,
474 border_y + 3 + grid_height * logo_y, 1);
476 draw_solid_square (start_x, start_y, start_dir, tgc);
477 draw_solid_square (end_x, end_y, end_dir, tgc);
482 draw_wall(i, j, dir) /* draw a single wall */
487 XDrawLine(dpy, win, gc,
488 border_x + grid_width * i,
489 border_y + grid_height * j,
490 border_x + grid_width * (i+1),
491 border_y + grid_height * j);
494 XDrawLine(dpy, win, gc,
495 border_x + grid_width * (i+1),
496 border_y + grid_height * j,
497 border_x + grid_width * (i+1),
498 border_y + grid_height * (j+1));
501 XDrawLine(dpy, win, gc,
502 border_x + grid_width * i,
503 border_y + grid_height * (j+1),
504 border_x + grid_width * (i+1),
505 border_y + grid_height * (j+1));
508 XDrawLine(dpy, win, gc,
509 border_x + grid_width * i,
510 border_y + grid_height * j,
511 border_x + grid_width * i,
512 border_y + grid_height * (j+1));
520 draw_solid_square(i, j, dir, gc) /* draw a solid square in a square */
521 register int i, j, dir;
525 case 0: XFillRectangle(dpy, win, gc,
526 border_x + bw + grid_width * i,
527 border_y - bw + grid_height * j,
528 grid_width - (bw+bw), grid_height);
530 case 1: XFillRectangle(dpy, win, gc,
531 border_x + bw + grid_width * i,
532 border_y + bw + grid_height * j,
533 grid_width, grid_height - (bw+bw));
535 case 2: XFillRectangle(dpy, win, gc,
536 border_x + bw + grid_width * i,
537 border_y + bw + grid_height * j,
538 grid_width - (bw+bw), grid_height);
540 case 3: XFillRectangle(dpy, win, gc,
541 border_x - bw + grid_width * i,
542 border_y + bw + grid_height * j,
543 grid_width, grid_height - (bw+bw));
551 solve_maze() /* solve it with graphical feedback */
556 /* plug up the surrounding wall */
557 maze[start_x][start_y] |= (WALL_TOP >> start_dir);
558 maze[end_x][end_y] |= (WALL_TOP >> end_dir);
560 /* initialize search path */
568 if ( ++path[i].dir >= 4 ) {
569 XFillRectangle(dpy, win, cgc,
570 border_x + bw + grid_width * (int)(path[i].x),
571 border_y + bw + grid_height * (int)(path[i].y),
572 grid_width - (bw+bw), grid_height - (bw+bw));
574 draw_solid_square( (int)(path[i].x), (int)(path[i].y),
575 (int)(path[i].dir), cgc);
577 else if ( ! (maze[path[i].x][path[i].y] &
578 (WALL_TOP >> path[i].dir)) &&
579 ( (i == 0) || ( (path[i].dir !=
580 (int)(path[i-1].dir+2)%4) ) ) ) {
583 if ( maze[path[i].x][path[i].y] & START_SQUARE ) {
587 if (check_events()) return;
588 /* Abort solve on expose - cheapo repaint strategy */
589 if (solve_delay) usleep (solve_delay);
595 enter_square(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;
622 * jmr additions for Jamie Zawinski's <jwz@netscape.com> screensaver stuff,
623 * note that the code above this has probably been hacked about in some
627 char *progclass = "Maze";
630 "Maze.background: black", /* to placate SGI */
631 "Maze.foreground: white", /* to placate SGI */
634 "*preDelay: 2000000",
635 "*postDelay: 4000000",
644 XrmOptionDescRec options[] = {
645 { "-grid-size", ".gridSize", XrmoptionSepArg, 0 },
646 { "-solve-delay", ".solveDelay", XrmoptionSepArg, 0 },
647 { "-pre-delay", ".preDelay", XrmoptionSepArg, 0 },
648 { "-post-delay", ".postDelay", XrmoptionSepArg, 0 },
649 { "-live-color", ".liveColor", XrmoptionSepArg, 0 },
650 { "-dead-color", ".deadColor", XrmoptionSepArg, 0 }
653 int options_size = (sizeof(options)/sizeof(options[0]));
655 void screenhack(display,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 extern void skull ();
723 /* round up to grid size */
724 w = ((logo_width / grid_width) + 1) * grid_width;
725 h = ((logo_height / grid_height) + 1) * grid_height;
726 logo_map = XCreatePixmap (dpy, win, w, h, 1);
728 draw_gc = XCreateGC (dpy, logo_map, GCForeground, &gcv);
730 erase_gc= XCreateGC (dpy, logo_map, GCForeground, &gcv);
731 XFillRectangle (dpy, logo_map, erase_gc, 0, 0, w, h);
732 skull (dpy, logo_map, draw_gc, erase_gc, 5, 0, w-10, h-10);
733 XFreeGC (dpy, draw_gc);
734 XFreeGC (dpy, erase_gc);
737 if (!(logo_map = XCreateBitmapFromData (dpy, win, logo_bits,
738 logo_width, logo_height)))
740 fprintf (stderr, "Can't create logo pixmap\n");
744 XMapRaised(dpy, win);
749 while (1) { /* primary execution loop [ rhess ] */
750 if (check_events()) continue ;
751 if (restart || stop) goto pop;
757 XClearWindow(dpy, win);
765 usleep (pre_solve_delay);
772 usleep (post_solve_delay);
782 static XWindowAttributes wattr;
786 XGetWindowAttributes (dpy, win, &wattr);
787 set_maze_sizes (wattr.width, wattr.height);
788 XClearWindow (dpy, win);