]> granicus.if.org Git - nethack/commitdiff
*** empty log message ***
authorjwalz <jwalz>
Sat, 5 Jan 2002 21:06:03 +0000 (21:06 +0000)
committerjwalz <jwalz>
Sat, 5 Jan 2002 21:06:03 +0000 (21:06 +0000)
win/X11/winX.c [new file with mode: 0644]

diff --git a/win/X11/winX.c b/win/X11/winX.c
new file mode 100644 (file)
index 0000000..0b43eb9
--- /dev/null
@@ -0,0 +1,2065 @@
+/*     SCCS Id: @(#)winX.c     3.3     1999/12/21      */
+/* Copyright (c) Dean Luick, 1992                                */
+/* NetHack may be freely redistributed.  See license for details. */
+
+/*
+ * "Main" file for the X window-port.  This contains most of the interface
+ * routines.  Please see doc/window.doc for an description of the window
+ * interface.
+ */
+
+#ifndef SYSV
+#define PRESERVE_NO_SYSV       /* X11 include files may define SYSV */
+#endif
+
+#ifdef MSDOS                   /* from compiler */
+#define SHORT_FILENAMES
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Cardinals.h>
+#include <X11/Xatom.h>
+#include <X11/Xos.h>
+
+/* for color support */
+#ifdef SHORT_FILENAMES
+#include <X11/IntrinsP.h>
+#else
+#include <X11/IntrinsicP.h>
+#endif
+
+#ifdef PRESERVE_NO_SYSV
+# ifdef SYSV
+#  undef SYSV
+# endif
+# undef PRESERVE_NO_SYSV
+#endif
+
+#ifdef SHORT_FILENAMES
+#undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
+#endif
+
+#include "hack.h"
+#include "winX.h"
+#include "dlb.h"
+#ifdef SHORT_FILENAMES
+#include "patchlev.h"
+#else
+#include "patchlevel.h"
+#endif
+
+/* Should be defined in <X11/Intrinsic.h> but you never know */
+#ifndef XtSpecificationRelease
+#define XtSpecificationRelease 0
+#endif
+
+/*
+ * Icons.
+ */
+#include "../win/X11/nh72icon"
+#include "../win/X11/nh56icon"
+#include "../win/X11/nh32icon"
+
+static struct icon_info {
+       const char *name;
+       unsigned char *bits;
+       unsigned width, height;
+} icon_data[] = {
+       { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
+       { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
+       { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
+       { (const char *)0, (unsigned char *)0, 0, 0 }
+};
+
+/*
+ * Private global variables (shared among the window port files).
+ */
+struct xwindow window_list[MAX_WINDOWS];
+AppResources appResources;
+void FDECL((*input_func), (Widget,XEvent *,String *,Cardinal *));
+int click_x, click_y, click_button;    /* Click position on a map window   */
+                                       /* (filled by set_button_values()). */
+int updated_inventory;
+
+
+/* Interface definition, for windows.c */
+struct window_procs X11_procs = {
+    "X11",
+    X11_init_nhwindows,
+    X11_player_selection,
+    X11_askname,
+    X11_get_nh_event,
+    X11_exit_nhwindows,
+    X11_suspend_nhwindows,
+    X11_resume_nhwindows,
+    X11_create_nhwindow,
+    X11_clear_nhwindow,
+    X11_display_nhwindow,
+    X11_destroy_nhwindow,
+    X11_curs,
+    X11_putstr,
+    X11_display_file,
+    X11_start_menu,
+    X11_add_menu,
+    X11_end_menu,
+    X11_select_menu,
+    genl_message_menu,         /* no need for X-specific handling */
+    X11_update_inventory,
+    X11_mark_synch,
+    X11_wait_synch,
+#ifdef CLIPPING
+    X11_cliparound,
+#endif
+#ifdef POSITIONBAR
+    donull,
+#endif
+    X11_print_glyph,
+    X11_raw_print,
+    X11_raw_print_bold,
+    X11_nhgetch,
+    X11_nh_poskey,
+    X11_nhbell,
+    X11_doprev_message,
+    X11_yn_function,
+    X11_getlin,
+    X11_get_ext_cmd,
+    X11_number_pad,
+    X11_delay_output,
+#ifdef CHANGE_COLOR    /* only a Mac option currently */
+    donull,
+    donull,
+#endif
+    /* other defs that really should go away (they're tty specific) */
+    X11_start_screen,
+    X11_end_screen,
+#ifdef GRAPHIC_TOMBSTONE
+    X11_outrip,
+#else
+    genl_outrip,
+#endif
+};
+
+/*
+ * Local functions.
+ */
+static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*));
+static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*));
+static int FDECL(input_event, (int));
+static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *));
+static void NDECL(init_standard_windows);
+
+
+/*
+ * Local variables.
+ */
+static boolean x_inited = FALSE;       /* TRUE if window system is set up. */
+static winid message_win = WIN_ERR,    /* These are the winids of the      */
+            map_win     = WIN_ERR,     /*   message, map, and status       */
+            status_win  = WIN_ERR;     /*   windows, when they are created */
+                                       /*   in init_windows().             */
+static Pixmap icon_pixmap = None;      /* Pixmap for icon.                 */
+
+/*
+ * Find the window structure that corresponds to the given widget.  Note
+ * that this is not the popup widget, nor the viewport, but the child.
+ */
+struct xwindow *
+find_widget(w)
+    Widget w;
+{
+    int windex;
+    struct xwindow *wp;
+
+    /* Search to find the corresponding window.  Look at the main widget, */
+    /* popup, the parent of the main widget, then parent of the widget. */
+    for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
+       if (wp->type != NHW_NONE &&
+           (wp->w == w || wp->popup == w || (wp->w && (XtParent(wp->w)) == w)
+               || (wp->popup == XtParent(w))))
+           break;
+
+    if (windex == MAX_WINDOWS) panic("find_widget:  can't match widget");
+    return wp;
+}
+
+/*
+ * Find a free window slot for use.
+ */
+static winid
+find_free_window()
+{
+    int windex;
+    struct xwindow *wp;
+
+    for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++)
+       if (wp->type == NHW_NONE) break;
+
+    if (windex == MAX_WINDOWS)
+       panic("find_free_window: no free windows!");
+    return (winid) windex;
+}
+
+/*
+ * Color conversion.  The default X11 color converters don't try very
+ * hard to find matching colors in PseudoColor visuals.  If they can't
+ * allocate the exact color, they puke and give you something stupid.
+ * This is an attempt to find some close readonly cell and use it.
+ */
+XtConvertArgRec const nhcolorConvertArgs[] = {
+    {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen),
+     sizeof(Screen *)},
+    {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap),
+     sizeof(Colormap)}
+};
+
+#define done(type, value) \
+       {                                                       \
+           if (toVal->addr != 0) {                             \
+               if (toVal->size < sizeof(type)) {               \
+                   toVal->size = sizeof(type);                 \
+                   return False;                               \
+               }                                               \
+               *(type*)(toVal->addr) = (value);                \
+           }                                                   \
+           else {                                              \
+               static type static_val;                         \
+               static_val = (value);                           \
+               toVal->addr = (genericptr_t)&static_val;        \
+           }                                                   \
+           toVal->size = sizeof(type);                         \
+           return True;                                        \
+       }
+
+/* decl.h declares these, but it screws up structure references -dlc */
+#undef red
+#undef green
+#undef blue
+
+/*
+ * Find a color that approximates the color named in "str".  The "str" color
+ * may be a color name ("red") or number ("#7f0000").  If str == NULL, then
+ * "color" is assumed to contain the RGB color wanted.
+ * The approximate color found is returned in color as well.
+ * Return True if something close was found.
+ */
+Boolean
+nhApproxColor(screen, colormap, str, color)
+Screen  *screen;       /* screen to use */
+Colormap colormap;     /* the colormap to use */
+char     *str;         /* color name */
+XColor   *color;       /* the X color structure; changed only if successful */
+{
+    int                ncells;
+    long       cdiff = 16777216; /* 2^24; hopefully our map is smaller */
+    XColor     tmp;
+    static     XColor *table = 0;
+    register int i, j;
+    register long tdiff;
+
+    /* if the screen doesn't have a big colormap, don't waste our time */
+    /* or if it's huge, and _some_ match should have been possible */
+    if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
+       return False;
+
+    if (str != (char *)0) {
+       if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
+           return False;
+    } else {
+       tmp = *color;
+       tmp.flags = 7;  /* force to use all 3 of RGB */
+    }
+
+    if (!table) {
+       table = (XColor *) XtCalloc(ncells, sizeof(XColor));
+       for(i=0; i<ncells; i++)
+           table[i].pixel = i;
+       XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
+    }
+
+    /* go thru cells and look for the one with smallest diff */
+    /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
+    /* a more knowledgeable color person might improve this -dlc */
+try_again:
+    for(i=0; i<ncells; i++) {
+       if(table[i].flags == tmp.flags) {
+           j = (int)table[i].red - (int)tmp.red;
+           if(j < 0) j = -j;
+           tdiff = j;
+           j = (int)table[i].green - (int)tmp.green;
+           if(j < 0) j = -j;
+           tdiff += j;
+           j = (int)table[i].blue - (int)tmp.blue;
+           if(j < 0) j = -j;
+           tdiff += j;
+           if(tdiff < cdiff) {
+               cdiff = tdiff;
+               tmp.pixel = i; /* table[i].pixel == i */
+           }
+       }
+    }
+
+    if(cdiff == 16777216) return False;        /* nothing found?! */
+
+    /*
+     * Found something.  Return it and mark this color as used to avoid
+     * reuse.  Reuse causes major contrast problems :-)
+     */
+    *color = table[tmp.pixel];
+    table[tmp.pixel].flags = 0;
+    /* try to alloc the color, so no one else can change it */
+    if(!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
+       cdiff = 16777216;
+       goto try_again;
+    }
+    return True;
+}
+
+Boolean
+nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
+Display*       dpy;
+XrmValuePtr    args;
+Cardinal       *num_args;
+XrmValuePtr    fromVal;
+XrmValuePtr    toVal;
+XtPointer      *closure_ret;
+{
+    String         str = (String)fromVal->addr;
+    XColor         screenColor;
+    XColor         exactColor;
+    Screen         *screen;
+    XtAppContext    app = XtDisplayToApplicationContext(dpy);
+    Colormap       colormap;
+    Status         status;
+    String          params[1];
+    Cardinal       num_params=1;
+
+    if (*num_args != 2) {
+     XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel",
+       "XtToolkitError",
+       "String to pixel conversion needs screen and colormap arguments",
+       (String *)0, (Cardinal *)0);
+     return False;
+    }
+
+    screen = *((Screen **) args[0].addr);
+    colormap = *((Colormap *) args[1].addr);
+
+    /* If Xt colors, use the Xt routine and hope for the best */
+#if (XtSpecificationRelease >= 5)
+    if ((strcmpi(str, XtDefaultBackground) == 0) ||
+       (strcmpi(str, XtDefaultForeground) == 0)) {
+       return
+         XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret);
+    }
+#else
+    if (strcmpi(str, XtDefaultBackground) == 0) {
+       *closure_ret = (char*)False;
+       done(Pixel, WhitePixelOfScreen(screen));
+    }
+    if (strcmpi(str, XtDefaultForeground) == 0) {
+       *closure_ret = (char*)False;
+       done(Pixel, BlackPixelOfScreen(screen));
+    }
+#endif
+
+    status = XAllocNamedColor(DisplayOfScreen(screen), colormap,
+                             (char*)str, &screenColor, &exactColor);
+    if (status == 0) {
+       String msg, type;
+
+       /* some versions of XAllocNamedColor don't allow #xxyyzz names */
+       if (str[0] == '#' &&
+           XParseColor(DisplayOfScreen(screen), colormap, str, &exactColor) &&
+           XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
+               *closure_ret = (char*)True;
+               done(Pixel, exactColor.pixel);
+       }
+
+       params[0] = str;
+       /* Server returns a specific error code but Xlib discards it.  Ugh */
+       if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str,
+                        &exactColor, &screenColor)) {
+           /* try to find another color that will do */
+           if (nhApproxColor(screen, colormap, (char*) str, &screenColor)) {
+               *closure_ret = (char*)True;
+               done(Pixel, screenColor.pixel);
+           }
+           type = "noColormap";
+           msg = "Cannot allocate colormap entry for \"%s\"";
+       }
+       else {
+           /* some versions of XLookupColor also don't allow #xxyyzz names */
+           if(str[0] == '#' &&
+              (nhApproxColor(screen, colormap, (char*) str, &screenColor))) {
+               *closure_ret = (char*)True;
+               done(Pixel, screenColor.pixel);
+           }
+           type = "badValue";
+           msg = "Color name \"%s\" is not defined";
+       }
+
+       XtAppWarningMsg(app, type, "cvtStringToPixel",
+                       "XtToolkitError", msg, params, &num_params);
+       *closure_ret = False;
+       return False;
+    } else {
+       *closure_ret = (char*)True;
+       done(Pixel, screenColor.pixel);
+    }
+}
+
+/* ARGSUSED */
+static void
+nhFreePixel(app, toVal, closure, args, num_args)
+XtAppContext   app;
+XrmValuePtr    toVal;
+XtPointer      closure;
+XrmValuePtr    args;
+Cardinal       *num_args;
+{
+    Screen         *screen;
+    Colormap       colormap;
+
+    if (*num_args != 2) {
+     XtAppWarningMsg(app, "wrongParameters",
+                    "freePixel", "XtToolkitError",
+                    "Freeing a pixel requires screen and colormap arguments",
+                    (String *)0, (Cardinal *)0);
+     return;
+    }
+
+    screen = *((Screen **) args[0].addr);
+    colormap = *((Colormap *) args[1].addr);
+
+    if (closure) {
+       XFreeColors( DisplayOfScreen(screen), colormap,
+                    (unsigned long*)toVal->addr, 1, (unsigned long)0
+                   );
+    }
+}
+
+/* [ALI] Utility function to ask Xaw for font height, since the previous
+ * assumption of ascent + descent is not always valid.
+ */
+Dimension
+nhFontHeight(w)
+Widget w;
+#ifdef _XawTextSink_h
+{
+    Widget sink;
+    XawTextPosition pos = 0;
+    int resWidth, resHeight;
+    Arg args[1];
+
+    XtSetArg(args[0], XtNtextSink, &sink);
+    XtGetValues(w, args, 1);
+
+    XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
+    return resHeight;
+}
+#else
+{
+    XFontStruct *fs;
+    Arg args[1];
+
+    XtSetArg(args[0], XtNfont, &fs);
+    XtGetValues(w, args, 1);
+
+    /* Assume font height is ascent + descent. */
+    return = fs->ascent + fs->descent;
+}
+#endif
+
+/* Global Functions ======================================================== */
+void
+X11_raw_print(str)
+    const char *str;
+{
+    (void) puts(str);
+}
+
+void
+X11_raw_print_bold(str)
+    const char *str;
+{
+    (void) puts(str);
+}
+
+void
+X11_curs(window, x, y)
+    winid window;
+    int x, y;
+{
+    check_winid(window);
+
+    if (x < 0 || x >= COLNO) {
+       impossible("curs:  bad x value [%d]", x);
+       x = 0;
+    }
+    if (y < 0 || y >= ROWNO) {
+       impossible("curs:  bad y value [%d]", y);
+       y = 0;
+    }
+
+    window_list[window].cursx = x;
+    window_list[window].cursy = y;
+}
+
+void
+X11_putstr(window, attr, str)
+    winid window;
+    int attr;
+    const char *str;
+{
+    winid new_win;
+    struct xwindow *wp;
+
+    check_winid(window);
+    wp = &window_list[window];
+
+    switch (wp->type) {
+       case NHW_MESSAGE:
+           (void) strncpy(toplines, str, TBUFSZ);      /* for Norep(). */
+           toplines[TBUFSZ - 1] = 0;
+           append_message(wp, str);
+           break;
+       case NHW_STATUS:
+           adjust_status(wp, str);
+           break;
+       case NHW_MAP:
+           impossible("putstr: called on map window \"%s\"", str);
+           break;
+       case NHW_MENU:
+           if (wp->menu_information->is_menu) {
+               impossible(
+                       "putstr:  called on a menu window, \"%s\" discarded",
+                       str);
+               break;
+           }
+           /*
+            * Change this menu window into a text window by creating a
+            * new text window, then copying it to this winid.
+            */
+           new_win = X11_create_nhwindow(NHW_TEXT);
+           X11_destroy_nhwindow(window);
+           *wp = window_list[new_win];
+           window_list[new_win].type = NHW_NONE;       /* allow re-use */
+           /* fall though to add text */
+       case NHW_TEXT:
+           add_to_text_window(wp, attr, str);
+           break;
+       default:
+           impossible("putstr: unknown window type [%d] \"%s\"",
+                                                           wp->type, str);
+    }
+}
+
+/* We do event processing as a callback, so this is a null routine. */
+void X11_get_nh_event() { return; }
+
+int
+X11_nhgetch()
+{
+    return input_event(EXIT_ON_KEY_PRESS);
+}
+
+
+int
+X11_nh_poskey(x, y, mod)
+    int *x, *y, *mod;
+{
+    int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
+
+    if (val == 0) {    /* user clicked on a map window */
+       *x   = click_x;
+       *y   = click_y;
+       *mod = click_button;
+    }
+    return val;
+}
+
+
+winid
+X11_create_nhwindow(type)
+    int type;
+{
+    winid window;
+    struct xwindow *wp;
+
+    if (!x_inited)
+       panic("create_nhwindow:  windows not initialized");
+
+    /*
+     * We have already created the standard message, map, and status
+     * windows in the window init routine.  The first window of that
+     * type to be created becomes the standard.
+     *
+     * A better way to do this would be to say that init_nhwindows()
+     * has already defined these three windows.
+     */
+    if (type == NHW_MAP && map_win != WIN_ERR) {
+       window = map_win;
+       map_win = WIN_ERR;
+       return window;
+    }
+    if (type == NHW_MESSAGE && message_win != WIN_ERR) {
+       window = message_win;
+       message_win = WIN_ERR;
+       return window;
+    }
+    if (type == NHW_STATUS && status_win != WIN_ERR) {
+       window = status_win;
+       status_win = WIN_ERR;
+       return window;
+    }
+
+    window = find_free_window();
+    wp = &window_list[window];
+
+    /* The create routines will set type, popup, w, and Win_info. */
+    wp->prevx = wp->prevy = wp->cursx = wp->cursy =
+                               wp->pixel_width = wp->pixel_height = 0;
+    wp->keep_window = FALSE;
+
+    switch (type) {
+       case NHW_MAP:
+           create_map_window(wp, TRUE, (Widget) 0);
+           break;
+       case NHW_MESSAGE:
+           create_message_window(wp, TRUE, (Widget) 0);
+           break;
+       case NHW_STATUS:
+           create_status_window(wp, TRUE, (Widget) 0);
+           break;
+       case NHW_MENU:
+           create_menu_window(wp);
+           break;
+       case NHW_TEXT:
+           create_text_window(wp);
+           break;
+       default:
+           panic("create_nhwindow: unknown type [%d]\n", type);
+           break;
+    }
+    return window;
+}
+
+void
+X11_clear_nhwindow(window)
+    winid window;
+{
+    struct xwindow *wp;
+
+    check_winid(window);
+    wp = &window_list[window];
+
+    switch (wp->type) {
+       case NHW_MAP:   clear_map_window(wp); break;
+       case NHW_TEXT:  clear_text_window(wp); break;
+       case NHW_STATUS:
+       case NHW_MENU:
+       case NHW_MESSAGE:
+           /* do nothing for these window types */
+           break;
+       default:
+           panic("clear_nhwindow: unknown window type [%d]\n", wp->type);
+           break;
+    }
+}
+
+void
+X11_display_nhwindow(window, blocking)
+    winid window;
+    boolean blocking;
+{
+    struct xwindow *wp;
+
+    check_winid(window);
+    wp = &window_list[window];
+
+    switch (wp->type) {
+       case NHW_MAP:
+           if (wp->popup)
+               nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
+           display_map_window(wp);     /* flush map */
+           /*
+            * We need to flush the message window here due to the way the tty
+            * port is set up.  To flush a window, you need to call this
+            * routine.  However, the tty port _pauses_ with a --more-- if we
+            * do a display_nhwindow(WIN_MESSAGE, FALSE).  Thus, we can't call
+            * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
+            * get a --more-- after every line.
+            *
+            * Perhaps the window document should mention that when the map
+            * is flushed, everything on the three main windows should be
+            * flushed.  Note: we don't need to flush the status window
+            * because we don't buffer changes.
+            */
+           if (WIN_MESSAGE != WIN_ERR)
+               display_message_window(&window_list[WIN_MESSAGE]);
+           if (blocking)
+               (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
+           break;
+       case NHW_MESSAGE:
+           if (wp->popup)
+                nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
+           display_message_window(wp); /* flush messages */
+           break;
+       case NHW_STATUS:
+           if (wp->popup)
+               nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
+           break;                      /* no flushing necessary */
+       case NHW_MENU: {
+           int n;
+           menu_item *selected;
+
+           /* pop up menu */
+           n = X11_select_menu(window, PICK_NONE, &selected);
+           if (n) {
+               impossible("perminvent: %d selected??", n);
+               free((genericptr_t)selected);
+           }
+           break;
+       }
+       case NHW_TEXT:
+           display_text_window(wp, blocking);  /* pop up text window */
+           break;
+       default:
+           panic("display_nhwindow: unknown window type [%d]\n", wp->type);
+           break;
+    }
+}
+
+void
+X11_destroy_nhwindow(window)
+    winid window;
+{
+    struct xwindow *wp;
+
+    check_winid(window);
+    wp = &window_list[window];
+    /*
+     * "Zap" known windows, but don't destroy them.  We need to keep the
+     * toplevel widget popped up so that later windows (e.g. tombstone)
+     * are visible on DECWindow systems.  This is due to the virtual
+     * roots that the DECWindow wm creates.
+     */
+    if (window == WIN_MESSAGE) {
+       wp->keep_window = TRUE;
+       WIN_MESSAGE = WIN_ERR;
+       iflags.window_inited = 0;
+    } else if (window == WIN_MAP) {
+       wp->keep_window = TRUE;
+       WIN_MAP = WIN_ERR;
+    } else if (window == WIN_STATUS) {
+       wp->keep_window = TRUE;
+       WIN_STATUS = WIN_ERR;
+    } else if (window == WIN_INVEN) {
+       /* don't need to keep this one */
+       WIN_INVEN = WIN_ERR;
+    }
+
+    switch (wp->type) {
+       case NHW_MAP:
+           destroy_map_window(wp);
+           break;
+       case NHW_MENU:
+           destroy_menu_window(wp);
+           break;
+       case NHW_TEXT:
+           destroy_text_window(wp);
+           break;
+       case NHW_STATUS:
+           destroy_status_window(wp);
+           break;
+       case NHW_MESSAGE:
+           destroy_message_window(wp);
+           break;
+       default:
+           panic("destroy_nhwindow: unknown window type [%d]", wp->type);
+           break;
+    }
+}
+
+void
+X11_update_inventory()
+{
+    if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
+       updated_inventory = 1;  /* hack to avoid mapping&raising window */
+       (void) display_inventory((char *)0, FALSE);
+       updated_inventory = 0;
+    }
+}
+
+/* The current implementation has all of the saved lines on the screen. */
+int X11_doprev_message() { return 0; }
+
+void
+X11_nhbell()
+{
+    /* We can't use XBell until toplevel has been initialized. */
+    if (x_inited)
+       XBell(XtDisplay(toplevel), 0);
+    /* else print ^G ?? */
+}
+
+void X11_mark_synch()
+{
+    if (x_inited) {
+       /*
+        * The window document is unclear about the status of text
+        * that has been pline()d but not displayed w/display_nhwindow().
+        * Both the main and tty code assume that a pline() followed
+        * by mark_synch() results in the text being seen, even if
+        * display_nhwindow() wasn't called.  Duplicate this behavior.
+        */
+       if (WIN_MESSAGE != WIN_ERR)
+           display_message_window(&window_list[WIN_MESSAGE]);
+       XSync(XtDisplay(toplevel), False);
+    }
+}
+
+void X11_wait_synch() { if (x_inited) XFlush(XtDisplay(toplevel)); }
+
+
+/* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
+void X11_resume_nhwindows() { return; }
+
+/* ARGSUSED */
+void X11_suspend_nhwindows(str) const char *str; { return; }
+
+/* Under X, we don't need to initialize the number pad. */
+/* ARGSUSED */
+void X11_number_pad(state) int state; { return; } /* called from options.c */
+
+
+void X11_start_screen() { return; } /* called from setftty() in unixtty.c */
+void X11_end_screen() { return; }   /* called from settty() in unixtty.c */
+
+#ifdef GRAPHIC_TOMBSTONE
+void X11_outrip(window, how)
+    winid window;
+    int how;
+{
+    struct xwindow *wp;
+
+    check_winid(window);
+    wp = &window_list[window];
+
+    if (wp->type == NHW_TEXT) {
+       wp->text_information->is_rip = TRUE;
+    } else {
+       panic("ripout on non-text window (window type [%d])\n", wp->type);
+    }
+
+    calculate_rip_text(how);
+}
+#endif
+
+/* init and exit nhwindows ------------------------------------------------- */
+
+XtAppContext app_context;              /* context of application */
+Widget      toplevel = (Widget) 0;     /* toplevel widget */
+Atom         wm_delete_window;         /* pop down windows */
+
+static XtActionsRec actions[] = {
+    {"dismiss_file",   dismiss_file},  /* action for file viewing widget */
+    {"delete_file",    delete_file},   /* action for file delete-window */
+    {"dismiss_text",   dismiss_text},  /* button action for text widget */
+    {"delete_text",    delete_text},   /* delete action for text widget */
+    {"key_dismiss_text",key_dismiss_text},/* key action for text widget */
+#ifdef GRAPHIC_TOMBSTONE
+    {"rip_dismiss_text",rip_dismiss_text},/* action for rip in text widget */
+#endif
+    {"menu_key",       menu_key},      /* action for menu accelerators */
+    {"yn_key",         yn_key},        /* action for yn accelerators */
+    {"yn_delete",      yn_delete},     /* action for yn delete-window */
+    {"askname_delete", askname_delete},/* action for askname delete-window */
+    {"getline_delete", getline_delete},/* action for getline delete-window */
+    {"menu_delete",    menu_delete},   /* action for menu delete-window */
+    {"ec_key",         ec_key},        /* action for extended commands */
+    {"ec_delete",      ec_delete},     /* action for ext-com menu delete */
+    {"ps_key",         ps_key},        /* action for player selection */
+    {"race_key",       race_key},      /* action for race selection */
+    {"gend_key",       gend_key},      /* action for gender selection */
+    {"algn_key",       algn_key},      /* action for alignment selection */
+    {"X11_hangup",     X11_hangup},    /* action for delete of top-level */
+    {"input",          map_input},     /* action for key input */
+    {"scroll",         nh_keyscroll},  /* action for scrolling by keys */
+};
+
+static XtResource resources[] = {
+    { "slow", "Slow", XtRBoolean, sizeof(Boolean),
+      XtOffset(AppResources *,slow), XtRString, "False" },
+    { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean),
+      XtOffset(AppResources *,autofocus), XtRString, "False" },
+    { "message_line", "Message_line", XtRBoolean, sizeof(Boolean),
+      XtOffset(AppResources *,message_line), XtRString, "False" },
+    { "double_tile_size", "Double_tile_size", XtRBoolean, sizeof(Boolean),
+      XtOffset(AppResources *,double_tile_size), XtRString, "False" },
+    { "tile_file", "Tile_file", XtRString, sizeof(String),
+      XtOffset(AppResources *,tile_file), XtRString, "" },
+    { "icon", "Icon", XtRString, sizeof(String),
+      XtOffset(AppResources *,icon), XtRString, "nh72" },
+    { "message_lines", "Message_lines", XtRInt, sizeof(int),
+      XtOffset(AppResources *,message_lines), XtRString, "12" },
+    { "pet_mark_bitmap", "Pet_mark_bitmap", XtRString, sizeof(String),
+      XtOffset(AppResources *,pet_mark_bitmap), XtRString, "pet_mark.xbm" },
+    { "pet_mark_color", "Pet_mark_color", XtRPixel, sizeof(XtRPixel),
+      XtOffset(AppResources *,pet_mark_color), XtRString, "Red" },
+#ifdef GRAPHIC_TOMBSTONE
+    { "tombstone", "Tombstone", XtRString, sizeof(String),
+      XtOffset(AppResources *,tombstone), XtRString, "rip.xpm" },
+    { "tombtext_x", "Tombtext_x", XtRInt, sizeof(int),
+      XtOffset(AppResources *,tombtext_x), XtRString, "155" },
+    { "tombtext_y", "Tombtext_y", XtRInt, sizeof(int),
+      XtOffset(AppResources *,tombtext_y), XtRString, "78" },
+    { "tombtext_dx", "Tombtext_dx", XtRInt, sizeof(int),
+      XtOffset(AppResources *,tombtext_dx), XtRString, "0" },
+    { "tombtext_dy", "Tombtext_dy", XtRInt, sizeof(int),
+      XtOffset(AppResources *,tombtext_dy), XtRString, "13" },
+#endif
+};
+
+void
+X11_init_nhwindows(argcp,argv)
+int* argcp;
+char** argv;
+{
+    static const char *banner_text[] = {
+       COPYRIGHT_BANNER_A,
+       COPYRIGHT_BANNER_B,
+       COPYRIGHT_BANNER_C,
+       "",
+       "",
+       0
+    };
+    register const char **pp;
+    int i;
+    Cardinal num_args;
+    Arg args[4];
+    uid_t savuid;
+
+    /* Init windows to nothing. */
+    for (i = 0; i < MAX_WINDOWS; i++)
+       window_list[i].type = NHW_NONE;
+
+    /*
+     * setuid hack: make sure that if nethack is setuid, to use real uid
+     * when opening X11 connections, in case the user is using xauth, since
+     * the "games" or whatever user probably doesn't have permission to open
+     * a window on the user's display.  This code is harmless if the binary
+     * is not installed setuid.  See include/system.h on compilation failures.
+     */
+    savuid = geteuid();
+    (void) seteuid(getuid());
+
+    XSetIOErrorHandler((XIOErrorHandler) hangup);
+
+    num_args = 0;
+    XtSetArg(args[num_args], XtNallowShellResize, True);       num_args++;
+    toplevel = XtAppInitialize(
+                   &app_context,
+                   "NetHack",                  /* application class */
+                   (XrmOptionDescList)0, 0,    /* options list */
+                   argcp, (String *)argv,      /* command line args */
+                   (String *)0,                /* fallback resources */
+                   (ArgList)args, num_args);
+    XtOverrideTranslations(toplevel,
+       XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
+
+    /* We don't need to realize the top level widget. */
+
+#ifdef TEXTCOLOR
+    /* add new color converter to deal with overused colormaps */
+    XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
+                      (XtConvertArgList)nhcolorConvertArgs,
+                      XtNumber(nhcolorConvertArgs),
+                      XtCacheByDisplay, nhFreePixel);
+#endif /* TEXTCOLOR */
+
+    /* Register the actions mentioned in "actions". */
+    XtAppAddActions(app_context, actions, XtNumber(actions));
+
+    /* Get application-wide resources */
+    XtGetApplicationResources(toplevel, (XtPointer)&appResources,
+                             resources, XtNumber(resources),
+                             (ArgList)0, ZERO);
+
+    /* Initialize other things. */
+    init_standard_windows();
+
+    /* Give the window manager an icon to use;  toplevel must be realized. */
+    if (appResources.icon && *appResources.icon) {
+       struct icon_info *ip;
+
+       for (ip = icon_data; ip->name; ip++)
+           if (!strcmp(appResources.icon, ip->name)) {
+               icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel),
+                               XtWindow(toplevel),
+                               (genericptr_t)ip->bits, ip->width, ip->height);
+               if (icon_pixmap != None) {
+                   XWMHints hints;
+
+                   (void) memset((genericptr_t)&hints, 0, sizeof(XWMHints));
+                   hints.flags = IconPixmapHint;
+                   hints.icon_pixmap = icon_pixmap;
+                   XSetWMHints(XtDisplay(toplevel),
+                               XtWindow(toplevel), &hints);
+               }
+               break;
+           }
+    }
+
+    /* end of setuid hack: reset uid back to the "games" uid */
+    (void) seteuid(savuid);
+
+    x_inited = TRUE;   /* X is now initialized */
+
+    /* Display the startup banner in the message window. */
+    for (pp = banner_text; *pp; pp++)
+       X11_putstr(WIN_MESSAGE, 0, *pp);
+}
+
+/*
+ * All done.
+ */
+/* ARGSUSED */
+void X11_exit_nhwindows(dummy)
+    const char *dummy;
+{
+    extern Pixmap tile_pixmap; /* from winmap.c */
+
+    /* explicitly free the icon and tile pixmaps */
+    if (icon_pixmap != None) {
+       XFreePixmap(XtDisplay(toplevel), icon_pixmap);
+       icon_pixmap = None;
+    }
+    if (tile_pixmap != None) {
+       XFreePixmap(XtDisplay(toplevel), tile_pixmap);
+       tile_pixmap = None;
+    }
+    if (WIN_INVEN != WIN_ERR)
+       X11_destroy_nhwindow(WIN_INVEN);
+    if (WIN_STATUS != WIN_ERR)
+       X11_destroy_nhwindow(WIN_STATUS);
+    if (WIN_MAP != WIN_ERR)
+       X11_destroy_nhwindow(WIN_MAP);
+    if (WIN_MESSAGE != WIN_ERR)
+       X11_destroy_nhwindow(WIN_MESSAGE);
+}
+
+
+/* delay_output ------------------------------------------------------------ */
+
+/*
+ * Timeout callback for delay_output().  Send a fake message to the map
+ * window.
+ */
+/* ARGSUSED */
+static void
+d_timeout(client_data, id)
+    XtPointer client_data;
+    XtIntervalId *id;
+{
+    XEvent event;
+    XClientMessageEvent *mesg;
+
+    /* Set up a fake message to the event handler. */
+    mesg = (XClientMessageEvent *) &event;
+    mesg->type = ClientMessage;
+    mesg->message_type = XA_STRING;
+    mesg->format = 8;
+    XSendEvent(XtDisplay(window_list[WIN_MAP].w),
+               XtWindow(window_list[WIN_MAP].w),
+               False,
+               NoEventMask,
+               (XEvent*) mesg);
+}
+
+/*
+ * Delay for 50ms.  This is not implemented asynch.  Maybe later.
+ * Start the timeout, then wait in the event loop.  The timeout
+ * function will send an event to the map window which will be waiting
+ * for a sent event.
+ */
+void
+X11_delay_output()
+{
+    if (!x_inited) return;
+
+    (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
+
+    /* The timeout function will enable the event loop exit. */
+    (void) x_event(EXIT_ON_SENT_EVENT);
+}
+
+/* X11_hangup -------------------------------------------------------------- */
+/* ARGSUSED */
+static void
+X11_hangup(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    hangup(1);         /* 1 is commonly SIGHUP, but ignored anyway */
+}
+
+/* askname ----------------------------------------------------------------- */
+/* ARGSUSED */
+static void
+askname_delete(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    nh_XtPopdown(w);
+    (void) strcpy(plname, "Mumbles");  /* give them a name... ;-) */
+    exit_x_event = TRUE;
+}
+
+/* Callback for askname dialog widget. */
+/* ARGSUSED */
+static void
+askname_done(w, client_data, call_data)
+    Widget w;
+    XtPointer client_data;
+    XtPointer call_data;
+{
+    int len;
+    char *s;
+    Widget dialog = (Widget) client_data;
+
+    s = (char *) GetDialogResponse(dialog);
+
+    len = strlen(s);
+    if (len == 0) {
+       X11_nhbell();
+       return;
+    }
+
+    /* Truncate name if necessary */
+    if (len >= sizeof(plname)-1)
+       len = sizeof(plname)-1;
+
+    (void) strncpy(plname, s, len);
+    plname[len] = '\0';
+    XtFree(s);
+
+    nh_XtPopdown(XtParent(dialog));
+    exit_x_event = TRUE;
+}
+
+void
+X11_askname()
+{
+    Widget popup, dialog;
+    Arg args[1];
+
+    XtSetArg(args[0], XtNallowShellResize, True);
+
+    popup = XtCreatePopupShell("askname", transientShellWidgetClass,
+                                  toplevel, args, ONE);
+    XtOverrideTranslations(popup,
+       XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
+
+    dialog = CreateDialog(popup, "dialog",
+                                   askname_done, (XtCallbackProc) 0);
+
+    SetDialogPrompt(dialog, "What is your name?");     /* set prompt */
+    SetDialogResponse(dialog, "");             /* set default answer */
+
+    XtRealizeWidget(popup);
+    positionpopup(popup, TRUE);                /* center,bottom */
+
+    nh_XtPopup(popup, (int)XtGrabExclusive, dialog);
+
+    /* The callback will enable the event loop exit. */
+    (void) x_event(EXIT_ON_EXIT);
+}
+
+
+/* getline ----------------------------------------------------------------- */
+/* This uses Tim Theisen's dialog widget set (from GhostView). */
+
+static Widget getline_popup, getline_dialog;
+
+#define CANCEL_STR "\033"
+static char *getline_input;
+
+
+/* Callback for getline dialog widget. */
+/* ARGSUSED */
+static void
+done_button(w, client_data, call_data)
+    Widget w;
+    XtPointer client_data;
+    XtPointer call_data;
+{
+    char *s;
+    Widget dialog = (Widget) client_data;
+
+    s = (char *) GetDialogResponse(dialog);
+    Strcpy(getline_input, s);
+    XtFree(s);
+    nh_XtPopdown(XtParent(dialog));
+    exit_x_event = TRUE;
+}
+
+/* ARGSUSED */
+static void
+getline_delete(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    Strcpy(getline_input, CANCEL_STR);
+    nh_XtPopdown(w);
+    exit_x_event = TRUE;
+}
+
+/* Callback for getline dialog widget. */
+/* ARGSUSED */
+static void
+abort_button(w, client_data, call_data)
+    Widget w;
+    XtPointer client_data;
+    XtPointer call_data;
+{
+    Widget dialog = (Widget) client_data;
+
+    Strcpy(getline_input, CANCEL_STR);
+    nh_XtPopdown(XtParent(dialog));
+    exit_x_event = TRUE;
+}
+
+
+void
+X11_getlin(question, input)
+    const char *question;
+    char *input;
+{
+    static boolean need_to_init = True;
+
+    getline_input = input;
+
+    flush_screen(1);
+    if (need_to_init) {
+       Arg args[1];
+
+       need_to_init = False;
+
+       XtSetArg(args[0], XtNallowShellResize, True);
+
+       getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass,
+                                  toplevel, args, ONE);
+       XtOverrideTranslations(getline_popup,
+           XtParseTranslationTable("<Message>WM_PROTOCOLS: getline_delete()"));
+
+       getline_dialog = CreateDialog(getline_popup, "dialog",
+                                   done_button, abort_button);
+
+       XtRealizeWidget(getline_popup);
+       XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
+                       &wm_delete_window, 1);
+    }
+    SetDialogPrompt(getline_dialog, (String)question); /* set prompt */
+    SetDialogResponse(getline_dialog, "");     /* set default answer */
+    positionpopup(getline_popup, TRUE);                /* center,bottom */
+
+    nh_XtPopup(getline_popup, (int)XtGrabExclusive, getline_dialog);
+
+    /* The callback will enable the event loop exit. */
+    (void) x_event(EXIT_ON_EXIT);
+}
+
+
+/* Display file ------------------------------------------------------------ */
+static const char display_translations[] =
+    "#override\n\
+     <Key>q: dismiss_file()\n\
+     <Key>Escape: dismiss_file()\n\
+     <BtnDown>: dismiss_file()";
+
+
+/* WM_DELETE_WINDOW callback for file dismissal. */
+/*ARGSUSED*/
+static void
+delete_file(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    nh_XtPopdown(w);
+    XtDestroyWidget(w);
+}
+
+/* Callback for file dismissal. */
+/*ARGSUSED*/
+static void
+dismiss_file(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    Widget popup = XtParent(w);
+    nh_XtPopdown(popup);
+    XtDestroyWidget(popup);
+}
+
+void
+X11_display_file(str, complain)
+    const char *str;
+    boolean complain;
+{
+    dlb *fp;
+    Arg args[12];
+    Cardinal num_args;
+    Widget popup, dispfile;
+    Position top_margin, bottom_margin, left_margin, right_margin;
+    XFontStruct *fs;
+    int new_width, new_height;
+#define LLEN 128
+    char line[LLEN];
+    int num_lines;
+    char *textlines;
+    int charcount;
+
+    /* Use the port-independent file opener to see if the file exists. */
+    fp = dlb_fopen(str, RDTMODE);
+
+    if (!fp) {
+       if(complain) pline("Cannot open %s.  Sorry.", str);
+
+       return; /* it doesn't exist, ignore */
+    }
+
+    /*
+     * Count the number of lines and characters in the file.
+     */
+    num_lines = 0;
+    charcount = 1;
+    while (dlb_fgets(line, LLEN, fp)) {
+       num_lines++;
+       charcount += strlen(line);
+    }
+
+    (void) dlb_fclose(fp);
+
+    /* Ignore empty files */
+    if (num_lines == 0) return;
+
+    /* If over the max window size, truncate the window size to the max */
+    if (num_lines >= DISPLAY_FILE_SIZE)
+       num_lines = DISPLAY_FILE_SIZE;
+
+    /*
+     * Re-open the file and read the data into a buffer.  Cannot use
+     * the XawAsciiFile type of widget, because that is not DLB-aware.
+     */
+    textlines = (char *) alloc((unsigned int) charcount);
+    textlines[0] = '\0';
+
+    fp = dlb_fopen(str, RDTMODE);
+
+    while (dlb_fgets(line, LLEN, fp)) {
+       (void) strcat(textlines, line);
+    }
+
+    (void) dlb_fclose(fp);
+
+    num_args = 0;
+    XtSetArg(args[num_args], XtNtitle, str);   num_args++;
+
+    popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
+                                              toplevel, args, num_args);
+    XtOverrideTranslations(popup,
+       XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
+
+    num_args = 0;
+    XtSetArg(args[num_args], XtNscrollHorizontal,
+                               XawtextScrollWhenNeeded);       num_args++;
+    XtSetArg(args[num_args], XtNscrollVertical,
+                               XawtextScrollWhenNeeded);       num_args++;
+    XtSetArg(args[num_args], XtNtype, XawAsciiString);         num_args++;
+    XtSetArg(args[num_args], XtNstring, textlines);            num_args++;
+    XtSetArg(args[num_args], XtNdisplayCaret, False);          num_args++;
+    XtSetArg(args[num_args], XtNtranslations,
+       XtParseTranslationTable(display_translations));         num_args++;
+
+    dispfile = XtCreateManagedWidget(
+                       "text",                 /* name */
+                       asciiTextWidgetClass,
+                       popup,                  /* parent widget */
+                       args,                   /* set some values */
+                       num_args);              /* number of values to set */
+
+    /* Get font and border information. */
+    num_args = 0;
+    XtSetArg(args[num_args], XtNfont,        &fs);            num_args++;
+    XtSetArg(args[num_args], XtNtopMargin,    &top_margin);    num_args++;
+    XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
+    XtSetArg(args[num_args], XtNleftMargin,   &left_margin);   num_args++;
+    XtSetArg(args[num_args], XtNrightMargin,  &right_margin);  num_args++;
+    XtGetValues(dispfile, args, num_args);
+
+    /*
+     * The data files are currently set up assuming an 80 char wide window
+     * and a fixed width font.  Soo..
+     */
+    new_height = num_lines * nhFontHeight(dispfile) +
+                                               top_margin + bottom_margin;
+    new_width  = 80 * fs->max_bounds.width + left_margin + right_margin;
+
+    /* Set the new width and height. */
+    num_args = 0;
+    XtSetArg(args[num_args], XtNwidth,  new_width);  num_args++;
+    XtSetArg(args[num_args], XtNheight, new_height); num_args++;
+    XtSetValues(dispfile, args, num_args);
+
+    nh_XtPopup(popup, (int)XtGrabNone, (Widget)0);
+    free(textlines);
+}
+
+
+/* yn_function ------------------------------------------------------------- */
+/* (not threaded) */
+
+static const char *yn_quitchars = " \n\r";
+static const char *yn_choices; /* string of acceptable input */
+static char yn_def;
+static char yn_return;         /* return value */
+static char yn_esc_map;                /* ESC maps to this char. */
+static Widget yn_popup;                /* popup for the yn fuction (created once) */
+static Widget yn_label;                /* label for yn function (created once) */
+static boolean yn_getting_num; /* TRUE if accepting digits */
+static int yn_ndigits;         /* digit count */
+static long yn_val;            /* accumulated value */
+
+static const char yn_translations[] =
+    "#override\n\
+     <Key>: yn_key()";
+
+/*
+ * Convert the given key event into a character.  If the key maps to
+ * more than one character only the first is returned.  If there is
+ * no conversion (i.e. just the CTRL key hit) a NUL is returned.
+ */
+char
+key_event_to_char(key)
+    XKeyEvent *key;
+{
+    char keystring[MAX_KEY_STRING];
+    int nbytes;
+    boolean meta = !!(key->state & Mod1Mask);
+
+    nbytes = XLookupString(key, keystring, MAX_KEY_STRING,
+                          (KeySym *)0, (XComposeStatus *)0);
+
+    /* Modifier keys return a zero lengh string when pressed. */
+    if (nbytes == 0) return '\0';
+
+    return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
+}
+
+/*
+ * Called when we get a WM_DELETE_WINDOW event on a yn window.
+ */
+/* ARGSUSED */
+static void
+yn_delete(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    yn_getting_num = FALSE;
+    /* Only use yn_esc_map if we have choices.  Otherwise, return ESC. */
+    yn_return = yn_choices ? yn_esc_map : '\033';
+    exit_x_event = TRUE;       /* exit our event handler */
+}
+
+/*
+ * Called when we get a key press event on a yn window.
+ */
+/* ARGSUSED */
+static void
+yn_key(w, event, params, num_params)
+    Widget w;
+    XEvent *event;
+    String *params;
+    Cardinal *num_params;
+{
+    char ch;
+
+    if(appResources.slow && !input_func)
+       map_input(w, event, params, num_params);
+
+    ch = key_event_to_char((XKeyEvent *) event);
+
+    if (ch == '\0') {  /* don't accept nul char or modifier event */
+       /* no bell */
+       return;
+    }
+
+    if (!yn_choices) {                 /* accept any input */
+       yn_return = ch;
+    } else {
+       ch = lowc(ch);                  /* move to lower case */
+
+       if (ch == '\033') {
+           yn_getting_num = FALSE;
+           yn_return = yn_esc_map;
+       } else if (index(yn_quitchars, ch)) {
+           yn_return = yn_def;
+       } else if (index(yn_choices, ch)) {
+           if (ch == '#') {
+               if (yn_getting_num) {   /* don't select again */
+                   X11_nhbell();
+                   return;
+               }
+               yn_getting_num = TRUE;
+               yn_ndigits = 0;
+               yn_val = 0;
+               return;                 /* wait for more input */
+           }
+           yn_return = ch;
+           if (ch != 'y') yn_getting_num = FALSE;
+       } else {
+           if (yn_getting_num) {
+               if (digit(ch)) {
+                   yn_ndigits++;
+                   yn_val = (yn_val * 10) + (long) (ch - '0');
+                   return;                     /* wait for more input */
+               }
+               if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) {
+                   yn_ndigits--;
+                   yn_val = yn_val/ 10;
+                   return;                     /* wait for more input */
+               }
+           }
+           X11_nhbell();               /* no match */
+           return;
+       }
+
+       if (yn_getting_num) {
+           yn_return = '#';
+           if (yn_val < 0) yn_val = 0;
+           yn_number = yn_val; /* assign global */
+       }
+    }
+    exit_x_event = TRUE;       /* exit our event handler */
+}
+
+
+char
+X11_yn_function(ques, choices, def)
+    const char *ques;
+    const char *choices;
+    char def;
+{
+    static Boolean need_to_init = True;
+    char buf[QBUFSZ];
+    Arg args[4];
+    Cardinal num_args;
+
+    yn_choices = choices;      /* set up globals for callback to use */
+    yn_def     = def;
+
+    /*
+     * This is sort of a kludge.  There are quite a few places in the main
+     * nethack code where a pline containing information is followed by a
+     * call to yn_function().  There is no flush of the message window
+     * (it is implicit in the tty window port), so the line never shows
+     * up for us!  Solution: do our own flush.
+     */
+    if (WIN_MESSAGE != WIN_ERR)
+       display_message_window(&window_list[WIN_MESSAGE]);
+
+    if (choices) {
+       char *cb, choicebuf[QBUFSZ];
+
+       Strcpy(choicebuf, choices);     /* anything beyond <esc> is hidden */
+       if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0';
+       /* ques [choices] (def) */
+       if ((int)(1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= QBUFSZ)
+           panic("yn_function:  question too long");
+       Sprintf(buf, "%s [%s] ", ques, choicebuf);
+       if (def) Sprintf(eos(buf), "(%c) ", def);
+
+       /* escape maps to 'q' or 'n' or default, in that order */
+       yn_esc_map = (index(choices, 'q') ? 'q' :
+                    (index(choices, 'n') ? 'n' :
+                                           def));
+    } else {
+       if ((int)(1 + strlen(ques)) >= QBUFSZ)
+           panic("yn_function:  question too long");
+       Strcpy(buf, ques);
+    }
+
+    if (!appResources.slow && need_to_init) {
+       need_to_init = False;
+
+       XtSetArg(args[0], XtNallowShellResize, True);
+       yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
+                                       toplevel, args, ONE);
+       XtOverrideTranslations(yn_popup,
+           XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
+
+       num_args = 0;
+       XtSetArg(args[num_args], XtNtranslations,
+               XtParseTranslationTable(yn_translations));      num_args++;
+       yn_label = XtCreateManagedWidget("yn_label",
+                               labelWidgetClass,
+                               yn_popup,
+                               args, num_args);
+
+       XtRealizeWidget(yn_popup);
+       XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
+                       &wm_delete_window, 1);
+    }
+
+    if(appResources.slow)
+       input_func = yn_key;
+
+    num_args = 0;
+    XtSetArg(args[num_args], XtNlabel, buf);   num_args++;
+    XtSetValues(yn_label, args, num_args);
+
+    if(!appResources.slow) {
+       /*
+        * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
+        * need to set the label twice to get the size to change.
+        */
+       num_args = 0;
+       XtSetArg(args[num_args], XtNlabel, buf); num_args++;
+       XtSetValues(yn_label, args, num_args);
+
+       positionpopup(yn_popup, TRUE);
+       nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label);
+    }
+
+    yn_getting_num = FALSE;
+    (void) x_event(EXIT_ON_EXIT);
+
+    if(appResources.slow) {
+       input_func = 0;
+       num_args = 0;
+       XtSetArg(args[num_args], XtNlabel, " ");        num_args++;
+       XtSetValues(yn_label, args, num_args);
+    } else {
+       nh_XtPopdown(yn_popup); /* this removes the event grab */
+    }
+
+    return yn_return;
+}
+
+/* End global functions ==================================================== */
+
+/*
+ * Before we wait for input via nhgetch() and nh_poskey(), we need to
+ * do some pre-processing.
+ */
+static int
+input_event(exit_condition)
+    int exit_condition;
+{
+    if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
+       check_turn_events();
+    if (WIN_MAP != WIN_ERR)    /* make sure cursor is not clipped */
+       check_cursor_visibility(&window_list[WIN_MAP]);
+    if (WIN_MESSAGE != WIN_ERR)        /* reset pause line */
+       set_last_pause(&window_list[WIN_MESSAGE]);
+
+    return x_event(exit_condition);
+}
+
+
+/*ARGSUSED*/
+void
+msgkey(w, data, event)
+    Widget w;
+    XtPointer data;
+    XEvent *event;
+{
+    Cardinal num = 0;
+    map_input(window_list[WIN_MAP].w, event, (String*) 0, &num);
+}
+
+/*ARGSUSED*/
+static void
+win_visible(w, data, event, flag)      /* only called for autofocus */
+    Widget w;
+    XtPointer data;    /* client_data not used */
+    XEvent *event;
+    Boolean *flag;     /* continue_to_dispatch flag not used */
+{
+    XVisibilityEvent *vis_event = (XVisibilityEvent *)event;
+
+    if (vis_event->state != VisibilityFullyObscured) {
+       /* one-time operation; cancel ourself */
+       XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
+                            win_visible, (XtPointer) 0);
+       /* grab initial input focus */
+       XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
+    }
+}
+
+/*
+ * Set up the playing console.  This has three major parts:  the
+ * message window, the map, and the status window.
+ */
+static void
+init_standard_windows()
+{
+    Widget form, message_viewport, map_viewport, status;
+    Arg args[8];
+    Cardinal num_args;
+    Dimension message_vp_width, map_vp_width, status_width, max_width;
+    int map_vp_hd, status_hd;
+    struct xwindow *wp;
+
+
+    num_args = 0;
+    XtSetArg(args[num_args], XtNallowShellResize, True);       num_args++;
+    form = XtCreateManagedWidget("nethack",
+                               panedWidgetClass,
+                               toplevel, args, num_args);
+
+    XtAddEventHandler(form, KeyPressMask, False,
+                     (XtEventHandler) msgkey, (XtPointer) 0);
+
+    if (appResources.autofocus)
+       XtAddEventHandler(toplevel, VisibilityChangeMask, False,
+                         win_visible, (XtPointer) 0);
+
+    /*
+     * Create message window.
+     */
+    WIN_MESSAGE = message_win = find_free_window();
+    wp = &window_list[message_win];
+    wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
+    wp->popup = (Widget) 0;
+    create_message_window(wp, FALSE, form);
+    message_viewport = XtParent(wp->w);
+
+
+    /* Tell the form that contains it that resizes are OK. */
+    num_args = 0;
+    XtSetArg(args[num_args], XtNresizable, True);              num_args++;
+    XtSetArg(args[num_args], XtNleft,     XtChainLeft);        num_args++;
+    XtSetArg(args[num_args], XtNtop,      XtChainTop);         num_args++;
+    XtSetValues(message_viewport, args, num_args);
+
+    if(appResources.slow) {
+       num_args = 0;
+       XtSetArg(args[num_args], XtNtranslations,
+                XtParseTranslationTable(yn_translations)); num_args++;
+       yn_label = XtCreateManagedWidget("yn_label",
+                                        labelWidgetClass,
+                                        form,
+                                        args, num_args);
+       num_args = 0;
+       XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++;
+       XtSetArg(args[num_args], XtNjustify, XtJustifyLeft);    num_args++;
+       XtSetArg(args[num_args], XtNresizable, True);   num_args++;
+       XtSetArg(args[num_args], XtNlabel, " ");        num_args++;
+       XtSetValues(yn_label, args, num_args);
+    }
+
+    /*
+     * Create the map window & viewport and chain the viewport beneath the
+     * message_viewport.
+     */
+    map_win = find_free_window();
+    wp = &window_list[map_win];
+    wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
+    wp->popup = (Widget) 0;
+    create_map_window(wp, FALSE, form);
+    map_viewport = XtParent(wp->w);
+
+    /* Chain beneath message_viewport or yn window. */
+    num_args = 0;
+    if(appResources.slow) {
+       XtSetArg(args[num_args], XtNfromVert, yn_label);        num_args++;
+    } else {
+       XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++;
+    }
+    XtSetArg(args[num_args], XtNbottom, XtChainBottom);                num_args++;
+    XtSetValues(map_viewport, args, num_args);
+
+    /* Create the status window, with the form as it's parent. */
+    status_win = find_free_window();
+    wp = &window_list[status_win];
+    wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
+    wp->popup = (Widget) 0;
+    create_status_window(wp, FALSE, form);
+    status = wp->w;
+
+    /*
+     * Chain the status window beneath the viewport.  Mark the left and right
+     * edges so that they stay a fixed distance from the left edge of the
+     * parent, as well as the top and bottom edges so that they stay a fixed
+     * distance from the bottom of the parent.  We do this so that the status
+     * will never expand or contract.
+     */
+    num_args = 0;
+    XtSetArg(args[num_args], XtNfromVert, map_viewport);       num_args++;
+    XtSetArg(args[num_args], XtNleft,    XtChainLeft);         num_args++;
+    XtSetArg(args[num_args], XtNright,   XtChainLeft);         num_args++;
+    XtSetArg(args[num_args], XtNtop,     XtChainBottom);       num_args++;
+    XtSetArg(args[num_args], XtNbottom,          XtChainBottom);       num_args++;
+    XtSetValues(status, args, num_args);
+
+
+    /*
+     * Realize the popup so that the status widget knows it's size.
+     *
+     * If we unset MappedWhenManaged then the DECwindow driver doesn't
+     * attach the nethack toplevel to the highest virtual root window.
+     * So don't do it.
+     */
+    /* XtSetMappedWhenManaged(toplevel, False); */
+    XtRealizeWidget(toplevel);
+    wm_delete_window = XInternAtom(XtDisplay(toplevel),
+                                  "WM_DELETE_WINDOW", False);
+    XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
+                   &wm_delete_window, 1);
+
+    /*
+     * Resize to at most full-screen.
+     */
+    {
+#define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
+
+       int screen_width  = WidthOfScreen(XtScreen(wp->w));
+       int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
+       Dimension form_width, form_height;
+
+       XtSetArg(args[0], XtNwidth, &form_width);
+       XtSetArg(args[1], XtNheight, &form_height);
+       XtGetValues(toplevel, args, TWO);
+       
+       if (form_width > screen_width || form_height > screen_height) {
+           XtSetArg(args[0], XtNwidth, min(form_width,screen_width));
+           XtSetArg(args[1], XtNheight, min(form_height,screen_height));
+           XtSetValues(toplevel, args, TWO);
+           XMoveWindow(XtDisplay(toplevel),XtWindow(toplevel),
+               0, TITLEBAR_SPACE);
+       }
+#undef TITLEBAR_SPACE
+    }
+
+    post_process_tiles();      /* after toplevel is realized */
+
+    /*
+     * Now get the default widths of the windows.
+     */
+    XtSetArg(args[0], XtNwidth, &message_vp_width);
+    XtGetValues(message_viewport, args, ONE);
+    XtSetArg(args[0], XtNwidth, &map_vp_width);
+    XtSetArg(args[1], XtNhorizDistance, &map_vp_hd);
+    XtGetValues(map_viewport, args, TWO);
+    XtSetArg(args[0], XtNwidth, &status_width);
+    XtSetArg(args[1], XtNhorizDistance, &status_hd);
+    XtGetValues(status, args, TWO);
+
+    /*
+     * Adjust positions and sizes.  The message viewport widens out to the
+     * widest width.  Both the map and status are centered by adjusting
+     * their horizDistance.
+     */
+    if (map_vp_width < status_width || map_vp_width < message_vp_width) {
+       if (status_width > message_vp_width) {
+           XtSetArg(args[0], XtNwidth, status_width);
+           XtSetValues(message_viewport, args, ONE);
+           max_width = status_width;
+       } else {
+/***** The status display looks better when left justified.
+           XtSetArg(args[0], XtNhorizDistance,
+                               status_hd+((message_vp_width-status_width)/2));
+           XtSetValues(status, args, ONE);
+*****/
+           max_width = message_vp_width;
+       }
+       XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2));
+       XtSetValues(map_viewport, args, ONE);
+
+    } else {   /* map is widest */
+       XtSetArg(args[0], XtNwidth, map_vp_width);
+       XtSetValues(message_viewport, args, ONE);
+
+/***** The status display looks better when left justified.
+       XtSetArg(args[0], XtNhorizDistance,
+                               status_hd+((map_vp_width-status_width)/2));
+
+       XtSetValues(status, args, ONE);
+*****/
+    }
+    /*
+     * Clear all data values on the fancy status widget so that the values
+     * used for spacing don't appear.  This needs to be called some time
+     * after the fancy status widget is realized (above, with the game popup),
+     * but before it is popped up.
+     */
+    null_out_status();
+    /*
+     * Set the map size to its standard size.  As with the message window
+     * above, the map window needs to be set to its constrained size until
+     * its parent (the viewport widget) was realized.
+     *
+     * Move the message window's slider to the bottom.
+     */
+    set_map_size(&window_list[map_win], COLNO, ROWNO);
+    set_message_slider(&window_list[message_win]);
+
+    /* attempt to catch fatal X11 errors before the program quits */
+    (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
+
+    /* We can now print to the message window. */
+    iflags.window_inited = 1;
+}
+
+
+void
+nh_XtPopup(w, g, childwid)
+    Widget w;          /* widget */
+    int    g;          /* type of grab */
+    Widget childwid;   /* child to recieve focus (can be None) */
+{
+    XtPopup(w, (XtGrabKind)g);
+    XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
+    if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid);
+}
+
+void
+nh_XtPopdown(w)
+    Widget w;
+{
+    XtPopdown(w);
+    if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None);
+}
+
+void
+win_X11_init()
+{
+#ifdef OPENWINBUG
+    /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
+     * two routines will not be found when linking.  An apparently correct
+     * executable is produced, along with nasty messages and a failure code
+     * returned to make.  The routines are in the static libXmu.a and
+     * libXmu.sa.4.0, but not in libXmu.so.4.0.  Rather than fiddle with
+     * static linking, we do this.
+     */
+    if (rn2(2) > 2) {
+       /* i.e., FALSE that an optimizer probably can't find */
+       get_wmShellWidgetClass();
+       get_applicationShellWidgetClass();
+    }
+#endif
+    return;
+}
+
+/* Callback
+ * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
+ */
+/*ARGSUSED*/
+void
+nh_keyscroll(viewport, event, params, num_params)
+    Widget   viewport;
+    XEvent   *event;
+    String   *params;
+    Cardinal *num_params;
+{
+    Arg arg[2];
+    Widget horiz_sb, vert_sb;
+    float top, shown;
+    Boolean do_call;
+    int direction;
+    Cardinal in_nparams = (num_params ? *num_params : 0);
+
+    if (in_nparams != 1) return; /* bad translation */
+
+    direction=atoi(params[0]);
+
+    horiz_sb = XtNameToWidget(viewport, "*horizontal");
+    vert_sb  = XtNameToWidget(viewport, "*vertical");
+
+    if (!horiz_sb && !vert_sb) {
+       /* Perhaps the widget enclosing this has scrollbars (could use while) */
+       Widget parent=XtParent(viewport);
+       if (parent) {
+           horiz_sb = XtNameToWidget(parent, "horizontal");
+           vert_sb  = XtNameToWidget(parent, "vertical");
+       }
+    }
+
+#define H_DELTA 0.25           /* distance of horiz shift */
+                               /* vert shift is half of curr distance */
+/* The V_DELTA is 1/2 the value of shown. */
+
+    if (horiz_sb) {
+       XtSetArg(arg[0], XtNshown,      &shown);
+       XtSetArg(arg[1], XtNtopOfThumb, &top);
+       XtGetValues(horiz_sb, arg, TWO);
+
+       do_call = True;
+
+       switch (direction) {
+         case 1: case 4: case 7:
+           top -= H_DELTA;
+           if (top < 0.0) top = 0.0;
+       break; case 3: case 6: case 9:
+           top += H_DELTA;
+           if (top + shown > 1.0) top = 1.0 - shown;
+       break; default:
+           do_call = False;
+       }
+
+       if (do_call) {
+           XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
+       }
+    }
+
+    if (vert_sb) {
+       XtSetArg(arg[0], XtNshown,      &shown);
+       XtSetArg(arg[1], XtNtopOfThumb, &top);
+       XtGetValues(vert_sb, arg, TWO);
+
+       do_call = True;
+
+       switch (direction) {
+         case 7: case 8: case 9:
+           top -= shown / 2.0;
+           if (top < 0.0) top = 0;
+       break; case 1: case 2: case 3:
+           top += shown / 2.0;
+           if (top + shown > 1.0) top = 1.0 - shown;
+       break; default:
+           do_call = False;
+       }
+
+       if (do_call) {
+           XtCallCallbacks(vert_sb, XtNjumpProc, &top);
+       }
+    }
+}
+
+/*winX.c*/