1 /******************************************************************************
4 * modified: [ 3-7-93 ] Jamie Zawinski <jwz@lucid.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 maze_restart_flag, random_index;
110 static int grid_width, grid_height;
114 static GC gc, cgc, tgc, logo_gc;
115 static Pixmap logo_map;
117 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);
165 set_maze_sizes (width, height)
168 maze_size_x = width / grid_width;
169 maze_size_y = height / grid_height;
173 initialize_maze() /* draw the surrounding wall and start/end squares */
175 register int i, j, wall;
177 /* initialize all squares */
178 for ( i=0; i<maze_size_x; i++) {
179 for ( j=0; j<maze_size_y; j++) {
185 for ( i=0; i<maze_size_x; i++ ) {
186 maze[i][0] |= WALL_TOP;
190 for ( j=0; j<maze_size_y; j++ ) {
191 maze[maze_size_x-1][j] |= WALL_RIGHT;
195 for ( i=0; i<maze_size_x; i++ ) {
196 maze[i][maze_size_y-1] |= WALL_BOTTOM;
200 for ( j=0; j<maze_size_y; j++ ) {
201 maze[0][j] |= WALL_LEFT;
204 /* set start square */
205 wall = get_random(4);
208 i = get_random(maze_size_x);
213 j = get_random(maze_size_y);
216 i = get_random(maze_size_x);
221 j = get_random(maze_size_y);
224 maze[i][j] |= START_SQUARE;
225 maze[i][j] |= ( DOOR_IN_TOP >> wall );
226 maze[i][j] &= ~( WALL_TOP >> wall );
238 i = get_random(maze_size_x);
243 j = get_random(maze_size_y);
246 i = get_random(maze_size_x);
251 j = get_random(maze_size_y);
254 maze[i][j] |= END_SQUARE;
255 maze[i][j] |= ( DOOR_OUT_TOP >> wall );
256 maze[i][j] &= ~( WALL_TOP >> wall );
262 if ((maze_size_x > 15) && (maze_size_y > 15))
264 int logow = 1 + logo_width / grid_width;
265 int logoh = 1 + logo_height / grid_height;
266 /* not closer than 3 grid units from a wall */
267 logo_x = get_random (maze_size_x - logow - 6) + 3;
268 logo_y = get_random (maze_size_y - logoh - 6) + 3;
269 for (i=0; i<logow; i++)
270 for (j=0; j<logoh; j++)
271 maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
274 logo_y = logo_x = -1;
277 static int choose_door ();
278 static int backup ();
279 static void draw_wall ();
280 static void draw_solid_square ();
281 static void enter_square ();
283 create_maze() /* create a maze layout given the intiialized maze */
285 register int i, newdoor;
288 move_list[sqnum].x = cur_sq_x;
289 move_list[sqnum].y = cur_sq_y;
290 move_list[sqnum].dir = newdoor;
291 while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
292 if ( backup() == -1 ) { /* no more doors ... backup */
293 return; /* done ... return */
297 /* mark the out door */
298 maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
312 /* mark the in door */
313 maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
315 /* if end square set path length and save path */
316 if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
318 for ( i=0; i<path_length; i++) {
319 save_path[i].x = move_list[i].x;
320 save_path[i].y = move_list[i].y;
321 save_path[i].dir = move_list[i].dir;
331 choose_door() /* pick a new path */
334 register int num_candidates;
340 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
342 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
344 if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
346 if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
347 maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
348 maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
349 draw_wall(cur_sq_x, cur_sq_y, 0);
352 candidates[num_candidates++] = 0;
356 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
358 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
360 if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
362 if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
363 maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
364 maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
365 draw_wall(cur_sq_x, cur_sq_y, 1);
368 candidates[num_candidates++] = 1;
372 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
374 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
376 if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
378 if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
379 maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
380 maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
381 draw_wall(cur_sq_x, cur_sq_y, 2);
384 candidates[num_candidates++] = 2;
388 if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
390 if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
392 if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
394 if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
395 maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
396 maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
397 draw_wall(cur_sq_x, cur_sq_y, 3);
400 candidates[num_candidates++] = 3;
403 if (num_candidates == 0)
405 if (num_candidates == 1)
406 return ( candidates[0] );
407 return ( candidates[ get_random(num_candidates) ] );
413 backup() /* back up a move */
416 cur_sq_x = move_list[sqnum].x;
417 cur_sq_y = move_list[sqnum].y;
422 draw_maze_border() /* draw the maze outline */
427 for ( i=0; i<maze_size_x; i++) {
428 if ( maze[i][0] & WALL_TOP ) {
429 XDrawLine(dpy, win, gc,
430 border_x + grid_width * i,
432 border_x + grid_width * (i+1) - 1,
435 if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
436 XDrawLine(dpy, win, gc,
437 border_x + grid_width * i,
438 border_y + grid_height * (maze_size_y) - 1,
439 border_x + grid_width * (i+1) - 1,
440 border_y + grid_height * (maze_size_y) - 1);
443 for ( j=0; j<maze_size_y; j++) {
444 if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
445 XDrawLine(dpy, win, gc,
446 border_x + grid_width * maze_size_x - 1,
447 border_y + grid_height * j,
448 border_x + grid_width * maze_size_x - 1,
449 border_y + grid_height * (j+1) - 1);
451 if ( maze[0][j] & WALL_LEFT ) {
452 XDrawLine(dpy, win, gc,
454 border_y + grid_height * j,
456 border_y + grid_height * (j+1) - 1);
464 unsigned int w, h, bw, d;
465 XGetGeometry (dpy, logo_map, &r, &x, &y, &w, &h, &bw, &d);
466 XCopyPlane (dpy, logo_map, win, logo_gc,
468 border_x + 3 + grid_width * logo_x,
469 border_y + 3 + grid_height * logo_y, 1);
471 draw_solid_square (start_x, start_y, start_dir, tgc);
472 draw_solid_square (end_x, end_y, end_dir, tgc);
477 draw_wall(i, j, dir) /* draw a single wall */
482 XDrawLine(dpy, win, gc,
483 border_x + grid_width * i,
484 border_y + grid_height * j,
485 border_x + grid_width * (i+1),
486 border_y + grid_height * j);
489 XDrawLine(dpy, win, gc,
490 border_x + grid_width * (i+1),
491 border_y + grid_height * j,
492 border_x + grid_width * (i+1),
493 border_y + grid_height * (j+1));
496 XDrawLine(dpy, win, gc,
497 border_x + grid_width * i,
498 border_y + grid_height * (j+1),
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,
506 border_x + grid_width * i,
507 border_y + grid_height * (j+1));
515 draw_solid_square(i, j, dir, gc) /* draw a solid square in a square */
516 register int i, j, dir;
520 case 0: XFillRectangle(dpy, win, gc,
521 border_x + bw + grid_width * i,
522 border_y - bw + grid_height * j,
523 grid_width - (bw+bw), grid_height);
525 case 1: XFillRectangle(dpy, win, gc,
526 border_x + bw + grid_width * i,
527 border_y + bw + grid_height * j,
528 grid_width, grid_height - (bw+bw));
530 case 2: XFillRectangle(dpy, win, gc,
531 border_x + bw + grid_width * i,
532 border_y + bw + grid_height * j,
533 grid_width - (bw+bw), grid_height);
535 case 3: XFillRectangle(dpy, win, gc,
536 border_x - bw + grid_width * i,
537 border_y + bw + grid_height * j,
538 grid_width, grid_height - (bw+bw));
545 solve_maze() /* solve it with graphical feedback */
550 /* plug up the surrounding wall */
551 maze[start_x][start_y] |= (WALL_TOP >> start_dir);
552 maze[end_x][end_y] |= (WALL_TOP >> end_dir);
554 /* initialize search path */
562 if ( ++path[i].dir >= 4 ) {
564 draw_solid_square( (int)(path[i].x), (int)(path[i].y),
565 (int)(path[i].dir), cgc);
567 else if ( ! (maze[path[i].x][path[i].y] &
568 (WALL_TOP >> path[i].dir)) &&
569 ( (i == 0) || ( (path[i].dir !=
570 (int)(path[i-1].dir+2)%4) ) ) ) {
573 if ( maze[path[i].x][path[i].y] & START_SQUARE ) {
577 if (check_events()) return;
578 /* Abort solve on expose - cheapo repaint strategy */
579 if (solve_delay) usleep (solve_delay);
585 enter_square(n) /* move into a neighboring square */
588 draw_solid_square( (int)path[n].x, (int)path[n].y,
589 (int)path[n].dir, tgc);
592 switch (path[n].dir) {
593 case 0: path[n+1].x = path[n].x;
594 path[n+1].y = path[n].y - 1;
596 case 1: path[n+1].x = path[n].x + 1;
597 path[n+1].y = path[n].y;
599 case 2: path[n+1].x = path[n].x;
600 path[n+1].y = path[n].y + 1;
602 case 3: path[n+1].x = path[n].x - 1;
603 path[n+1].y = path[n].y;
612 * jmr additions for Jamie Zawinski's <jwz@lucid.com> screensaver stuff,
613 * note that the code above this has probably been hacked about in some
617 char *progclass = "Maze";
621 "*background: black",
623 "*preDelay: 2000000",
624 "*postDelay: 4000000",
633 XrmOptionDescRec options[] = {
634 { "-grid-size", ".gridSize", XrmoptionSepArg, 0 },
635 { "-solve-delay", ".solveDelay", XrmoptionSepArg, 0 },
636 { "-pre-delay", ".preDelay", XrmoptionSepArg, 0 },
637 { "-post-delay", ".postDelay", XrmoptionSepArg, 0 },
638 { "-live-color", ".liveColor", XrmoptionSepArg, 0 },
639 { "-dead-color", ".deadColor", XrmoptionSepArg, 0 }
642 int options_size = (sizeof(options)/sizeof(options[0]));
644 void screenhack(display,window)
650 XWindowAttributes xgwa;
651 unsigned long bg, fg, pfg, pbg, lfg;
653 size = get_integer_resource ("gridSize", "Dimension");
654 root = get_boolean_resource("root", "Boolean");
655 solve_delay = get_integer_resource ("solveDelay", "Integer");
656 pre_solve_delay = get_integer_resource ("preDelay", "Integer");
657 post_solve_delay = get_integer_resource ("postDelay", "Integer");
659 if (size < 2) size = 7 + (random () % 30);
660 grid_width = grid_height = size;
661 bw = (size > 6 ? 3 : (size-1)/2);
663 dpy = display; win = window; /* the maze stuff uses global variables */
665 XGetWindowAttributes (dpy, win, &xgwa);
670 set_maze_sizes (xgwa.width, xgwa.height);
673 XSelectInput (dpy, win, ExposureMask|ButtonPressMask|StructureNotifyMask);
675 gc = XCreateGC(dpy, win, 0, 0);
676 cgc = XCreateGC(dpy, win, 0, 0);
677 tgc = XCreateGC(dpy,win,0,0);
678 logo_gc = XCreateGC(dpy, win, 0, 0);
680 gray = XCreateBitmapFromData (dpy,win,gray1_bits,gray1_width,gray1_height);
682 bg = get_pixel_resource ("background","Background", dpy, xgwa.colormap);
683 fg = get_pixel_resource ("foreground","Foreground", dpy, xgwa.colormap);
684 lfg = get_pixel_resource ("logoColor", "Foreground", dpy, xgwa.colormap);
685 pfg = get_pixel_resource ("liveColor", "Foreground", dpy, xgwa.colormap);
686 pbg = get_pixel_resource ("deadColor", "Foreground", dpy, xgwa.colormap);
687 if (mono_p) lfg = pfg = fg;
690 lfg = ((bg == WhitePixel (dpy, DefaultScreen (dpy)))
691 ? BlackPixel (dpy, DefaultScreen (dpy))
692 : WhitePixel (dpy, DefaultScreen (dpy)));
694 XSetForeground (dpy, gc, fg);
695 XSetBackground (dpy, gc, bg);
696 XSetForeground (dpy, cgc, pbg);
697 XSetBackground (dpy, cgc, bg);
698 XSetForeground (dpy, tgc, pfg);
699 XSetBackground (dpy, tgc, bg);
700 XSetForeground (dpy, logo_gc, lfg);
701 XSetBackground (dpy, logo_gc, bg);
703 XSetStipple (dpy, cgc, gray);
704 XSetFillStyle (dpy, cgc, FillOpaqueStippled);
710 GC draw_gc, erase_gc;
711 extern void skull ();
712 /* round up to grid size */
713 w = ((logo_width / grid_width) + 1) * grid_width;
714 h = ((logo_height / grid_height) + 1) * grid_height;
715 logo_map = XCreatePixmap (dpy, win, w, h, 1);
717 draw_gc = XCreateGC (dpy, logo_map, GCForeground, &gcv);
719 erase_gc= XCreateGC (dpy, logo_map, GCForeground, &gcv);
720 XFillRectangle (dpy, logo_map, erase_gc, 0, 0, w, h);
721 skull (dpy, logo_map, draw_gc, erase_gc, 5, 0, w-10, h-10);
722 XFreeGC (dpy, draw_gc);
723 XFreeGC (dpy, erase_gc);
726 if (!(logo_map = XCreateBitmapFromData (dpy, win, logo_bits,
727 logo_width, logo_height)))
729 fprintf (stderr, "Can't create logo pixmap\n");
733 XMapRaised(dpy, win);
738 while (1) { /* primary execution loop [ rhess ] */
739 if (check_events()) continue ;
740 if (restart || stop) goto pop;
746 XClearWindow(dpy, win);
754 usleep (pre_solve_delay);
761 usleep (post_solve_delay);
771 static XWindowAttributes wattr;
775 XGetWindowAttributes (dpy, win, &wattr);
776 set_maze_sizes (wattr.width, wattr.height);
777 XClearWindow (dpy, win);