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