1 /*
2  * XLife Copyright 1989 Jon Bennett jb7m+@andrew.cmu.edu, jcrb@cs.cmu.edu
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  The copyright holders make no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  *
22  * CMU SUCKS
23  */
24 
25 /*
26  * A lot of modifications were added at 2001, 2011-14 by Vladimir Lidovski vol.litwr@gmail.com
27  * (C) This version of XLife may be used under the same conditions as mentioned above
28  * $Id: main.c 279 2014-01-14 08:09:57Z litwr $
29  */
30 
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xos.h>
34 #include <X11/Xatom.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include "defs.h"
38 #include "tile.h"
39 #include "file.h"
40 #include "xwidget.h"
41 #include "colors.h"
42 #include "clipboard.h"
43 #include "patchlevel.h"
44 #include "framebuffer.h"
45 #include "history.h"
46 #include "topology.h"
47 
48 /*#define	char	unsigned char*/
49 #include "icon.h"
50 #include "cursor.h"
51 /*#undef char*/
52 
53 #ifndef GRAB_FRACTION
54 #define GRAB_FRACTION	0.8	/* fraction of screen to size */
55 #endif /* GRAB_FRACTION */
56 
57 coord_t savex, savey;	/* last point seen during boxing */
58 static coord_t xmarker[3], ymarker[3], linex, liney;
59 
redraw_lifew()60 void redraw_lifew() {
61    XClearWindow(disp, lifew);
62    pivot = 0;
63    fb_clear();
64    drawboundedgrid();
65    redisplay(MESH);
66 }
67 
DoExpose(Window win)68 void DoExpose(Window win) {
69    if (convmode) return;
70    if (win == lifew) {
71       XClearWindow(disp, lifew);
72       pivot = 0;
73       drawboundedgrid();
74 #if VFREQ != 0
75       prev_vsync.tv_sec--;
76 #endif
77       redisplay(MESH + EXPOSE);
78    }
79    else if (win == inputw) {
80       XClearWindow(disp, inputw);
81       XDrawString(disp, inputw, ntextgc, INPUTXOFF, INPUTYOFF, inpbuf,
82                                                                strlen(inpbuf));
83    }
84    else if (win == rulew)
85       showrules();
86    else if (win == statew)
87       showstates();
88    else if (win == loadw)
89       drawloadwidget();
90    else if (win == helpw) {
91       if (helpw_mode == -3)
92          redraw_slashinfo();
93       else if (helpw_mode == -1)
94          redraw_help();
95       else if (helpw_mode < 0)
96          redraw_viewvars();
97       else
98          eo_comments = redraw_comments();
99    }
100 }
101 
ResizeLW(int oheight)102 void ResizeLW(int oheight) {
103    XResizeWindow(disp, rulew, rulew_len, INPUTH);
104    XMoveWindow(disp, rulew, width - rulew_len
105                     - dispcoord*(COORDW + BORDERWIDTH) - BORDERWIDTH, oheight);
106    if (maxstates > 2) {
107       if (width > maxstates*(FONTWIDTH + 3) - BORDERWIDTH - STATEOFF) {
108          statescols = maxstates;
109          XMoveWindow(disp, statew, width - maxstates*(FONTWIDTH + 3)
110                                       - 3*BORDERWIDTH - STATEOFF, BORDERWIDTH);
111          XResizeWindow(disp, statew, maxstates*(FONTWIDTH + 3) + STATEOFF,
112                                                                        INPUTH);
113       }
114       else {
115          int w, h;
116          w = (width + BORDERWIDTH)/(FONTWIDTH + 3);
117          h = maxstates/w + (maxstates%w? 1: 0);
118          statescols = maxstates/h + (maxstates%h? 1: 0);
119          XMoveWindow(disp, statew, width - statescols*(FONTWIDTH + 3)
120                                       - 3*BORDERWIDTH - STATEOFF, BORDERWIDTH);
121          XResizeWindow(disp, statew, statescols*(FONTWIDTH + 3) + STATEOFF,
122                                                 (FONTHEIGHT + 3)*h + STATEOFF);
123       }
124       showstates();
125    }
126    else
127       XLowerWindow(disp, statew);
128    XMoveWindow(disp, coordw, width - dispcoord*(COORDW + BORDERWIDTH)
129                                                        - BORDERWIDTH, oheight);
130 }
131 
DoResize(void)132 void DoResize(void) {
133    int owidth = width, oheight = height;
134    if ((width = event.xconfigure.width) < 50)
135       XResizeWindow(disp, mainw, width = 50, height);
136    if ((height = event.xconfigure.height) < 70)
137       XResizeWindow(disp, mainw, width, height = 70);
138    xpos += sgn(owidth - width)*shr(abs(owidth - width), scale + 1);
139    ypos += sgn(oheight - height)*shr(abs(oheight - height), scale + 1);
140    inputlength = width/FONTWIDTH;
141    oheight = height - INPUTH - BORDERWIDTH*3;
142    if (oheight < 5) oheight = 5;
143    ResizeLW(oheight);
144    XResizeWindow(disp, lifew, width - BORDERWIDTH*2, oheight);
145    XResizeWindow(disp, helpw, width - BORDERWIDTH*2, height - BORDERWIDTH*2);
146    if (helpw_mode || eo_comments) DoExpose(helpw);
147    XMoveWindow(disp, inputw, 0, oheight);
148    XResizeWindow(disp, inputw, width - BORDERWIDTH*2, INPUTH);
149 }
150 
alloc_states(unsigned new_ev_mode,unsigned newmaxstates)151 void alloc_states(unsigned new_ev_mode, unsigned newmaxstates) {
152    if (maxstates == newmaxstates && new_ev_mode == ev_mode)
153       return;
154    confirmload(); /* it is necessary for changesize */
155 /*    if (newmaxstates < maxstates) clear_all();*/
156    clipboard_copy(&active);
157    if (new_ev_mode == VALENCE_DRIVEN)
158       changesize(sizeof(struct twostate_t));
159    else if (new_ev_mode == PAYOFF_DRIVEN || new_ev_mode == TAB8_DRIVEN)
160       changesize(sizeof(struct pstate_t));
161    else
162       changesize(sizeof(struct nstate_t)); /* also gstate_t */
163    ev_mode = new_ev_mode;
164    clipboard_flush(&active, maxstates = newmaxstates);
165    ResizeLW(height - INPUTH - BORDERWIDTH*3);
166 }
167 
DoKeySymIn(KeySym keysym)168 int DoKeySymIn(KeySym keysym) {
169    int dx, dy;
170    coord_t sxpos, sypos;
171    switch (keysym) {
172    case XK_4:
173    case XK_KP_4:
174    case XK_Left:
175    case XK_KP_Left:
176       prep_tentative();
177       if (event.xkey.state & ShiftMask)
178          xpos -= dx = SCALE(width/4);
179       else
180          xpos -= dx = (scale >= 0) ? 1 : 1 << -scale;
181       display_move(0, -dx, 0);
182       break;
183    case XK_6:
184    case XK_KP_6:
185    case XK_Right:
186    case XK_KP_Right:
187       prep_tentative();
188       if (event.xkey.state & ShiftMask)
189          xpos += dx = SCALE(width/4);
190       else
191          xpos += dx = (scale >= 0) ? 1 : 1 << -scale;
192       display_move(0, dx, 0);
193       break;
194    case XK_8:
195    case XK_KP_8:
196    case XK_Up:
197    case XK_KP_Up:
198       prep_tentative();
199       if (event.xkey.state & ShiftMask)
200          ypos -= dy = SCALE(height/4);
201       else
202          ypos -= dy = (scale >= 0) ? 1 : 1 << -scale;
203       display_move(0, 0, -dy);
204       break;
205    case XK_2:
206    case XK_KP_2:
207    case XK_Down:
208    case XK_KP_Down:
209       prep_tentative();
210       if (event.xkey.state & ShiftMask)
211          ypos += dy = SCALE(height/4);
212       else
213          ypos += dy = (scale >= 0) ? 1 : 1 << -scale;
214       display_move(0, 0, dy);
215       break;
216    case XK_Home:
217    case XK_7:
218    case XK_KP_7:
219    case XK_KP_Home:
220       prep_tentative();
221       if (event.xkey.state & ShiftMask) {
222          xpos -= dx = SCALE(width/4);
223          ypos -= dy = SCALE(height/4);
224       }
225       else {
226          xpos -= dx = (scale >= 0) ? 1 : 1 << -scale;
227          ypos -= dy = (scale >= 0) ? 1 : 1 << -scale;
228       }
229       display_move(0, -dx, -dy);
230       break;
231    case XK_Page_Up:
232    case XK_9:
233    case XK_KP_9:
234    case XK_KP_Page_Up:
235       prep_tentative();
236       if (event.xkey.state & ShiftMask) {
237          xpos += dx = SCALE(width/4);
238          ypos -= dy = SCALE(height/4);
239       }
240       else {
241          xpos += dx = (scale >= 0) ? 1 : 1 << -scale;
242          ypos -= dy = (scale >= 0) ? 1 : 1 << -scale;
243       }
244       display_move(0, dx, -dy);
245       break;
246    case XK_Page_Down:
247    case XK_3:
248    case XK_KP_3:
249    case XK_KP_Page_Down:
250       prep_tentative();
251       if (event.xkey.state & ShiftMask) {
252          xpos += dx = SCALE(width/4);
253          ypos += dy = SCALE(height/4);
254       }
255       else {
256          xpos += dx = (scale >= 0) ? 1 : 1 << -scale;
257          ypos += dy = (scale >= 0) ? 1 : 1 << -scale;
258       }
259       display_move(0, dx, dy);
260       break;
261    case XK_End:
262    case XK_1:
263    case XK_KP_1:
264    case XK_KP_End:
265       prep_tentative();
266       if (event.xkey.state & ShiftMask) {
267            xpos -= dx = SCALE(width/4),
268            ypos += dy = SCALE(height/4);
269       }
270       else {
271            xpos -= dx = (scale >= 0) ? 1 : 1 << -scale;
272            ypos += dy = (scale >= 0) ? 1 : 1 << -scale;
273       }
274       display_move(0, -dx, dy);
275       break;
276    case XK_5:
277    case XK_KP_5:
278    case XK_KP_Begin:
279       prep_tentative();
280       sxpos = xpos, sypos = ypos;
281       center();
282       display_move(0, xpos - sxpos, ypos - sypos);
283       break;
284    case XK_0:
285    case XK_KP_0:
286    case XK_KP_Insert:
287       prep_tentative();
288       sxpos = xpos, sypos = ypos;
289       median();
290       display_move(0, xpos - sxpos, ypos - sypos);
291       break;
292    case XK_Help:
293       help();
294       break;
295    case XK_Tab:
296       if (tentative.tiles && paintcolor > 1)
297          change_tentative_color();
298       break;
299    default:
300       return 0;
301    }
302 /* could process it */
303    return 1;
304 }
305 
inc_scale(void)306 void inc_scale(void) {
307    if (scale < MAXSCALE) {
308       setscale(++scale);
309       xpos += SCALE(event.xmotion.x);
310       ypos += SCALE(event.xmotion.y);
311       redraw_lifew();
312    }
313 }
314 
dec_scale(void)315 void dec_scale(void) {
316    if (scale > MINSCALE) {
317       xpos -= SCALE(event.xmotion.x);
318       ypos -= SCALE(event.xmotion.y);
319       setscale(--scale);
320       redraw_lifew();
321    }
322 }
323 
set_active_rules(char * s,int force)324 void set_active_rules(char *s, int force) {
325    int i = rulew_len;
326    strcpy(active_rules, s);
327    rulew_len = (strlen(s) + 1)*FONTWIDTH;
328    if (force && i != rulew_len)
329       ResizeLW(height - INPUTH - BORDERWIDTH*3);
330 }
331 
rules_changed(void)332 int rules_changed(void) {
333    char *pn = active_rules, *po = saved_rules;
334    if (!strcmp(pn, po))
335       return 0;
336    if (strstr(pn, po));
337    else if (strstr(po, pn)) {
338       po = active_rules;
339       pn = saved_rules;
340    }
341    else
342       return 1;
343    if (!strcmp(pn + strlen(po), "+"))
344       return 0;
345    return 1;
346 }
347 
fixpatterntopology(void)348 void fixpatterntopology(void) {
349    if (bounding_box(&active) && limits && (active.xmin < x_min_limit && x_min_limit
350               || active.xmax >= x_max_limit && x_max_limit
351               || active.ymin < y_min_limit && y_min_limit
352               || active.ymax >= y_max_limit && y_max_limit)) {
353              make_tentative(active.xmin, active.ymin, active.xmax, active.ymax);
354              copy_tentative();
355              clear_pattern(&tentative);
356         }
357 }
358 
fix_mouse_coord(int coord,int limit)359 static int fix_mouse_coord(int coord, int limit) {
360    if (coord < 0)
361       return 0;
362    if (coord > limit)
363       return limit - 40;
364    return coord;
365 }
366 
adjust_pivotpos()367 static void adjust_pivotpos() {
368    if (tentative.tiles && bounding_box(&tentative) < 1000000) {
369 /* don't change pivot position for the very big pattern */
370       clipboard_copy(&tentative);
371       clipboard.x -= tentative.xmin - STARTX;
372       clipboard.y -= tentative.ymin - STARTY;
373       if (!wireframe) boxpattern(0);
374       clear_pattern(&tentative);
375       clipboard_flush(&tentative, maxstates);
376       if (!wireframe) boxpattern(CYAN_EVER);
377    }
378 }
379 
DoKeyIn(char * kbuf)380 void DoKeyIn(char *kbuf) {
381     int num, dx, dy;
382     switch(kbuf[0]) {
383     case 0:
384        return;
385 
386     case 'r':
387         redraw_lifew();
388         displaystats();
389         break;
390 
391     case 'R':
392         strcpy(saved_rules, active_rules);
393         newrules();
394         displaystats();
395         if (rules_changed())
396            free_loadscript();
397         showrules();
398         break;
399 
400     case 'X':
401         widget_main(WIDST_LOAD_PALETTE);
402         break;
403 
404     case 'Z':
405         InitCurrentColors();
406         DefaultPalette();
407         redisplay(MESH);
408         break;
409 
410     case 'F':
411         strcpy(saved_rules, active_rules);
412         widget_main(WIDST_LOAD_RULE);
413         break;
414 
415     case '=':
416     case '+':
417         inc_scale();
418         break;
419 
420     case '-':
421         dec_scale();
422         break;
423 
424     case '.':
425         prep_tentative();
426         xpos += dx = SCALE(event.xbutton.x - width/2);
427         ypos += dy = SCALE(event.xbutton.y - height/2);
428         display_move(0, dx, dy);
429         break;
430 
431     case 'g':
432         confirmload();
433         state = state == RUN ? STOP : RUN;
434         lastx--;
435         bounding_box(&active);
436         redraw_lifew();
437         break;
438 
439     case '@':
440         if (ev_mode == VALENCE_DRIVEN || historymode) {
441            char s[100];
442            confirmload();
443            strcpy(s, active_rules);
444            if (historymode)
445               s[strlen(s) - 1] = 0;
446            else
447               strcat(s, "+");
448            file_rules(s);
449         }
450         else {
451            strcpy(inpbuf, "History is only allowed for 2-state automata");
452            announce_and_wait(RED_EVER);
453            displaystats();
454         }
455         break;
456 
457     case 'H':
458         if (truehistory = !truehistory) {
459            confirmload();
460            clearhistorytiles();
461            pattern2history(&active, 0);
462            announce("History record mode is on");
463         }
464         else {
465            redisplay(0);
466            announce("History record mode is off");
467         }
468         break;
469 
470     case 'c':
471         bounding_box(&active);
472         if (oscillators) {
473 osc_on_msg:
474 	   strcpy(inpbuf, "Oscillator check mode is on!");
475            announce_and_wait(RED_EVER);
476 	}
477         else
478 	   dispboxes ^= 1;
479         displaystats();
480 	break;
481 
482     case '*':
483         dispspeed = !dispspeed;
484         displaystats();
485         break;
486 
487     case '$':
488         bounding_box(&active);
489         if (oscillators)
490 	  goto osc_on_msg;
491         else
492           dispchanges ^= 1;
493         displaystats();
494         break;
495 
496     case 'P':
497         if (oscillators ^= 1) {
498            save_dispboxes = dispboxes;
499            save_dispchanges = dispchanges;
500            dispboxes = dispchanges = 1;
501            confirmload();
502            copypattern(&active, &oscillator_check);
503            generate(&active);
504            generate(&oscillator_check);
505            oscillator_check.generations = active.generations;
506            strcpy(inpbuf, "Oscillator check mode is on!");
507            announce_and_wait(GREEN_EVER);
508            state = RUN;
509         }
510         else {
511            dispboxes = save_dispboxes;
512            dispchanges = save_dispchanges;
513 	   announce("Oscillator check mode is off");
514         }
515         break;
516 
517     case 'E':
518         if ((pseudocolor = (pseudocolor + 1)%5) > 0) {
519            pseudopaint[0][1] = pseudocolor == 1 ? 0 : 2;
520            pseudopaint[1][0] = pseudocolor == 3 ? 1 : 3;
521            pseudopaint[1][1] = pseudocolor == 4 ? 0 : 1;
522            strcpy(inpbuf, "Pseudocolor mode: ");
523            pseudocolor_msg();
524            announce(inpbuf);
525         }
526         else
527            announce("Pseudocolor mode is off");
528         redisplay(MESH);
529         break;
530 
531     case 'o':
532         num = tentative.tiles != 0;
533         confirmload();
534         generate(&active);
535         lastx--;
536         redisplay(num);
537         displaystats();
538         break;
539 
540     case 'p':
541 	if (dispcoord)
542 	    dispcoord = FALSE;
543 	else
544 	{
545 	    lastx--;	/* fake move to force coordinates to print */
546 	    dispcoord = TRUE;
547 	}
548 /* force resize of input window */
549 	ResizeLW(height - INPUTH - BORDERWIDTH*3);
550 	showrules();
551 	break;
552 
553     case 'n': {
554         coord_t sxpos = xpos, sypos = ypos;
555         prep_tentative();
556         current_tile();
557         display_move(0, xpos - sxpos, ypos - sypos);
558         break;
559     }
560 
561     case '(':
562     case '[':
563     case '{':
564 	for (num = 0; kbuf[0] != "([{"[num]; num++);
565 	xmarker[num] = XPOS(event.xmotion.x);
566 	ymarker[num] = YPOS(event.xmotion.y);
567 	sprintf(inpbuf, "Marker %d set to (%d,%d)",
568 		num, xmarker[num] - xorigin, ymarker[num] - yorigin);
569 	DoExpose(inputw);
570 	break;
571 
572     case ')':
573     case ']':
574     case '}':
575 	for (num = 0; kbuf[0] != ")]}"[num]; num++);
576 	xpos += xmarker[num] - XPOS(event.xmotion.x);
577 	ypos += ymarker[num] - XPOS(event.xmotion.y);
578 	redraw_lifew();
579         break;
580 
581    case 'J':
582       announce("Jump to x, y: ");
583       minbuflen = 14;
584       getxstring();
585       {
586          int x, y;
587          if (sscanf(inpbuf + 13, "%d, %d", &x, &y) == 2) {
588             xpos += x + xorigin - XPOS(event.xmotion.x);
589             ypos += y + yorigin - YPOS(event.xmotion.y);
590             redraw_lifew();
591          }
592          displaystats();
593       }
594       break;
595 
596    case 'T':
597       confirmload();
598       announce("Enter topology: ");
599       minbuflen = 16;
600       getxstring();
601       if (!inpbuf[16])
602          goto exit_T;
603       {
604          char s[80];
605          strcpy(s, inpbuf + 16);
606          strncpy(inpbuf, " Wrong topology ", 16);
607          if (set_topology(s) > 0) {
608             announce_and_wait(RED_EVER);
609             goto exit_T;
610          }
611       }
612       fixpatterntopology();
613       redraw_lifew();
614       showrules();
615 exit_T:
616       displaystats();
617       break;
618 
619     case 'j':
620       announce("Enter jump length: ");
621       minbuflen = 19;
622       getxstring();
623       {
624          unsigned x;
625          if (sscanf(inpbuf + 19, "%u", &x) == 1 && x < 200000 && x > 0)
626             hideperiod = x;
627       }
628       if (hideperiod < 2)
629          strcpy(inpbuf, "No jump");
630       else
631          sprintf(inpbuf, "Jump set to %u", hideperiod);
632       DoExpose(inputw);
633       break;
634 
635     case 'O':
636 	announce("Origin set to active cell");
637 	xorigin = XPOS(event.xmotion.x);
638 	yorigin = YPOS(event.xmotion.y);
639         showcoord(1);
640 	break;
641 
642     case 'Y':
643         if (tentative.tiles) {
644            clear_pattern(&active);
645            copy_tentative();
646            clear_pattern(&tentative);
647            redraw_lifew();
648            displaystats();
649         }
650         break;
651 
652     case 'C':
653         if (tentative.tiles)
654            clear_pattern(&tentative);
655         else {
656            setscale(scale = 3);
657            clear_pattern(&active);
658            free_loadscript();
659            numcomments = outcome[0] = 0;
660            speed = 0;
661            state = STOP;
662            displaystats();
663            center();
664         }
665         redraw_lifew();
666 	break;
667 
668     case 'S':
669         widget_main(WIDST_SAVE);
670 	break;
671 
672     case 'W':
673 /* Confirm latest load before saving script */
674         confirmload();
675         savex = XPOS(fix_mouse_coord(event.xmotion.x, width));
676         savey = YPOS(fix_mouse_coord(event.xmotion.y, height));
677         if (loadscript == loadscript->next) {
678             strcpy(inpbuf, "There is no script to save!");
679 	    announce_and_wait(RED_EVER);
680 	    displaystats();
681 	}
682 	else
683             widget_main(WIDST_SAVE_SCRIPT);
684 	break;
685 
686     case 'D':
687 	free_loadscript();
688         numcomments = outcome[0] = 0;
689 	if (tentative.tiles) {
690 	    clear_pattern(&tentative);
691 	    redraw_lifew();
692 	    announce("Load script (and latest load) discarded");
693 	}
694 	else
695 	    announce("Load script discarded");
696 	break;
697 
698     case 'l':
699         confirmload();
700         iloadx = loadx = XPOS(fix_mouse_coord(event.xmotion.x, width));
701         iloady = loady = YPOS(fix_mouse_coord(event.xmotion.y, height));
702         widget_main(WIDST_LOAD_PATTERN);
703         adjust_pivotpos();
704         break;
705 
706     case 'L':
707         if (stashed[0]) {
708            confirmload();
709            iloadx = loadx = XPOS(fix_mouse_coord(event.xmotion.x, width));
710            iloady = loady = YPOS(fix_mouse_coord(event.xmotion.y, height));
711            strcpy(saved_rules, active_rules);
712            if (!loadfile_req(stashed)) {
713               sprintf(inpbuf, "Can't load %s", stashed);
714               goto ERROUT;
715            }
716            adjust_pivotpos();
717            if (rules_changed())
718               free_loadscript();
719         }
720         else {
721 	   strcpy(inpbuf, "No previous load");
722 ERROUT:
723            announce_and_wait(RED_EVER);
724            displaystats();
725 	}
726         break;
727 
728     case 'u':
729         prep_tentative();
730         txx = tyy = 1;
731         txy = tyx = 0;
732         loadx = iloadx;
733         loady = iloady;
734         display_move(0, 0, 0);
735         break;
736 
737     case 'h':
738 	confirmload();
739 	if (state == HIDE) {
740             state = RUN;
741 	    redraw_lifew();
742 	}
743 	else
744 	    state = HIDE;
745 	break;
746 
747     case '/':
748         view_slashinfo();
749         break;
750 
751     case 'b':
752         if (bounding_box(&active) == 0)
753             sprintf(inpbuf, "Life is extinct");
754         else
755             sprintf(inpbuf,
756                     "Life bounds: %d <= x <= %d  %d <= y <= %d",
757                     active.xmin - xorigin, active.xmax - xorigin,
758                     active.ymin - yorigin, active.ymax - yorigin);
759         announce_and_wait(0);
760         displaystats();
761         break;
762 
763     case '?':
764 	help();
765 	break;
766 
767     case 'f':
768 	settimeout(delay = DELAY_FAST);
769 	break;
770 
771     case 'm':
772 	settimeout(delay = DELAY_MED);
773 	break;
774 
775     case 's':
776 	settimeout(delay = DELAY_SLOW);
777 	break;
778 
779     case '>':
780         if (delay >= DELAY_INCREMENT)
781             settimeout(delay -= DELAY_INCREMENT);
782         break;
783 
784     case '<':
785         settimeout(delay += DELAY_INCREMENT);
786         break;
787 
788     case '!':
789 	randomize();
790 	break;
791 
792     case '%': {
793            float r;
794            announce("Input density (% or nothing): ");
795            minbuflen = 30;
796            getxstring();
797            if (sscanf(inpbuf + 30, "%f", &r) == 1)
798                rnd_density = r/100;
799            sprintf(inpbuf, "Using %.2f%% density", rnd_density*100);
800            DoExpose(inputw);
801         }
802         break;
803 
804    case 'i':               /* set evolution counter */
805         announce("Number of generations to perform (default = infinite): ");
806         minbuflen = 55;
807         getxstring();
808         if (sscanf(inpbuf + 55, "%u", &runcounter) != 1)
809            runcounter = 0;
810         displaystats();
811         break;
812 
813     case '#': /* toggle wireframe tentative pattern mode */
814         strcpy(inpbuf, "Wireframe mode is o");
815         if (wireframe ^= 1)
816            strcat(inpbuf, "n");
817         else
818            strcat(inpbuf, "ff");
819         DoExpose(inputw);
820         if (tentative.tiles)
821              redraw_lifew();
822         break;
823 
824     case 'M': /* toggle gridmode */
825         dispmesh ^= 1;
826         redraw_lifew();
827         break;
828 
829     case '^': {
830 	   unsigned x = 0;
831            announce("Set random seed (integer number): ");
832            minbuflen = 17;
833            getxstring();
834 	   if (sscanf(inpbuf + 16, "%u", &x) == 1)
835 	       srandom(randomseed = x);
836 	   sprintf(inpbuf, "Random seed is set to %u", x);
837 	   DoExpose(inputw);
838         }
839         break;
840 
841     case 'N':
842 	name_file();
843 	break;
844 
845     case 'A':
846 	comment();
847 	break;
848 
849     case 'V':
850 	view_comments();
851 	break;
852 
853     case 'K':
854         numcomments = outcome[0] = 0;
855         announce("Comments are discarded");
856         break;
857 
858     case 'k':
859         num = lookcell(&active, XPOS(event.xmotion.x), YPOS(event.xmotion.y));
860         setcolor(num, 0, 0);
861         break;
862 
863     case 'v':
864         viewvars();
865         break;
866 
867     case 'B':
868 	benchmark();
869         bounding_box(&active);
870 	break;
871 
872     case 'Q':
873         //XCloseDisplay(disp);
874 	exit(0);
875 
876     case 'U': /* Get rid of loaded pattern */
877         if (stashed[0]) {
878            clear_pattern(&tentative);
879            redraw_lifew();
880         }
881         break;
882 
883     case 'I': /* Force confirm of loaded pattern */
884         confirmload();
885         display_move(1, 0, 0);
886         break;
887 
888     case 'd':
889         copy_tentative();
890         break;
891 
892     case 'G': /* perform some generations on loaded pattern */
893         prep_tentative();
894         genload();
895         lastx--;
896         bounding_box(&active);
897         display_move(0, 0, 0);
898         break;
899 
900     case 'x':
901        if (tentative.tiles) {
902          long dlx, dly, tx, ty;
903          prep_tentative();
904          clipboard_copy(&tentative);
905          loadx += dlx = XPOS(event.xbutton.x) - loadx;
906          loady += dly = YPOS(event.xbutton.y) - loady;
907          iloadx += tx = (dly*txy - dlx*tyy)/(txy*tyx - txx*tyy);
908          iloady += ty = (dlx*tyx - dly*txx)/(txy*tyx - txx*tyy);
909          clipboard.x -= tx;
910          clipboard.y -= ty;
911          clear_pattern(&tentative);
912          clipboard_flush(&tentative, maxstates);
913        }
914        break;
915 
916     case 'a':
917         if (ev_mode == TABLE_DRIVEN || ev_mode == TAB8_DRIVEN)
918 	  set_transition();
919 	break;
920     case 't':
921         if (ev_mode == TABLE_DRIVEN || ev_mode == TAB8_DRIVEN)
922 	  test_transition();
923     }
924     kbuf[0] = '\0';	/* get rid of old keystroke so shift doesn't bring it back */
925 }
926 
cell2line(cell_t c)927 static int cell2line(cell_t c) {
928    if (chgcell(&active, linex, liney, c)) {
929       drawcell(RXPOS(linex), RYPOS(liney), c);
930       fb_ins_old(linex, liney, c);
931       return 1;
932    }
933    return 0;
934 }
935 
Motion(void)936 void Motion(void) { /* handle X motion events */
937     if (event.xmotion.window == lifew) {
938         coord_t x = XPOS(event.xmotion.x), y = YPOS(event.xmotion.y);
939         if (tentative.tiles) {
940            if (event.xmotion.state & Button1MotionMask)
941               moveload();
942            return;
943         }
944         if (event.xmotion.state & Button1MotionMask) {
945             int changed = 0;
946             if (!fix_mouse_coord(event.xmotion.x, width) || !fix_mouse_coord(event.xmotion.y, height))
947                return;
948             while (abs(linex - x) > 0 || abs(liney - y) > 0) {
949                linex += sgn((int)(x - linex)), liney += sgn((int)(y - liney));
950                if (limits && !chk_limits(linex, liney))
951                   break;
952                if (cell2line(paintcolor)) changed = 1;
953             }
954             if (changed) displaystats();
955         }
956         else if (event.xmotion.state & Button3MotionMask) {
957             /* erase the old box, draw the new one */
958           if (!limits || chk_limits(loadx, loady)) {
959              if (limits) {
960                 if (limits&1)
961                   if (x < x_min_limit)
962                     x = x_min_limit;
963                   else if (x >= x_max_limit)
964                     x = x_max_limit - 1;
965                 if (limits&2)
966                   if (y < y_min_limit)
967                     y = y_min_limit;
968                   else if (y >= y_max_limit)
969                     y = y_max_limit - 1;
970              }
971              if (savex != x || savey != y) {
972                 erasebox(loadx, loady, savex, savey);
973                 drawbox(loadx, loady, savex = x, savey = y, ORANGE_EVER);
974              }
975           }
976         }
977         else if (event.xmotion.state & Button2MotionMask) {
978             int changed = 0;
979             if (!fix_mouse_coord(event.xmotion.x, width) || !fix_mouse_coord(event.xmotion.y, height))
980                return;
981             while (abs(linex - x) > 0 || abs(liney - y) > 0) {
982                linex += sgn((int)(x - linex)), liney += sgn((int)(y - liney));
983                if (limits && !chk_limits(linex, liney))
984                   break;
985                if (cell2line(0)) changed = 1;
986             }
987             if (changed) displaystats();
988         }
989     }
990 }
991 
Button(void)992 void Button(void) { /* handle a button-press event */
993     if (event.xbutton.window == statew)
994         if (event.xbutton.button == 1)
995 	    setcolor(-1, event.xbutton.x, event.xbutton.y);
996         else
997             entercolor(event.xbutton.x, event.xbutton.y);
998     else if (event.xbutton.window == lifew) {
999         int dy;
1000         switch (event.xbutton.button) {
1001         case 5:
1002             prep_tentative();
1003             ypos += dy = (scale >= 0) ? 5 : 5 << -scale;
1004             display_move(0, 0, dy);
1005             return;
1006         case 4:
1007             prep_tentative();
1008             ypos -= dy = (scale >= 0) ? 5 : 5 << -scale;
1009             display_move(0, 0, -dy);
1010             return;
1011         }
1012         if (tentative.tiles)
1013           switch (event.xbutton.button) {
1014           case 1:
1015             moveload();
1016             break;
1017           case 2:
1018             flipload();
1019             break;
1020           case 3:
1021             turnload();
1022           }
1023         else {
1024           coord_t x = XPOS(event.xmotion.x), y = YPOS(event.xmotion.y);
1025           switch(event.xbutton.button) {
1026           case 1:
1027             if (!limits || chk_limits(x, y)) {
1028               showcoord(TRUE);
1029               linex = x, liney = y;
1030               if (cell2line(paintcolor)) displaystats();
1031             }
1032             break;
1033           case 2:
1034             if (!limits || chk_limits(x, y)) {
1035               showcoord(TRUE);
1036               linex = x, liney = y;
1037               if (cell2line(0)) displaystats();
1038             }
1039             break;
1040           case 3:
1041             savex = loadx = x;
1042             savey = loady = y;
1043             drawbox(x, y, x, y, ORANGE_EVER);
1044           }
1045         }
1046     }
1047 }
1048 
Release()1049 void Release() {
1050    if (event.xbutton.window == lifew && !tentative.tiles
1051                                                 && event.xbutton.button == 3) {
1052       int dx = 0, dy = 0;
1053       if (loadx == savex && savey == loady) {
1054          drawbox(loadx, loady, loadx, loady, 0);
1055          xpos += dx = SCALE(event.xbutton.x - width/2);
1056          ypos += dy = SCALE(event.xbutton.y - height/2);
1057       }
1058       else {
1059          make_tentative(loadx, loady, savex, savey);
1060          displaystats();
1061       }
1062       display_move(0, dx, dy);
1063     }
1064 }
1065 
selectmainfont()1066 static XFontStruct* selectmainfont() {
1067    XFontStruct *f;
1068    char fontspec[80];
1069    if (f = XLoadQueryFont(disp, NORMALFONT)) return f;
1070    sprintf(fontspec, "-*-courier-medium-r-*--%d-*-*-*-*-*-*-*", FONTHEIGHT);
1071    if (f = XLoadQueryFont(disp, fontspec)) return f;
1072    sprintf(fontspec, "-*-courier-*-r-*--%d-*-*-*-*-*-*-*", FONTHEIGHT);
1073    if (f = XLoadQueryFont(disp, fontspec)) return f;
1074    return 0;
1075 }
1076 
selectsmallfont()1077 static XFontStruct* selectsmallfont() {
1078    XFontStruct *f;
1079    if (f = XLoadQueryFont(disp, "6x9")) return f;
1080    if (f = XLoadQueryFont(disp, "-*-*-medium-r-*--9-*-*-*-*-*-*-*")) return f;
1081    if (f = XLoadQueryFont(disp, "-*-*-*-r-*--8-*-*-*-*-*-*-*")) return f;
1082    if (f = XLoadQueryFont(disp, "-*-courier-bold-r-*--8-*-*-*-*-*-*-*")) return f;
1083    if (f = XLoadQueryFont(disp, "-*-*-medium-r-*--10-*-*-*-*-*-*-*")) return f;
1084    if (f = XLoadQueryFont(disp, "-*-*-*-r-*--9-*-*-*-*-*-*-*")) return f;
1085    if (f = XLoadQueryFont(disp, "-*-*-medium-r-*--11-*-*-*-*-*-*-*")) return f;
1086    if (f = XLoadQueryFont(disp, "-*-*-medium-r-*--12-*-*-*-*-*-*-*")) return f;
1087    if (f = XLoadQueryFont(disp, "*6x*")) return f;
1088    if (f = XLoadQueryFont(disp, "*7x*")) return f;
1089    return selectmainfont();
1090 }
1091 
make_delay(long sec,long msec)1092 void make_delay(long sec, long msec) {
1093    struct timeval inputdelay;
1094    inputdelay.tv_sec = sec;
1095    inputdelay.tv_usec = msec;
1096    select(32, 0, 0, 0, &inputdelay);
1097 }
1098 
check_param(int b)1099 static int check_param(int b) {
1100    if (b)
1101       fatal("Usage: xlife [-display disp] [-geometry geom]  [-l pattern-lib] [filename]\n");
1102    return 1;
1103 }
1104 
init_load(char * initpat)1105 static void init_load(char *initpat) {
1106    char *s = checktilda(initpat);
1107    if (s == 0) return;
1108    loadx = xpos;
1109    loady = ypos;
1110    txx = tyy = 1;
1111    txy = tyx = 0;
1112    if (loadfile_req(s))
1113       adjust_pivotpos();
1114 }
1115 
1116 #ifdef MICROSOFT
wmain(int argc,char ** argv)1117 int wmain(int argc, char **argv) {
1118 #else
1119 int main(int argc, char **argv) {
1120 #endif
1121     int i, qgens = 0;
1122     Cursor cursor;
1123     Pixmap icon, cursor_data, cursor_mask;
1124     XSizeHints hints;
1125     XWMHints wm_hints;
1126     XClassHint class_hints;
1127     XSetWindowAttributes winat;
1128     XColor white, black;
1129     char *geomstring = 0;
1130     char *initpat = 0;
1131     XTextProperty iconName;
1132     unsigned long cwhite, cblack;
1133     char *icon_name = "Xlife";
1134     char *display = getenv("DISPLAY");
1135 
1136     if (strstr(argv[0], "lifeconv")) {
1137       saveformat = 0;
1138       convmode++;
1139       for (i = 1; i < argc; i++)
1140         if (*argv[i] == '-')
1141               if (strchr("ACDIMPRSpv?lg4", argv[i][1]))
1142                   switch (argv[i][1]) {
1143                      case '?':
1144 le:                     fatal("Usage: lifeconv -ACDIMPRSpv? [-l pattern-lib] [-g n] filename\n");
1145                      case 'v':
1146                         printf("Lifeconv/XLife v%d.%d.%d\n", majorversion,
1147                                                   minorversion, patchlevel);
1148                         return 0;
1149                      case 'g':
1150                         if (argc <= i + 1 || sscanf(argv[++i], "%d", &qgens) != 1) goto le;
1151                         break;
1152                      case 'l':
1153                         if (argc <= i + 1) goto le;
1154                         strcpy(matchlibfile, argv[++i]);
1155                         break;
1156                      case 'p':
1157                         oldp++;
1158                         argv[i][1] = 'P';
1159                      default:
1160                         if (saveformat != 0) goto le;
1161                         saveformat = argv[i][1];
1162                   }
1163               else
1164                  goto le;
1165         else if (initpat == 0)
1166             initpat = argv[i];
1167     }
1168     else
1169       for (i = 1; i < argc; i++)
1170 	if (!strcmp(argv[i], "-geometry") && check_param(argc <= i + 1))
1171 	    geomstring = argv[++i];
1172         else if (!strcmp(argv[i], "-l") && check_param(argc <= i + 1))
1173             strcpy(matchlibfile, argv[++i]);
1174         else if (!strcmp(argv[i], "-display") && check_param(argc <= i + 1))
1175             display = argv[++i];
1176 	else if (*argv[i] != '-')
1177 	    initpat = argv[i];
1178     if (convmode && (initpat == 0 || saveformat == 0)) goto le;
1179     if (!getcwd(inidir, PATNAMESIZ))
1180         fatal("Too long dirname\n");
1181     if (!display)
1182         fatal("No display defined!\n");
1183     if (!(disp = XOpenDisplay(display)))
1184         fatal("Can't open X display\n");
1185     if (XDefaultDepth(disp, screen) < STATEBITS)
1186         fatal("Not enough colors for compiled STATEBITS value\n");
1187 #if VFREQ != 0
1188     gettimeofday(&prev_vsync, 0);
1189 #endif
1190     setcurdir(LIFEDIR); /*inidir*/
1191     fileinit();
1192     initcells(&active);
1193     initcells(&tentative);
1194     xpos = xorigin = lastx = STARTX;
1195     ypos = yorigin = lasty = STARTY;
1196     dispcoord = TRUE;
1197     dispchanges = dispboxes = FALSE;
1198     state = STOP;
1199     gentab();
1200     srandom(time(0));
1201 
1202     screen = DefaultScreen(disp);
1203     rootw = RootWindow(disp, screen);
1204     fcolor = cwhite = WhitePixel(disp, screen);
1205     bcolor = cblack = BlackPixel(disp, screen);
1206 
1207     hints.x = hints.y = 0;
1208     maxwidth = width = DisplayWidth(disp, screen);
1209     inputlength = width/FONTWIDTH;
1210     height = DisplayHeight(disp, screen);
1211     if ((pfb = malloc(i = height*width*sizeof(struct FB))) == 0)
1212        fatal("Not enough memory for the framebuffer");
1213     else
1214        memset(pfb, 0, i);
1215     hints.width = width*GRAB_FRACTION;
1216     hints.height = height*GRAB_FRACTION;
1217     hints.flags = PPosition | PSize;
1218     if (geomstring) {
1219 	int result = XParseGeometry(geomstring, &hints.x, &hints.y,
1220                             (unsigned*)&hints.width, (unsigned*)&hints.height);
1221 	if (result & XNegative)
1222 	    hints.x += (DisplayWidth(disp, screen) - hints.width)*GRAB_FRACTION;
1223 	if (result & YNegative)
1224 	    hints.y += (DisplayHeight(disp, screen) - hints.height)*GRAB_FRACTION;
1225 	if (result & XValue || result & YValue) {
1226 	    hints.flags |= USPosition;
1227 	    hints.flags &= ~PPosition;
1228 	}
1229 	if (result & WidthValue || result & HeightValue) {
1230 	    hints.flags |= USSize;
1231 	    hints.flags &= ~PSize;
1232 	}
1233     }
1234     mainw = XCreateSimpleWindow(disp, rootw,
1235 		hints.x, hints.y, hints.width, hints.height, 0, fcolor, bcolor);
1236     if (!mainw)
1237 	fatal("Can't open main window\n");
1238     icon = XCreateBitmapFromData(disp, mainw, icon_bits, icon_width, icon_height);
1239     if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0)
1240         fatal("structure allocation for iconName failed.\n");
1241     wm_hints.initial_state = NormalState;
1242     wm_hints.input = True;
1243     wm_hints.icon_pixmap = icon;
1244     wm_hints.flags = IconPixmapHint | StateHint | InputHint;
1245     class_hints.res_name =  argv[0];
1246     class_hints.res_class = "Basicwin";
1247     XSetWMProperties(disp, mainw, 0, &iconName, argv, argc, &hints, &wm_hints, &class_hints);
1248     IniPalette();
1249     black = cellcolor[0];
1250     white = cellcolor[1];
1251 /* text display is forced to black on white */
1252     xgcv.background = cwhite;
1253     xgcv.foreground = cblack;
1254     ntextgc = XCreateGC(disp, mainw, GCForeground | GCBackground, &xgcv);
1255     xgcv.background = cblack;
1256     xgcv.foreground = cwhite;
1257     itextgc = XCreateGC(disp, mainw, GCForeground | GCBackground, &xgcv);
1258 /* create XOR GC for pivot display */
1259     xgcv.foreground = cellcolor[CYAN_EVER].pixel;
1260     xgcv.function = GXxor;
1261     xorgc = XCreateGC(disp, mainw, GCForeground | GCFunction, &xgcv);
1262     if (!(nfont = selectmainfont()))
1263        fatal("Can't load font\n");
1264     XSetFont(disp, ntextgc, nfont->fid);
1265     XSetFont(disp, itextgc, nfont->fid);
1266     XSetFont(disp, cellgc[LOADW_BG], nfont->fid);
1267     XSetFont(disp, xorgc, nfont->fid);
1268     cfont = selectsmallfont();
1269     xgcv.function = GXxor;
1270     xgcv.foreground = cellcolor[ORANGE_EVER].pixel;
1271     invgc = XCreateGC(disp, mainw, GCForeground | GCFunction, &xgcv);
1272     XSetFont(disp, invgc, cfont->fid);
1273     cursor_data = XCreateBitmapFromData(disp, mainw, cursor_data_bits,
1274                 cursor_data_width, cursor_data_height);
1275     cursor_mask = XCreateBitmapFromData(disp, mainw, cursor_mask_bits,
1276                 cursor_mask_width, cursor_mask_height);
1277     cursor = XCreatePixmapCursor(disp, cursor_data, cursor_mask, &white,
1278                 &black, cursor_data_x_hot, cursor_data_y_hot);
1279     XDefineCursor(disp, mainw, cursor);
1280     width = hints.width;
1281     height = hints.height;
1282     set_active_rules("life", 0);
1283     lifew = XCreateSimpleWindow(disp, mainw,
1284 		0, 0, width - BORDERWIDTH*2,
1285                 height - INPUTH - BORDERWIDTH*3, BORDERWIDTH, fcolor, bcolor);
1286     helpw = XCreateSimpleWindow(disp, mainw,
1287 		0, 0, width - BORDERWIDTH*2, height - BORDERWIDTH*2,
1288                 BORDERWIDTH, cwhite, cblack);
1289     coordw = XCreateSimpleWindow(disp, mainw,
1290 		width - COORDW - BORDERWIDTH*2,
1291 		height - INPUTH - BORDERWIDTH*2,
1292                 COORDW, INPUTH, BORDERWIDTH, cblack, cwhite);
1293     rulew = XCreateSimpleWindow(disp, mainw,
1294 		width - COORDW - rulew_len - BORDERWIDTH*2,
1295 		height - INPUTH - BORDERWIDTH*2,
1296                 rulew_len, INPUTH, BORDERWIDTH, cblack, cwhite);
1297     statew = XCreateSimpleWindow(disp, mainw,
1298                 width, BORDERWIDTH, 1, INPUTH, BORDERWIDTH,
1299                 cblack, cwhite);
1300     loadw = XCreateSimpleWindow(disp, mainw,
1301                 50, /*LOADW_HEIGHT + 50 > height ? 0 : 40*/4, LOADW_WIDTH, LOADW_HEIGHT, BORDERWIDTH,
1302                 cblack, cellcolor[LOADW_BG].pixel);
1303     inputw = XCreateSimpleWindow(disp, mainw, 0,
1304                 height - INPUTH - BORDERWIDTH*2, width - BORDERWIDTH*2,
1305                 INPUTH, BORDERWIDTH, cblack, cwhite);
1306     winat.win_gravity = SouthGravity;
1307     XChangeWindowAttributes(disp, inputw, CWWinGravity, &winat);
1308     XSelectInput(disp, mainw,
1309 #ifndef MICROSOFT
1310                 KeyPressMask |
1311 #endif
1312                 ExposureMask | StructureNotifyMask);
1313     XSelectInput(disp, inputw, ButtonPressMask | ExposureMask);
1314     XSelectInput(disp, lifew, KeyPressMask | ButtonPressMask | Button1MotionMask
1315        | PointerMotionMask | Button3MotionMask | ButtonReleaseMask | ExposureMask);
1316     XSelectInput(disp, helpw, KeyPressMask | ButtonPressMask | ExposureMask);
1317     XSelectInput(disp, rulew, ExposureMask);
1318     XSelectInput(disp, statew, ExposureMask | ButtonPressMask);
1319     XSelectInput(disp, loadw, KeyPressMask | ExposureMask | ButtonPressMask
1320                                         | PointerMotionMask | LeaveWindowMask);
1321     XSelectInput(disp, coordw, ExposureMask);
1322     alloc_states(VALENCE_DRIVEN, 2);
1323     setscale(scale = 3);
1324     settimeout(delay = DELAY_FAST);
1325 /*
1326  * Only accept one pattern since it is highly unlikely that overlaying
1327  * n patterns is what you want to do
1328  */
1329     if (saveformat == 'C') {
1330         collect(initpat, 0);
1331         return 0;
1332     }
1333     if (convmode) {
1334        init_load(initpat);
1335        i = bounding_box(&tentative);
1336        confirmload();
1337        if (i != bounding_box(&active))
1338           fatal("Can't automatically convert this pattern with topology information -- Use XLife for the conversion\n");
1339        while (qgens-- > 0)
1340           generate(&active);
1341 #ifdef MICROSOFT
1342 /* MINGW can't use stdout :-( */
1343        {
1344        FILE *f = fopen(argv[argc - 1], "r");
1345        if (f || argc < 4)
1346 errwin:    fatalw("Wrong name for the output file. Use syntax: lifeconv OPTIONS INFILE OUTFILE");
1347        f = fopen(argv[argc - 1], "w");
1348        if (!f) goto errwin;
1349        saveall(f, saveformat);
1350        fclose(f);
1351        }
1352 #else
1353        saveall(stdout, saveformat);
1354 #endif
1355        return 0;
1356     }
1357     xpos -= SCALE(width >> 1);
1358     ypos -= SCALE(height >> 1);
1359     XMapWindow(disp, inputw);
1360     XMapWindow(disp, helpw);
1361     XMapWindow(disp, lifew);
1362     XMapWindow(disp, mainw);
1363     XMapWindow(disp, rulew);
1364     XMapWindow(disp, statew);
1365     XMapWindow(disp, loadw);
1366     XMapWindow(disp, coordw);
1367     XLowerWindow(disp, helpw);
1368     XLowerWindow(disp, statew);
1369     XLowerWindow(disp, loadw);
1370     XRaiseWindow(disp, inputw);
1371     XRaiseWindow(disp, rulew);
1372     XRaiseWindow(disp, coordw);
1373     showrules();
1374     displaystats();
1375     if (initpat)
1376         init_load(initpat);
1377 #ifdef MICROSOFT
1378     griddisplay(1);
1379 #endif
1380     for (;;) {
1381 	while (XCheckMaskEvent(disp, KeyPressMask|ButtonPressMask|Button1MotionMask
1382 	        |PointerMotionMask|Button3MotionMask|ButtonReleaseMask|ExposureMask
1383 	        |StructureNotifyMask, &event)) {
1384 	    switch(event.type) {
1385 	      case KeyPress:
1386 		XLookupString(&event.xkey, keybuf, 16, &ks, 0);
1387 		if (!DoKeySymIn(ks))
1388                     if (event.xkey.window == lifew || !strchr("(){}[].JOkx", *keybuf))
1389 		        DoKeyIn(keybuf);
1390 		break;
1391 	      case MotionNotify:
1392 /* don't allow drawing until load is confirmed */
1393                 Motion();
1394 		break;
1395 	      case ButtonPress:
1396 		Button();
1397 		break;
1398 	      case ButtonRelease:
1399 		Release();
1400 		break;
1401 	      case ConfigureNotify:
1402 		DoResize();
1403                 redraw_lifew();
1404 		break;
1405 	      case Expose:
1406 	        DoExpose(event.xexpose.window);
1407 	    }
1408         }
1409 	showcoord(FALSE);
1410 	if (state != STOP) {
1411             generate(&active);
1412             if (hideperiod > 1) {
1413                 for (i = 1; i < hideperiod; i++)
1414                    if (state != STOP)
1415                       generate(&active);
1416                 if (state != HIDE)
1417                    redisplay(0);
1418             }
1419             else
1420 	        redisplay(0);
1421             if (speed < 10000*hideperiod || ((active.generations/hideperiod)&0x3f) == 0)
1422                 displaystats();
1423 	}
1424 	else
1425             make_delay(0, 100000);
1426 	if (state == RUN)
1427             make_delay(timeout.tv_sec, timeout.tv_usec);
1428     }
1429 }
1430 /* main.c ends here */
1431