1 /******************************************************************************
4 * modified: [ 3-7-93 ] Jamie Zawinski <jwz@mcom.com>
5 * added the XRoger logo, cleaned up resources, made
6 * grid size a parameter.
7 * modified: [ 3-3-93 ] Jim Randell <jmr@mddjmr.fc.hp.com>
8 * Added the colour stuff and integrated it with jwz's
9 * screenhack stuff. There's still some work that could
10 * be done on this, particularly allowing a resource to
11 * specify how big the squares are.
12 * modified: [ 10-4-88 ] Richard Hess ...!uunet!cimshop!rhess
13 * [ Revised primary execution loop within main()...
14 * [ Extended X event handler, check_events()...
15 * modified: [ 1-29-88 ] Dave Lemke lemke@sun.com
17 * [ Note the word "hacked" -- this is extremely ugly, but at
18 * [ least it does the job. NOT a good programming example
20 * original: [ 6/21/85 ] Martin Weiss Sun Microsystems [ SunView ]
22 ******************************************************************************
23 Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the names of Sun or MIT not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific prior written permission. Sun and M.I.T.
34 make no representations about the suitability of this software for
35 any purpose. It is provided "as is" without any express or implied warranty.
37 SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
40 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
41 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
42 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
43 OR PERFORMANCE OF THIS SOFTWARE.
44 *****************************************************************************/
46 #include "screenhack.h"
50 static int solve_delay, pre_solve_delay, post_solve_delay;
54 #include <X11/Xutil.h>
55 #include <X11/bitmaps/gray1>
57 #define MAX_MAZE_SIZE_X 500
58 #define MAX_MAZE_SIZE_Y 500
60 #define MOVE_LIST_SIZE (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)
62 #define WALL_TOP 0x8000
63 #define WALL_RIGHT 0x4000
64 #define WALL_BOTTOM 0x2000
65 #define WALL_LEFT 0x1000
67 #define DOOR_IN_TOP 0x800
68 #define DOOR_IN_RIGHT 0x400
69 #define DOOR_IN_BOTTOM 0x200
70 #define DOOR_IN_LEFT 0x100
71 #define DOOR_IN_ANY 0xF00
73 #define DOOR_OUT_TOP 0x80
74 #define DOOR_OUT_RIGHT 0x40
75 #define DOOR_OUT_BOTTOM 0x20
76 #define DOOR_OUT_LEFT 0x10
78 #define START_SQUARE 0x2
79 #define END_SQUARE 0x1
84 #define get_random(x) (random() % (x))
86 static int logo_x, logo_y;
89 # define logo_width 128
90 # define logo_height 128
92 # include <X11/bitmaps/xlogo64>
93 # define logo_width xlogo64_width
94 # define logo_height xlogo64_height
95 # define logo_bits xlogo64_bits
98 static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];
104 } move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];
106 static int maze_size_x, maze_size_y;
107 static int sqnum, cur_sq_x, cur_sq_y, path_length;
108 static int start_x, start_y, start_dir, end_x, end_y, end_dir;
109 static int grid_width, grid_height;
113 static GC gc, cgc, tgc, logo_gc;
114 static Pixmap logo_map;
116 static int x = 0, y = 0, restart = 0, stop = 1, state = 1;
119 check_events() /* X event handler [ rhess ] */
128 switch (e.xbutton.button) {
134 if (state == 5) state = 4 ;
147 case ConfigureNotify:
152 XClearWindow (dpy, win);
166 set_maze_sizes (width, height)
169 maze_size_x = width / grid_width;
170 maze_size_y = height / grid_height;
175 initialize_maze() /* draw the surrounding wall and start/end squares */
177 register int i, j, wall;
179 /* initialize all squares */
180 for ( i=0; i<maze_size_x; i++) {
181 for ( j=0; j<maze_size_y; j++) {
187 for ( i=0; i<maze_size_x; i++ ) {
188 maze[i][0] |= WALL_TOP;
192 for ( j=0; j<maze_size_y; j++ ) {
193 maze[maze_size_x-1][j] |= WALL_RIGHT;
197 for ( i=0; i<maze_size_x; i++ ) {
198 maze[i][maze_size_y-1] |= WALL_BOTTOM;
202 for ( j=0; j<maze_size_y; j++ ) {
203 maze[0][j] |= WALL_LEFT;
206 /* set start square */
207 wall = get_random(4);
210 i = get_random(maze_size_x);
215 j = get_random(maze_size_y);
218 i = get_random(maze_size_x);
223 j = get_random(maze_size_y);
226 maze[i][j] |= START_SQUARE;
227 maze[i][j] |= ( DOOR_IN_TOP >> wall );
228 maze[i][j] &= ~( WALL_TOP >> wall );
240 i = get_random(maze_size_x);
245 j = get_random(maze_size_y);
248 i = get_random(maze_size_x);
253 j = get_random(maze_size_y);
256 maze[i][j] |= END_SQUARE;
257 maze[i][j] |= ( DOOR_OUT_TOP >> wall );
258 maze[i][j] &= ~( WALL_TOP >> wall );
264 if ((maze_size_x > 15) && (maze_size_y > 15))
266 int logow = 1 + logo_width / grid_width;
267 int logoh = 1 + logo_height / grid_height;
268 /* not closer than 3 grid units from a wall */
269 logo_x = get_random (maze_size_x - logow - 6) + 3;
270 logo_y = get_random (maze_size_y - logoh - 6) + 3;
271 for (i=0; i<logow; i++)
272 for (j=0; j<logoh; j++)
273 maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
276 logo_y = logo_x = -1;
279 static int choose_door ();
280 static int backup ();
281 static void draw_wall ();
282 static void draw_solid_square ();
283 static void enter_square ();
286 create_maze() /* create a maze layout given the intiialized maze */
288 register int i, newdoor;
291 move_list[sqnum].x = cur_sq_x;
292 move_list[sqnum].y = cur_sq_y;
293 move_list[sqnum].dir = newdoor;
294 while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
295 if ( backup() == -1 ) { /* no more doors ... backup */
296 return; /* done ... return */
300 /* mark the out door */
301 maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
315 /* mark the in door */
316 maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
318 /* if end square set path length and save path */
319 if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
321 for ( i=0; i<path_length; i++) {
322 save_path[i].x = move_list[i].x;
323 save_path[i].y = move_list[i].y;
324 save_path[i].dir = move_list[i].dir;
334 choose_door() /* pick a new path */
337 register int num_candidates;
342 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
344 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
346 if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
348 if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
349 maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
350 maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
351 draw_wall(cur_sq_x, cur_sq_y, 0);
354 candidates[num_candidates++] = 0;
358 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
360 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
362 if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
364 if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
365 maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
366 maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
367 draw_wall(cur_sq_x, cur_sq_y, 1);
370 candidates[num_candidates++] = 1;
374 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
376 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
378 if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
380 if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
381 maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
382 maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
383 draw_wall(cur_sq_x, cur_sq_y, 2);
386 candidates[num_candidates++] = 2;
390 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
392 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
394 if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
396 if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
397 maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
398 maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
399 draw_wall(cur_sq_x, cur_sq_y, 3);
402 candidates[num_candidates++] = 3;
405 if (num_candidates == 0)
407 if (num_candidates == 1)
408 return ( candidates[0] );
409 return ( candidates[ get_random(num_candidates) ] );
415 backup() /* back up a move */
418 cur_sq_x = move_list[sqnum].x;
419 cur_sq_y = move_list[sqnum].y;
425 draw_maze_border() /* draw the maze outline */
430 for ( i=0; i<maze_size_x; i++) {
431 if ( maze[i][0] & WALL_TOP ) {
432 XDrawLine(dpy, win, gc,
433 border_x + grid_width * i,
435 border_x + grid_width * (i+1) - 1,
438 if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
439 XDrawLine(dpy, win, gc,
440 border_x + grid_width * i,
441 border_y + grid_height * (maze_size_y) - 1,
442 border_x + grid_width * (i+1) - 1,
443 border_y + grid_height * (maze_size_y) - 1);
446 for ( j=0; j<maze_size_y; j++) {
447 if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
448 XDrawLine(dpy, win, gc,
449 border_x + grid_width * maze_size_x - 1,
450 border_y + grid_height * j,
451 border_x + grid_width * maze_size_x - 1,
452 border_y + grid_height * (j+1) - 1);
454 if ( maze[0][j] & WALL_LEFT ) {
455 XDrawLine(dpy, win, gc,
457 border_y + grid_height * j,
459 border_y + grid_height * (j+1) - 1);
467 unsigned int w, h, bw, d;
468 XGetGeometry (dpy, logo_map, &r, &x, &y, &w, &h, &bw, &d);
469 XCopyPlane (dpy, logo_map, win, logo_gc,
471 border_x + 3 + grid_width * logo_x,
472 border_y + 3 + grid_height * logo_y, 1);
474 draw_solid_square (start_x, start_y, start_dir, tgc);
475 draw_solid_square (end_x, end_y, end_dir, tgc);
480 draw_wall(i, j, dir) /* draw a single wall */
485 XDrawLine(dpy, win, gc,
486 border_x + grid_width * i,
487 border_y + grid_height * j,
488 border_x + grid_width * (i+1),
489 border_y + grid_height * j);
492 XDrawLine(dpy, win, gc,
493 border_x + grid_width * (i+1),
494 border_y + grid_height * j,
495 border_x + grid_width * (i+1),
496 border_y + grid_height * (j+1));
499 XDrawLine(dpy, win, gc,
500 border_x + grid_width * i,
501 border_y + grid_height * (j+1),
502 border_x + grid_width * (i+1),
503 border_y + grid_height * (j+1));
506 XDrawLine(dpy, win, gc,
507 border_x + grid_width * i,
508 border_y + grid_height * j,
509 border_x + grid_width * i,
510 border_y + grid_height * (j+1));
518 draw_solid_square(i, j, dir, gc) /* draw a solid square in a square */
519 register int i, j, dir;
523 case 0: XFillRectangle(dpy, win, gc,
524 border_x + bw + grid_width * i,
525 border_y - bw + grid_height * j,
526 grid_width - (bw+bw), grid_height);
528 case 1: XFillRectangle(dpy, win, gc,
529 border_x + bw + grid_width * i,
530 border_y + bw + grid_height * j,
531 grid_width, grid_height - (bw+bw));
533 case 2: XFillRectangle(dpy, win, gc,
534 border_x + bw + grid_width * i,
535 border_y + bw + grid_height * j,
536 grid_width - (bw+bw), grid_height);
538 case 3: XFillRectangle(dpy, win, gc,
539 border_x - bw + grid_width * i,
540 border_y + bw + grid_height * j,
541 grid_width, grid_height - (bw+bw));
549 solve_maze() /* solve it with graphical feedback */
554 /* plug up the surrounding wall */
555 maze[start_x][start_y] |= (WALL_TOP >> start_dir);
556 maze[end_x][end_y] |= (WALL_TOP >> end_dir);
558 /* initialize search path */
566 if ( ++path[i].dir >= 4 ) {
568 draw_solid_square( (int)(path[i].x), (int)(path[i].y),
569 (int)(path[i].dir), cgc);
571 else if ( ! (maze[path[i].x][path[i].y] &
572 (WALL_TOP >> path[i].dir)) &&
573 ( (i == 0) || ( (path[i].dir !=
574 (int)(path[i-1].dir+2)%4) ) ) ) {
577 if ( maze[path[i].x][path[i].y] & START_SQUARE ) {
581 if (check_events()) return;
582 /* Abort solve on expose - cheapo repaint strategy */
583 if (solve_delay) usleep (solve_delay);
589 enter_square(n) /* move into a neighboring square */
592 draw_solid_square( (int)path[n].x, (int)path[n].y,
593 (int)path[n].dir, tgc);
596 switch (path[n].dir) {
597 case 0: path[n+1].x = path[n].x;
598 path[n+1].y = path[n].y - 1;
600 case 1: path[n+1].x = path[n].x + 1;
601 path[n+1].y = path[n].y;
603 case 2: path[n+1].x = path[n].x;
604 path[n+1].y = path[n].y + 1;
606 case 3: path[n+1].x = path[n].x - 1;
607 path[n+1].y = path[n].y;
616 * jmr additions for Jamie Zawinski's <jwz@mcom.com> screensaver stuff,
617 * note that the code above this has probably been hacked about in some
621 char *progclass = "Maze";
624 "Maze.background: black", /* to placate SGI */
625 "Maze.foreground: white", /* to placate SGI */
628 "*preDelay: 2000000",
629 "*postDelay: 4000000",
638 XrmOptionDescRec options[] = {
639 { "-grid-size", ".gridSize", XrmoptionSepArg, 0 },
640 { "-solve-delay", ".solveDelay", XrmoptionSepArg, 0 },
641 { "-pre-delay", ".preDelay", XrmoptionSepArg, 0 },
642 { "-post-delay", ".postDelay", XrmoptionSepArg, 0 },
643 { "-live-color", ".liveColor", XrmoptionSepArg, 0 },
644 { "-dead-color", ".deadColor", XrmoptionSepArg, 0 }
647 int options_size = (sizeof(options)/sizeof(options[0]));
649 void screenhack(display,window)
655 XWindowAttributes xgwa;
656 unsigned long bg, fg, pfg, pbg, lfg;
658 size = get_integer_resource ("gridSize", "Dimension");
659 root = get_boolean_resource("root", "Boolean");
660 solve_delay = get_integer_resource ("solveDelay", "Integer");
661 pre_solve_delay = get_integer_resource ("preDelay", "Integer");
662 post_solve_delay = get_integer_resource ("postDelay", "Integer");
664 if (size < 2) size = 7 + (random () % 30);
665 grid_width = grid_height = size;
666 bw = (size > 6 ? 3 : (size-1)/2);
668 dpy = display; win = window; /* the maze stuff uses global variables */
670 XGetWindowAttributes (dpy, win, &xgwa);
675 set_maze_sizes (xgwa.width, xgwa.height);
678 XSelectInput (dpy, win, ExposureMask|ButtonPressMask|StructureNotifyMask);
680 gc = XCreateGC(dpy, win, 0, 0);
681 cgc = XCreateGC(dpy, win, 0, 0);
682 tgc = XCreateGC(dpy,win,0,0);
683 logo_gc = XCreateGC(dpy, win, 0, 0);
685 gray = XCreateBitmapFromData (dpy,win,gray1_bits,gray1_width,gray1_height);
687 bg = get_pixel_resource ("background","Background", dpy, xgwa.colormap);
688 fg = get_pixel_resource ("foreground","Foreground", dpy, xgwa.colormap);
689 lfg = get_pixel_resource ("logoColor", "Foreground", dpy, xgwa.colormap);
690 pfg = get_pixel_resource ("liveColor", "Foreground", dpy, xgwa.colormap);
691 pbg = get_pixel_resource ("deadColor", "Foreground", dpy, xgwa.colormap);
692 if (mono_p) lfg = pfg = fg;
695 lfg = ((bg == WhitePixel (dpy, DefaultScreen (dpy)))
696 ? BlackPixel (dpy, DefaultScreen (dpy))
697 : WhitePixel (dpy, DefaultScreen (dpy)));
699 XSetForeground (dpy, gc, fg);
700 XSetBackground (dpy, gc, bg);
701 XSetForeground (dpy, cgc, pbg);
702 XSetBackground (dpy, cgc, bg);
703 XSetForeground (dpy, tgc, pfg);
704 XSetBackground (dpy, tgc, bg);
705 XSetForeground (dpy, logo_gc, lfg);
706 XSetBackground (dpy, logo_gc, bg);
708 XSetStipple (dpy, cgc, gray);
709 XSetFillStyle (dpy, cgc, FillOpaqueStippled);
715 GC draw_gc, erase_gc;
716 extern void skull ();
717 /* round up to grid size */
718 w = ((logo_width / grid_width) + 1) * grid_width;
719 h = ((logo_height / grid_height) + 1) * grid_height;
720 logo_map = XCreatePixmap (dpy, win, w, h, 1);
722 draw_gc = XCreateGC (dpy, logo_map, GCForeground, &gcv);
724 erase_gc= XCreateGC (dpy, logo_map, GCForeground, &gcv);
725 XFillRectangle (dpy, logo_map, erase_gc, 0, 0, w, h);
726 skull (dpy, logo_map, draw_gc, erase_gc, 5, 0, w-10, h-10);
727 XFreeGC (dpy, draw_gc);
728 XFreeGC (dpy, erase_gc);
731 if (!(logo_map = XCreateBitmapFromData (dpy, win, logo_bits,
732 logo_width, logo_height)))
734 fprintf (stderr, "Can't create logo pixmap\n");
738 XMapRaised(dpy, win);
743 while (1) { /* primary execution loop [ rhess ] */
744 if (check_events()) continue ;
745 if (restart || stop) goto pop;
751 XClearWindow(dpy, win);
759 usleep (pre_solve_delay);
766 usleep (post_solve_delay);
776 static XWindowAttributes wattr;
780 XGetWindowAttributes (dpy, win, &wattr);
781 set_maze_sizes (wattr.width, wattr.height);
782 XClearWindow (dpy, win);