Logo Search packages:      
Sourcecode: jnethack version File versions  Download package

winX.c

/*    SCCS Id: @(#)winX.c     3.2   96/04/05    */
/* 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.
 */

/*
**    Japanese version Copyright (C) Issei Numata, 1994-1999
**    changing point is marked `JP' (94/6/7) or XI18N (96/7/19)
**    JNetHack may be freely redistributed.  See license for details. 
*/

#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

#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.               */

#ifdef      INSTALLCOLORMAP
Colormap     cmap;
#endif

/*
 * 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;
}

#ifdef INSTALLCOLORMAP
Boolean
nhCvtStringToColormap(dpy, args, num_args, fromVal, toVal, data)
Display*    dpy;
XrmValuePtr args;
Cardinal    *num_args;
XrmValuePtr fromVal;
XrmValuePtr toVal;
XtPointer   data;
{
  XColor    black, white;
  int screen = DefaultScreen(dpy);
  Colormap dcmap = DefaultColormap(dpy, screen);
  
  if(strncmp(fromVal->addr, "install", 7)){
    return False;
  }
  if(!cmap){
    cmap = XCreateColormap(dpy,
                     RootWindow(dpy, screen),
                     DefaultVisual(dpy, screen),
                     AllocNone);
    /*
      手抜き
     */
    black.red = 0;
    black.green = 0;
    black.blue = 0;

    white.red = 0xffff;
    white.green = 0xffff;
    white.blue = 0xffff;

    XAllocColor(dpy, dcmap, &black);
    XAllocColor(dpy, dcmap, &white);

    XAllocColor(dpy, cmap, &black);
    XAllocColor(dpy, cmap, &white);
  }
  
  done(Colormap, cmap);
}
#endif

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
                );
    }
}

/* Global Functions ======================================================== */
void
X11_raw_print(str)
    const char *str;
{
/*JP
    (void) puts(str);
*/
    (void) jputs(str);
}

void
X11_raw_print_bold(str)
    const char *str;
{
/*JP
    (void) puts(str);
*/
    (void) jputs(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:
          Strcpy(toplines, str);    /* for Norep(). */
          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);
    }

#ifndef XI18N
    calculate_rip_text(how);
#else
    calculate_rip_text(how, wp->Win_info.Text_info->fontset);
#endif
}
#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
#ifdef RADAR
    {"dismiss_radar",   dismiss_radar},
#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 */
    {"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, "" },
/*JP*/
    { "tile_width", "Tile_width", XtRInt, sizeof(int),
      XtOffset(AppResources *,tile_width), XtRString, "32" },
    { "tile_height", "Tile_height", XtRInt, sizeof(int),
      XtOffset(AppResources *,tile_height), XtRString, "32" },
/*JP*/
    { "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" },
#ifdef RADAR      /* 機能してません */
    { "radar", "Radar", XtRBoolean, sizeof(Boolean),
      XtOffset(AppResources *, radar), XtRString, "True" },
#endif
#endif
    { "centerPopups", "CenterPopups", XtRBoolean, sizeof(Boolean),
      XtOffset(AppResources *,center_popups), XtRImmediate, False },
};

void
X11_init_nhwindows(argcp,argv)
int* argcp;
char** argv;
{
    static const char *banner_text[] = {
      COPYRIGHT_BANNER_A,
      COPYRIGHT_BANNER_B,
      COPYRIGHT_BANNER_C,
      "",
/*JP*/
      "NetHack[日本語版], Copyright 1993, 1994",
      "         By I. Numata , S. Miyashita and N. Hamada.",
      "",
      0
    };
    register const char **pp;
    int i;
    Cardinal num_args;
    Arg args[4];


    /* Init windows to nothing. */
    for (i = 0; i < MAX_WINDOWS; i++)
      window_list[i].type = NHW_NONE;

/*JP
** i18n initialize by issei 1994/1/8
*/

#ifdef XI18N
    XtSetLanguageProc(NULL,NULL,NULL);
    setlocale(LC_CTYPE, "");
#endif
    XSetIOErrorHandler((XIOErrorHandler) hangup);
#ifdef INSTALLCOLORMAP
    XtSetTypeConverter(XtRString, XtRColormap, nhCvtStringToColormap, 
                   0, 0, XtCacheByDisplay, 0);
#endif

    num_args = 0;
    XtSetArg(args[num_args], XtNallowShellResize, True);    num_args++;
    toplevel = XtAppInitialize(
                &app_context,
                "JNetHack",               /* 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;

                hints.flags = IconPixmapHint;
                hints.icon_pixmap = icon_pixmap;
                XSetWMHints(XtDisplay(toplevel),
                        XtWindow(toplevel), &hints);
            }
            break;
          }
    }

    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';

    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);
    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;
/*JP    XFontStruct *fs;*/
    int new_width, new_height;
#define LLEN 128
    char line[LLEN];
    int num_lines;
    char *textlines;
    int charcount;
#ifdef XI18N
    XFontSet fontset;
    XFontSetExtents *extent;
#endif

    /* 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++;
/*JP*/
#if defined(X11R6) && defined(XI18N)
    XtSetArg(args[num_args], XtNinternational, True); num_args++;
#endif

    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;
#ifndef XI18N
    XtSetArg(args[num_args], XtNfont,           &fs);       num_args++;
#else
    XtSetArg(args[num_args], XtNfontSet,      &fontset);    num_args++;
#endif
    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);

    /*
     * Font height is ascent + descent.
     *
     * The data files are currently set up assuming an 80 char wide window
     * and a fixed width font.  Soo..
     */
#ifndef XI18N
    new_height = num_lines * (fs->ascent + fs->descent) +
                                    top_margin + bottom_margin;
    new_width  = 80 * fs->max_bounds.width + left_margin + right_margin;
#else
    extent = XExtentsOfFontSet(fontset);
    new_height = num_lines * extent->max_logical_extent.height +
                                    top_margin + bottom_margin;
    new_width  = 40 * extent->max_logical_extent.width + left_margin + right_margin;
#endif

    /* 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++;
/*JP*/
#if defined(X11R6) && defined(XI18N)
    XtSetArg(args[num_args], XtNinternational, True); num_args++;
#endif
      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++;
/*JP    form = XtCreateManagedWidget("nethack",*/
    form = XtCreateManagedWidget("jnethack",
                        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++;
/*JP*/
#if defined(X11R6) && defined(XI18N)
    XtSetArg(args[num_args], XtNinternational, True); num_args++;
#endif
      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]);
#ifdef RADAR
    viewport = XtNameToWidget(toplevel, "*map_viewport");
#endif
    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*/

Generated by  Doxygen 1.6.0   Back to index