home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xsokoban-31 / display.c < prev    next >
C/C++ Source or Header  |  1997-06-13  |  19KB  |  671 lines

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