Files
nethack/win/X11/winX.c
jwalz c3960b1d49 SAFERHANGUP followup
We've previously been supporting R4+ for X11, but signals weren't added
until R6.  This at least lets it compile under R5.
2003-09-19 16:32:41 +00:00

2146 lines
58 KiB
C

/* SCCS Id: @(#)winX.c 3.4 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
#ifndef NO_SIGNAL
#include <signal.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;
#if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
# if XtSpecificationRelease >= 6
#define X11_HANGUP_SIGNAL
static XtSignalId X11_sig_id;
# endif
#endif
/* Interface definition, for windows.c */
struct window_procs X11_procs = {
"X11",
WC_COLOR|WC_HILITE_PET,
0L,
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
genl_preference_update,
};
/*
* 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);
#ifdef X11_HANGUP_SIGNAL
static void FDECL(X11_sig, (int));
static void FDECL(X11_sig_cb, (XtPointer, XtSignalId*));
#endif
/*
* 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");
#ifdef X11_HANGUP_SIGNAL
/* set up our own signal handlers on the first call. Can't do this in
* X11_init_nhwindows because unixmain sets its handler after calling
* all the init routines. */
if (X11_sig_id == 0) {
X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer)0);
# ifdef SA_RESTART
{
struct sigaction sact;
(void) memset((char*) &sact, 0, sizeof(struct sigaction));
sact.sa_handler = (SIG_RET_TYPE)X11_sig;
(void) sigaction(SIGHUP, &sact, (struct sigaction*)0);
# ifdef SIGXCPU
(void) sigaction(SIGXCPU, &sact, (struct sigaction*)0);
# endif
}
# else
(void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
# ifdef SIGXCPU
(void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
# endif
# endif
}
#endif
/*
* 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, "True" },
{ "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);
}
#ifdef X11_HANGUP_SIGNAL
static void
X11_sig(sig) /* Unix signal handler */
int sig;
{
XtNoticeSignal(X11_sig_id);
hangup(sig);
}
static void
X11_sig_cb(not_used, id)
XtPointer not_used;
XtSignalId* 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);
}
#endif
/* 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;
{
int len;
char *s;
Widget dialog = (Widget) client_data;
s = (char *) GetDialogResponse(dialog);
len = strlen(s);
/* Truncate input if necessary */
if (len >= BUFSZ) len = BUFSZ - 1;
(void) strncpy(getline_input, s, len);
getline_input[len] = '\0';
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*/