1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 
5 #include "config_local.h"
6 
7 #ifdef HAVE_LIMITS_H
8 #include <limits.h>
9 #else
10 #ifdef HAVE_SYS_LIMITS_H
11 #include <sys/limits.h>
12 #endif
13 #endif
14 
15 #include "externs.h"
16 #include "globals.h"
17 #include "defaults.h"
18 #include "help.h"
19 #include "display.h"
20 
21 #if USE_XPM
22 #include "xpm.h"
23 #endif
24 
25 static short LoadBitmaps(void);
26 static short LoadOneBitmap(char *fname, char *altname, Pixmap *pix, int map);
27 static void MakeHelpWindows(void);
28 static Pixmap GetObjectPixmap(int, int, char);
29 static int PickWall(int, int);
30 static void DrawString(int, int, char *);
31 static void DisplayLevel(void);
32 static void DisplayPackets(void);
33 static void DisplayHelp(void);
34 
35 /* mnemonic defines to help orient some of the text/line drawing, sizes */
36 #define HELPLINE ((bit_height * MAXROW) + 30)
37 #define STATUSLINE ((bit_height * MAXROW) + 5)
38 #define HELP_H (bit_height * MAXROW)
39 #define HELP_W (bit_width * MAXCOL)
40 
41 /* local to this file */
42 static Window win;
43 static GC gc, rgc, drgc;
44 static unsigned int width, height, depth;
45 static XFontStruct *finfo;
46 static Boolean optwalls;
47 static Cursor this_curs;
48 static Pixmap help[HELP_PAGES], floor;
49 static Pixmap blank, work, man, saveman, goal, object,
50        treasure, walls[NUM_WALLS];
51 static Boolean font_alloc = _false_, gc_alloc = _false_,
52         pix_alloc = _false_, cmap_alloc = _false_, win_alloc = _false_;
53 static int hlpscrn = -1;
54 static char buf[500];
55 static int scr;
56 
57 /* globals */
58 Display *dpy;
59 Atom wm_delete_window, wm_protocols;
60 Boolean display_alloc = _false_;
61 unsigned bit_width, bit_height;
62 Colormap cmap;
63 
64 /* names of the fancy wall bitmap files.  If you define a set of fancy
65  * wall bitmaps, they must use these names
66  */
67 static char *wallname[] = {
68  "lonewall", "southwall", "westwall", "llcornerwall",
69  "northwall", "vertiwall", "ulcornerwall", "west_twall",
70  "eastwall", "lrcornerwall", "horizwall", "south_twall",
71  "urcornerwall", "east_twall", "north_twall", "centerwall"
72 };
73 
74 /* Do all the nasty X stuff like making the windows, setting all the defaults,
75  * creating all the pixmaps, loading everything, and mapping the window.
76  * This does NOT do the XOpenDisplay(), so that the -display switch can be
77  * handled cleanly.
78  */
InitX(void)79 short InitX(void)
80 {
81   int i;
82   Boolean reverse = _false_, tmpwalls = _false_;
83   char *rval;
84   unsigned long fore, back, bord, curs, gc_mask;
85   XSizeHints szh;
86   XWMHints wmh;
87   XSetWindowAttributes wattr;
88   XClassHint clh;
89   XGCValues val, reval;
90   XTextProperty wname, iname;
91   XColor cfg, cbg;
92   Atom protocols[1];
93   Window root;
94 
95   /* these are always needed */
96   scr = DefaultScreen(dpy);
97   cmap = DefaultColormap(dpy, scr);
98   depth = DefaultDepth(dpy, scr);
99   root = RootWindow(dpy, scr);
100 
101   /* Get a new colormap now, if needed. */
102   if (ownColormap) {
103       XWindowAttributes wa;
104       XGetWindowAttributes(dpy, root, &wa);
105       cmap = XCreateColormap(dpy, root, wa.visual, AllocNone);
106       cmap_alloc = _true_;
107   }
108 
109   /* here is where we figure out the resources and set the defaults.
110    * resources can be either on the command line, or in your .Xdefaults/
111    * .Xresources files.  They are read in and parsed in main.c, but used
112    * here.
113    */
114   finfo = GetFontResource(FONT);
115   if (!finfo) return E_NOFONT;
116   font_alloc = _true_;
117 
118   rval = GetResource(REVERSE);
119   if(rval != (char *)0) {
120     reverse = StringToBoolean(rval);
121   }
122 
123   fore = GetColorOrDefault(dpy, FOREG, depth, "black", _false_);
124   back = GetColorOrDefault(dpy, BACKG, depth, "grey90", _true_);
125 
126   if(reverse) {
127     unsigned long t;
128     t = fore;
129     fore = back;
130     back = t;
131   }
132 
133   if(!GetColorResource(BORDER, &bord))
134     bord = fore;
135   if(!GetColorResource(CURSOR, &curs))
136     curs = fore;
137 
138   bitpath = GetResource(BITDIR);
139   rval = GetResource(WALLS);
140   if(rval != (char *)0)
141     tmpwalls = StringToBoolean(rval);
142 
143   /* Walls are funny.  If a alternate bitpath has been defined, assume
144    * !fancywalls unless explicitly told fancy walls.  If the default
145    * bitpath is being used, you can assume fancy walls.
146    */
147   if(bitpath && !tmpwalls)
148     optwalls = _false_;
149   else
150     optwalls = _true_;
151 
152   width = MAXCOL * DEF_BITW;
153   height = MAXROW * DEF_BITH + 50;
154 
155   wmh.initial_state = NormalState;
156   wmh.input = True;
157   wmh.flags = (StateHint | InputHint);
158 
159   clh.res_class = clh.res_name = progname;
160 
161   /* Make sure the window and icon names are set */
162   if(!XStringListToTextProperty(&progname, 1, &wname))
163     return E_NOMEM;
164   if(!XStringListToTextProperty(&progname, 1, &iname))
165     return E_NOMEM;
166 
167 
168   /* load in a cursor, and recolor it so it looks pretty */
169   this_curs = XCreateFontCursor(dpy, DEF_CURSOR);
170   cfg.pixel = curs;
171   cbg.pixel = back;
172   XQueryColor(dpy, cmap, &cfg);
173   XQueryColor(dpy, cmap, &cbg);
174   XRecolorCursor(dpy, this_curs, &cfg, &cbg);
175 
176   /* set up the funky little window attributes */
177   wattr.background_pixel = back;
178   wattr.border_pixel = bord;
179   wattr.backing_store = Always;
180   wattr.event_mask = (KeyPressMask | ExposureMask | ButtonPressMask |
181 		      ButtonReleaseMask);
182   wattr.cursor = this_curs;
183   wattr.colormap = cmap;
184 
185   /* Create the window. we create it with NO size so that we
186    * can load in the bitmaps; we later resize it correctly.
187    */
188   win = XCreateWindow(dpy, root, 0, 0, width, height, 4,
189 		      CopyFromParent, InputOutput, CopyFromParent,
190                       (CWBackPixel | CWBorderPixel | CWBackingStore |
191                        CWEventMask | CWCursor | CWColormap), &wattr);
192   win_alloc = _true_;
193 
194 
195   /* this will set the bit_width and bit_height as well as loading
196    * in the pretty little bitmaps
197    */
198   switch (LoadBitmaps()) {
199     case 0: break;
200     case E_NOCOLOR: return E_NOCOLOR;
201     case E_NOBITMAP: return E_NOBITMAP;
202   }
203   blank = XCreatePixmap(dpy, win, bit_width, bit_height, 1);
204   pix_alloc = _true_;
205 
206   width = MAXCOL * bit_width;
207   height = MAXROW * bit_height + 50;
208 
209   /* whee, resize the window with the correct size now that we know it */
210   XResizeWindow(dpy, win, width, height);
211 
212   /* set up the size hints, we don't want manual resizing allowed. */
213   szh.min_width = szh.width = szh.max_width = width;
214   szh.min_height = szh.height = szh.max_height = height;
215   szh.x = szh.y = 0;
216   szh.flags = (PSize | PPosition | PMinSize | PMaxSize);
217 
218   /* now SET all those hints we create above */
219   XSetWMNormalHints(dpy, win, &szh);
220   XSetWMHints(dpy, win, &wmh);
221   XSetClassHint(dpy, win, &clh);
222   XSetWMName(dpy, win, &wname);
223   XSetWMIconName(dpy, win, &iname);
224 
225   /* Turn on WM_DELETE_WINDOW */
226   wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
227   wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", 0);
228   protocols[0] = wm_delete_window;
229   XSetWMProtocols(dpy, win, protocols, 1);
230 
231   work = XCreatePixmap(dpy, win, width, height, depth);
232 
233   /* set up all the relevant GC's */
234   val.foreground = reval.background = fore;
235   val.background = reval.foreground = back;
236   val.function = reval.function = GXcopy;
237   val.font = reval.font = finfo->fid;
238   gc_mask = (GCForeground | GCBackground | GCFunction | GCFont);
239   gc = XCreateGC(dpy, work, gc_mask, &val);
240   rgc = XCreateGC(dpy, blank, gc_mask, &reval);
241   drgc = XCreateGC(dpy, work, gc_mask, &reval);
242 
243   /* make the help windows and the working bitmaps */
244   /* we need to do this down here since it requires GCs to be allocated */
245   for(i = 0; i < HELP_PAGES; i++)
246     help[i] = XCreatePixmap(dpy, win, HELP_W, HELP_H, depth);
247   MakeHelpWindows();
248   XFillRectangle(dpy, blank, rgc, 0, 0, bit_width, bit_height);
249 
250   gc_alloc = _true_;
251 
252   /* display the friendly little clear screen */
253   ClearScreen();
254   XMapWindow(dpy, win);
255   RedisplayScreen();
256 
257   return 0;
258 }
259 
260 /* deallocate all the memory and structures used in creating stuff */
DestroyDisplay(void)261 void DestroyDisplay(void)
262 {
263   int i;
264 
265   if (!display_alloc) return;
266 
267   /* kill the font */
268   if (font_alloc) {
269       XFreeFont(dpy, finfo);
270       font_alloc = _false_;
271   }
272 
273   if (cmap_alloc) {
274       XFreeColormap(dpy, cmap);
275       cmap = DefaultColormap(dpy, scr);
276       cmap_alloc = _false_;
277   }
278 
279   /* Destroy everything allocated right around the gcs.  Help windows are
280    * freed here cause they are created about the same time.  (Yes, I know
281    * this could cause problems, it hasn't yet.
282    */
283   if(gc_alloc) {
284     XFreeGC(dpy, gc);
285     XFreeGC(dpy, rgc);
286     XFreeGC(dpy, drgc);
287     XFreePixmap(dpy, work);
288     for (i = 0; i < HELP_PAGES; i++)
289       XFreePixmap(dpy, help[i]);
290     gc_alloc = _false_;
291   }
292   /* free up all the allocated pix */
293   if(pix_alloc) {
294     XFreePixmap(dpy, man);
295     XFreePixmap(dpy, saveman);
296     XFreePixmap(dpy, goal);
297     XFreePixmap(dpy, treasure);
298     XFreePixmap(dpy, object);
299     XFreePixmap(dpy, floor);
300     XFreePixmap(dpy, blank);
301     for(i = 0; i < NUM_WALLS; i++)
302       if(i == 0 || optwalls)
303         XFreePixmap(dpy, walls[i]);
304     pix_alloc = _false_;
305   }
306   /* okay.. NOW we can destroy the main window and the display */
307     if (win_alloc) {
308 	XDestroyWindow(dpy, win);
309 	win_alloc = _false_;
310     }
311 }
312 
313 static Boolean full_pixmap[256];
314 
TryBitmapFile(char * template,Pixmap * pix,char * bitpath,char * fname,int map)315 static Boolean TryBitmapFile(char *template, Pixmap *pix, char *bitpath,
316 			     char *fname, int map)
317 {
318   unsigned int width, height;
319   int dum3, dum4;
320   sprintf(buf, template, bitpath, fname);
321 
322   if(XReadBitmapFile(dpy, win, buf, &width, &height, pix, &dum3, &dum4) ==
323        BitmapSuccess) {
324        if (width > bit_width) bit_width = width;
325        if (height > bit_height) bit_height = height;
326        full_pixmap[map] = _false_;
327        return _true_;
328   }
329   return _false_;
330 }
331 
332 #if USE_XPM
333 /*
334    Try to load an XPM file. Return 0 if success, E_NOCOLOR if there were
335    not enough colors to allocate for the pixmap, E_NOBITMAP if the pixmap
336    couldn't be loaded.
337 */
338 static Boolean xpm_color_failed = _false_;
339 
TryPixmapFile(char * template,Pixmap * pix,char * bitpath,char * fname,int map)340 static short TryPixmapFile(char *template, Pixmap *pix, char *bitpath,
341                              char *fname, int map)
342 {
343     int ret;
344     char *errmsg;
345     XpmAttributes attr;
346     XWindowAttributes wa;
347     if (xpm_color_failed)
348 	return E_NOCOLOR; /* Don't keep trying for each pixmap */
349     if (!XGetWindowAttributes(dpy, win, &wa)) {
350 	fprintf(stderr, "What? Can't get attributes of window\n");
351 	abort();
352     }
353     if (wa.depth < 8) {
354 	/* Hopeless! Not enough colors...*/
355 	return E_NOBITMAP;
356     }
357 
358     attr.valuemask = XpmCloseness | XpmExactColors | XpmColorKey | XpmColormap |
359 		    XpmDepth;
360     attr.colormap = wa.colormap;
361     attr.depth = wa.depth;
362     attr.color_key = XPM_COLOR;
363     attr.closeness = 10;
364     attr.exactColors = _false_;
365     sprintf(buf, template, bitpath, fname);
366     if ((ret = XpmReadFileToPixmap(dpy, win, buf, pix, NULL, &attr)) ==
367 	 XpmSuccess) {
368 
369 	if (attr.width > bit_width) bit_width = attr.width;
370 	if (attr.height > bit_height) bit_height = attr.height;
371 	full_pixmap[map] = _true_;
372 	return 0;
373     }
374     switch(ret) {
375       case XpmColorError: return 0; /* partial success */
376       case XpmSuccess: errmsg = "success"; break;
377       case XpmOpenFailed: return E_NOBITMAP; /* open failed */
378       case XpmFileInvalid: errmsg = "file format invalid"; break;
379       case XpmNoMemory: errmsg = "No memory"; break;
380       case XpmColorFailed: return E_NOCOLOR;
381       default: errmsg = "unknown error code"; break;
382     }
383     fprintf(stderr, "XpmReadFileToPixmap (%s) failed: %s\n", buf, errmsg);
384     return E_NOBITMAP;
385 }
386 #endif
387 
388 /* Load in a single bitmap.  If this bitmap is the largest in the x or
389  * y direction, set bit_width or bit_height appropriately.  If your pixmaps
390  * are of varying sizes, a bit_width by bit_height box is guaranteed to be
391  * able to surround all of them.
392  * Return 0 for success, E_NOBITMAP if a loadable bitmap could not be found,
393  * E_NOCOLOR if a loadable pixmap was found but there were not enough colors
394  * load it.
395  */
396 
LoadOneBitmap(char * fname,char * altname,Pixmap * pix,int map)397 short LoadOneBitmap(char *fname, char *altname, Pixmap *pix, int map)
398 {
399     if(bitpath && *bitpath) {
400 	/* we have something to try other than the default, let's do it */
401 #if USE_XPM
402 	switch(TryPixmapFile("%s/%s.xpm", pix, bitpath, fname, map)) {
403 	  case 0: return 0;
404 	  case E_NOCOLOR: return E_NOCOLOR;
405 	  case E_NOBITMAP: break;
406 	}
407 	switch(TryPixmapFile("%s/%s.xpm", pix, bitpath, altname, map)) {
408 	  case 0: return 0;
409 	  case E_NOCOLOR: return E_NOCOLOR;
410 	  case E_NOBITMAP: break;
411 	}
412 #endif
413 	if (TryBitmapFile("%s/%s.xbm", pix, bitpath, fname, map)) return 0;
414 	if (TryBitmapFile("%s/%s.xbm", pix, bitpath, altname, map)) return 0;
415 	return E_NOBITMAP;
416     }
417 
418 #if USE_XPM
419       switch(TryPixmapFile("%s/%s.xpm", pix, BITPATH, fname, map)) {
420 	case 0: return 0;
421 	case E_NOCOLOR: return E_NOCOLOR;
422 	case E_NOBITMAP: break;
423       }
424       switch(TryPixmapFile("%s/%s.xpm", pix, BITPATH, altname, map)) {
425 	case 0: return 0;
426 	case E_NOCOLOR: return E_NOCOLOR;
427 	case E_NOBITMAP: break;
428       }
429 #endif
430     if (TryBitmapFile("%s/%s.xbm", pix, BITPATH, fname, map)) return 0;
431     if (TryBitmapFile("%s/%s.xbm", pix, BITPATH, altname, map)) return 0;
432     return E_NOBITMAP;
433 }
434 
435 /* loads all the bitmaps in.. if any fail, it returns E_NOBITMAP up a level
436  * so the program can report the error to the user.  It tries to load in the
437  * alternates as well.
438  */
LoadBitmaps(void)439 short LoadBitmaps(void)
440 {
441     register int i;
442     short ret;
443 
444     if ((ret = LoadOneBitmap("man", NULL, &man, player))) return ret;
445     if ((ret = LoadOneBitmap("saveman", "man", &saveman, playerstore)))
446       return ret;
447     if ((ret = LoadOneBitmap("object", NULL, &object, packet))) return ret;
448     if ((ret = LoadOneBitmap("treasure", NULL, &treasure, save))) return ret;
449     if ((ret = LoadOneBitmap("goal", NULL, &goal, store))) return ret;
450     if ((ret = LoadOneBitmap("floor", NULL, &floor, ground))) return ret;
451 
452     if(optwalls) {
453 	for(i = 0; i < NUM_WALLS; i++) {
454 	    if ((ret = LoadOneBitmap(wallname[i], "wall", &walls[i], wall)))
455 	      return ret;
456 	}
457     } else {
458 	if ((ret = LoadOneBitmap("wall", NULL, &walls[0], wall))) return ret;
459     }
460     return 0;
461 }
462 
DrawPixmap(Drawable w,Pixmap p,int mapchar,int x,int y)463 static void DrawPixmap(Drawable w, Pixmap p, int mapchar, int x, int y)
464 {
465   if (full_pixmap[mapchar])
466       XCopyArea(dpy, p, w, gc, 0, 0, bit_width, bit_height, x, y);
467   else
468       XCopyPlane(dpy, p, w, gc, 0, 0, bit_width, bit_height, x, y, 1);
469 }
470 
471 /* Create and draw all the help windows.  This is not wholly foolproof with
472  * the variable-size bitmap code yet, as the constants to place things on the
473  * screen, are just that, constants.  This should be rewritten.
474  */
MakeHelpWindows(void)475 void MakeHelpWindows(void)
476 {
477   int i;
478   int ypos = 0;
479 #if WWW
480   char *title = "    Sokoban  --  X+WWW version 3.3c --  Help page %d";
481 #else
482   char *title = "    Sokoban  --  X version 3.3c --  Help page %d";
483 #endif
484   char *next = "     Press <Return> to exit";
485 
486   for(i = 0; i < HELP_PAGES; i++) {
487     XFillRectangle(dpy, help[i], drgc, 0, 0, HELP_W, HELP_H);
488     sprintf(buf, title, (i+1));
489     XDrawImageString(dpy, help[i], gc, 0, 11, buf, strlen(buf));
490     XDrawLine(dpy, help[i], gc, 0, 17, HELP_W, 17);
491     XDrawLine(dpy, help[i], gc, 0, HELP_H-20, HELP_W, HELP_H-20);
492     XDrawImageString(dpy, help[i], gc, 2, HELP_H-7, next, strlen(next));
493   }
494   for(i = 0; help_pages[i].textline != NULL; i++) {
495     ypos += help_pages[i].ydelta;
496     XDrawImageString(dpy,help[help_pages[i].page], gc,
497                      help_pages[i].xpos * (finfo->max_bounds.width),
498                      ypos, help_pages[i].textline,
499                      strlen(help_pages[i].textline));
500   }
501 
502   DrawPixmap(help[0], man, player, 180, 340);
503   DrawPixmap(help[0], goal, store, 280, 340);
504   DrawPixmap(help[0], walls[0], wall, 389, 340);
505   DrawPixmap(help[0], object, packet, 507, 340);
506   DrawPixmap(help[0], treasure, save, 270, 388);
507   DrawPixmap(help[0], saveman, playerstore, 507, 388);
508 }
509 
ClearScreen(void)510 void ClearScreen(void)
511 {
512   register int i,j;
513 
514   XFillRectangle(dpy, work, drgc, 0, 0, width, height);
515   for(i = 0; i < MAXROW; i++)
516     for(j = 0; j < MAXCOL; j++)
517       DrawPixmap(work, floor, ground, j*bit_width, i*bit_height);
518   XDrawLine(dpy, work, gc, 0, bit_height*MAXROW, bit_width*MAXCOL,
519             bit_height*MAXROW);
520 }
521 
RedisplayScreen(void)522 void RedisplayScreen(void)
523 {
524   if(hlpscrn == -1)
525     XCopyArea(dpy, work, win, gc, 0, 0, width, height, 0, 0);
526   else
527     XCopyArea(dpy, help[hlpscrn], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
528   XFlush(dpy);
529 }
530 
SyncScreen(void)531 void SyncScreen(void)
532 {
533   XSync(dpy, 0);
534 }
535 
ShowScreen(void)536 void ShowScreen(void)
537 {
538   register int i,j;
539 
540   for(i = 0; i < rows; i++)
541     for(j = 0; j < cols && map[i][j] != 0; j++)
542       MapChar(map[i][j], i, j, 0);
543   DisplayLevel();
544   DisplayPackets();
545   DisplaySave();
546   DisplayMoves();
547   DisplayPushes();
548   DisplayHelp();
549   RedisplayScreen();
550 }
551 
MapChar(char c,int i,int j,Boolean copy_area)552 void MapChar(char c, int i, int j, Boolean copy_area)
553 {
554   Pixmap this;
555 
556   this = GetObjectPixmap(i, j, c); /* i, j are passed so walls can be done */
557   if (full_pixmap[(int)c])
558   XCopyArea(dpy, this, work, gc, 0, 0, bit_width, bit_height, cX(j), cY(i));
559   else
560   XCopyPlane(dpy, this, work, gc, 0, 0, bit_width, bit_height, cX(j), cY(i), 1);
561   if (copy_area) {
562     XCopyArea(dpy, work, win, gc, cX(j), cY(i), bit_width, bit_height,
563 	      cX(j), cY(i));
564   }
565 }
566 
567 /* figures out the appropriate pixmap from the internal game representation.
568  * Handles fancy walls.
569  */
GetObjectPixmap(int i,int j,char c)570 Pixmap GetObjectPixmap(int i, int j, char c)
571 {
572   switch(c) {
573     case player: return man;
574     case playerstore: return saveman;
575     case store: return goal;
576     case save: return treasure;
577     case packet: return object;
578     case wall:
579        if(optwalls) return walls[PickWall(i,j)];
580        else return walls[0];
581     case ground: return floor;
582     default: return blank;
583   }
584 }
585 
586 /* returns and index into the fancy walls array. works by assigning a value
587  * to each 'position'.. the type of fancy wall is computed based on how
588  * many neighboring walls there are.
589  */
PickWall(int i,int j)590 int PickWall(int i, int j)
591 {
592   int ret = 0;
593 
594   if(i > 0 && map[i-1][j] == wall) ret += 1;
595   if(j < cols && map[i][j+1] == wall) ret += 2;
596   if(i < rows && map[i+1][j] == wall) ret += 4;
597   if(j > 0 && map[i][j-1] == wall) ret += 8;
598   return ret;
599 }
600 
601 /* Draws a string onto the working pixmap */
DrawString(int x,int y,char * text)602 void DrawString(int x, int y, char *text)
603 {
604   int x_off, y_off;
605 
606   x_off = x * finfo->max_bounds.width;
607   y_off = y + finfo->ascent;
608 
609   XDrawImageString(dpy, work, gc, x_off, y_off, text, strlen(text));
610 }
611 
612 /* The following routines display various 'statusline' stuff (ie moves, pushes,
613  * etc) on the screen.  they are called as they are needed to be changed to
614  * avoid unnecessary drawing */
DisplayLevel(void)615 void DisplayLevel(void)
616 {
617    sprintf(buf, "Level: %3d", level);
618    DrawString(0, STATUSLINE, buf);
619 }
620 
DisplayPackets(void)621 void DisplayPackets(void)
622 {
623    sprintf(buf, "Packets: %3d", packets);
624    DrawString(12, STATUSLINE, buf);
625 }
626 
DisplaySave(void)627 void DisplaySave(void)
628 {
629   sprintf(buf, "Saved: %3d", savepack);
630   DrawString(26, STATUSLINE, buf);
631 }
632 
DisplayMoves(void)633 void DisplayMoves(void)
634 {
635   sprintf(buf, "Moves: %5d", moves);
636   DrawString(38, STATUSLINE, buf);
637 }
638 
DisplayPushes(void)639 void DisplayPushes(void)
640 {
641   sprintf(buf, "Pushes: %3d", pushes);
642   DrawString(52, STATUSLINE, buf);
643 }
644 
DisplayHelp(void)645 void DisplayHelp(void)
646 {
647   DrawString(0, HELPLINE, "Press ? for help.");
648 }
649 
650 /* Function used by the help pager.  We ONLY want to flip pages if a key
651  * key is pressed.. We want to exit the help pager if ENTER is pressed.
652  * As above, <shift> and <control> and other such fun keys are NOT counted
653  * as a keypress.
654  */
WaitForEnter(void)655 Boolean WaitForEnter(void)
656 {
657   KeySym keyhit;
658   char buf[1];
659   int bufs = 1;
660   XComposeStatus compose;
661   XEvent xev;
662 
663   while (1) {
664     XNextEvent(dpy, &xev);
665     switch(xev.type) {
666       case Expose:
667 	RedisplayScreen();
668 	break;
669       case KeyPress:
670 	buf[0] = '\0';
671 	XLookupString(&xev.xkey, buf, bufs, &keyhit, &compose);
672 	if(buf[0]) {
673 	  return (keyhit == XK_Return) ? _true_ : _false_;
674 	}
675 	break;
676       default:
677 	break;
678     }
679   }
680 }
681 
ShowHelp(void)682 void ShowHelp(void)
683 {
684   int i = 0;
685   Boolean done = _false_;
686 
687   hlpscrn = 0;
688   XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
689   XFlush(dpy);
690   while(!done) {
691     done = WaitForEnter();
692     if(done) {
693       hlpscrn = -1;
694       return;
695     } else {
696       i = (i+1)%HELP_PAGES;
697       hlpscrn = i;
698       XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
699       XFlush(dpy);
700     }
701   }
702 }
703 
DisplayScores(short * newlev)704 short DisplayScores(short *newlev)
705 {
706     return DisplayScores_(dpy, win, newlev);
707 }
708 
709 
HelpMessage(void)710 void HelpMessage(void)
711 {
712   XBell(dpy, 0);
713   RedisplayScreen();
714 }
715