From: jwalz Date: Sat, 5 Jan 2002 21:06:03 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3536 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a91cc7270febbb6b3ff170932768b3d0bcda414a;p=nethack *** empty log message *** --- diff --git a/win/X11/winX.c b/win/X11/winX.c new file mode 100644 index 000000000..0b43eb970 --- /dev/null +++ b/win/X11/winX.c @@ -0,0 +1,2065 @@ +/* SCCS Id: @(#)winX.c 3.3 1999/12/21 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * "Main" file for the X window-port. This contains most of the interface + * routines. Please see doc/window.doc for an description of the window + * interface. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#ifdef MSDOS /* from compiler */ +#define SHORT_FILENAMES +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* for color support */ +#ifdef SHORT_FILENAMES +#include +#else +#include +#endif + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#ifdef SHORT_FILENAMES +#undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */ +#endif + +#include "hack.h" +#include "winX.h" +#include "dlb.h" +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +/* Should be defined in but you never know */ +#ifndef XtSpecificationRelease +#define XtSpecificationRelease 0 +#endif + +/* + * Icons. + */ +#include "../win/X11/nh72icon" +#include "../win/X11/nh56icon" +#include "../win/X11/nh32icon" + +static struct icon_info { + const char *name; + unsigned char *bits; + unsigned width, height; +} icon_data[] = { + { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height }, + { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height }, + { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height }, + { (const char *)0, (unsigned char *)0, 0, 0 } +}; + +/* + * Private global variables (shared among the window port files). + */ +struct xwindow window_list[MAX_WINDOWS]; +AppResources appResources; +void FDECL((*input_func), (Widget,XEvent *,String *,Cardinal *)); +int click_x, click_y, click_button; /* Click position on a map window */ + /* (filled by set_button_values()). */ +int updated_inventory; + + +/* Interface definition, for windows.c */ +struct window_procs X11_procs = { + "X11", + X11_init_nhwindows, + X11_player_selection, + X11_askname, + X11_get_nh_event, + X11_exit_nhwindows, + X11_suspend_nhwindows, + X11_resume_nhwindows, + X11_create_nhwindow, + X11_clear_nhwindow, + X11_display_nhwindow, + X11_destroy_nhwindow, + X11_curs, + X11_putstr, + X11_display_file, + X11_start_menu, + X11_add_menu, + X11_end_menu, + X11_select_menu, + genl_message_menu, /* no need for X-specific handling */ + X11_update_inventory, + X11_mark_synch, + X11_wait_synch, +#ifdef CLIPPING + X11_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + X11_print_glyph, + X11_raw_print, + X11_raw_print_bold, + X11_nhgetch, + X11_nh_poskey, + X11_nhbell, + X11_doprev_message, + X11_yn_function, + X11_getlin, + X11_get_ext_cmd, + X11_number_pad, + X11_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + X11_start_screen, + X11_end_screen, +#ifdef GRAPHIC_TOMBSTONE + X11_outrip, +#else + genl_outrip, +#endif +}; + +/* + * Local functions. + */ +static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*)); +static int FDECL(input_event, (int)); +static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *)); +static void NDECL(init_standard_windows); + + +/* + * Local variables. + */ +static boolean x_inited = FALSE; /* TRUE if window system is set up. */ +static winid message_win = WIN_ERR, /* These are the winids of the */ + map_win = WIN_ERR, /* message, map, and status */ + status_win = WIN_ERR; /* windows, when they are created */ + /* in init_windows(). */ +static Pixmap icon_pixmap = None; /* Pixmap for icon. */ + +/* + * Find the window structure that corresponds to the given widget. Note + * that this is not the popup widget, nor the viewport, but the child. + */ +struct xwindow * +find_widget(w) + Widget w; +{ + int windex; + struct xwindow *wp; + + /* Search to find the corresponding window. Look at the main widget, */ + /* popup, the parent of the main widget, then parent of the widget. */ + for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++) + if (wp->type != NHW_NONE && + (wp->w == w || wp->popup == w || (wp->w && (XtParent(wp->w)) == w) + || (wp->popup == XtParent(w)))) + break; + + if (windex == MAX_WINDOWS) panic("find_widget: can't match widget"); + return wp; +} + +/* + * Find a free window slot for use. + */ +static winid +find_free_window() +{ + int windex; + struct xwindow *wp; + + for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++) + if (wp->type == NHW_NONE) break; + + if (windex == MAX_WINDOWS) + panic("find_free_window: no free windows!"); + return (winid) windex; +} + +/* + * Color conversion. The default X11 color converters don't try very + * hard to find matching colors in PseudoColor visuals. If they can't + * allocate the exact color, they puke and give you something stupid. + * This is an attempt to find some close readonly cell and use it. + */ +XtConvertArgRec const nhcolorConvertArgs[] = { + {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen), + sizeof(Screen *)}, + {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap), + sizeof(Colormap)} +}; + +#define done(type, value) \ + { \ + if (toVal->addr != 0) { \ + if (toVal->size < sizeof(type)) { \ + toVal->size = sizeof(type); \ + return False; \ + } \ + *(type*)(toVal->addr) = (value); \ + } \ + else { \ + static type static_val; \ + static_val = (value); \ + toVal->addr = (genericptr_t)&static_val; \ + } \ + toVal->size = sizeof(type); \ + return True; \ + } + +/* decl.h declares these, but it screws up structure references -dlc */ +#undef red +#undef green +#undef blue + +/* + * Find a color that approximates the color named in "str". The "str" color + * may be a color name ("red") or number ("#7f0000"). If str == NULL, then + * "color" is assumed to contain the RGB color wanted. + * The approximate color found is returned in color as well. + * Return True if something close was found. + */ +Boolean +nhApproxColor(screen, colormap, str, color) +Screen *screen; /* screen to use */ +Colormap colormap; /* the colormap to use */ +char *str; /* color name */ +XColor *color; /* the X color structure; changed only if successful */ +{ + int ncells; + long cdiff = 16777216; /* 2^24; hopefully our map is smaller */ + XColor tmp; + static XColor *table = 0; + register int i, j; + register long tdiff; + + /* if the screen doesn't have a big colormap, don't waste our time */ + /* or if it's huge, and _some_ match should have been possible */ + if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096) + return False; + + if (str != (char *)0) { + if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp)) + return False; + } else { + tmp = *color; + tmp.flags = 7; /* force to use all 3 of RGB */ + } + + if (!table) { + table = (XColor *) XtCalloc(ncells, sizeof(XColor)); + for(i=0; iaddr; + XColor screenColor; + XColor exactColor; + Screen *screen; + XtAppContext app = XtDisplayToApplicationContext(dpy); + Colormap colormap; + Status status; + String params[1]; + Cardinal num_params=1; + + if (*num_args != 2) { + XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel", + "XtToolkitError", + "String to pixel conversion needs screen and colormap arguments", + (String *)0, (Cardinal *)0); + return False; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + + /* If Xt colors, use the Xt routine and hope for the best */ +#if (XtSpecificationRelease >= 5) + if ((strcmpi(str, XtDefaultBackground) == 0) || + (strcmpi(str, XtDefaultForeground) == 0)) { + return + XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret); + } +#else + if (strcmpi(str, XtDefaultBackground) == 0) { + *closure_ret = (char*)False; + done(Pixel, WhitePixelOfScreen(screen)); + } + if (strcmpi(str, XtDefaultForeground) == 0) { + *closure_ret = (char*)False; + done(Pixel, BlackPixelOfScreen(screen)); + } +#endif + + status = XAllocNamedColor(DisplayOfScreen(screen), colormap, + (char*)str, &screenColor, &exactColor); + if (status == 0) { + String msg, type; + + /* some versions of XAllocNamedColor don't allow #xxyyzz names */ + if (str[0] == '#' && + XParseColor(DisplayOfScreen(screen), colormap, str, &exactColor) && + XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) { + *closure_ret = (char*)True; + done(Pixel, exactColor.pixel); + } + + params[0] = str; + /* Server returns a specific error code but Xlib discards it. Ugh */ + if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str, + &exactColor, &screenColor)) { + /* try to find another color that will do */ + if (nhApproxColor(screen, colormap, (char*) str, &screenColor)) { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } + type = "noColormap"; + msg = "Cannot allocate colormap entry for \"%s\""; + } + else { + /* some versions of XLookupColor also don't allow #xxyyzz names */ + if(str[0] == '#' && + (nhApproxColor(screen, colormap, (char*) str, &screenColor))) { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } + type = "badValue"; + msg = "Color name \"%s\" is not defined"; + } + + XtAppWarningMsg(app, type, "cvtStringToPixel", + "XtToolkitError", msg, params, &num_params); + *closure_ret = False; + return False; + } else { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } +} + +/* ARGSUSED */ +static void +nhFreePixel(app, toVal, closure, args, num_args) +XtAppContext app; +XrmValuePtr toVal; +XtPointer closure; +XrmValuePtr args; +Cardinal *num_args; +{ + Screen *screen; + Colormap colormap; + + if (*num_args != 2) { + XtAppWarningMsg(app, "wrongParameters", + "freePixel", "XtToolkitError", + "Freeing a pixel requires screen and colormap arguments", + (String *)0, (Cardinal *)0); + return; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + + if (closure) { + XFreeColors( DisplayOfScreen(screen), colormap, + (unsigned long*)toVal->addr, 1, (unsigned long)0 + ); + } +} + +/* [ALI] Utility function to ask Xaw for font height, since the previous + * assumption of ascent + descent is not always valid. + */ +Dimension +nhFontHeight(w) +Widget w; +#ifdef _XawTextSink_h +{ + Widget sink; + XawTextPosition pos = 0; + int resWidth, resHeight; + Arg args[1]; + + XtSetArg(args[0], XtNtextSink, &sink); + XtGetValues(w, args, 1); + + XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight); + return resHeight; +} +#else +{ + XFontStruct *fs; + Arg args[1]; + + XtSetArg(args[0], XtNfont, &fs); + XtGetValues(w, args, 1); + + /* Assume font height is ascent + descent. */ + return = fs->ascent + fs->descent; +} +#endif + +/* Global Functions ======================================================== */ +void +X11_raw_print(str) + const char *str; +{ + (void) puts(str); +} + +void +X11_raw_print_bold(str) + const char *str; +{ + (void) puts(str); +} + +void +X11_curs(window, x, y) + winid window; + int x, y; +{ + check_winid(window); + + if (x < 0 || x >= COLNO) { + impossible("curs: bad x value [%d]", x); + x = 0; + } + if (y < 0 || y >= ROWNO) { + impossible("curs: bad y value [%d]", y); + y = 0; + } + + window_list[window].cursx = x; + window_list[window].cursy = y; +} + +void +X11_putstr(window, attr, str) + winid window; + int attr; + const char *str; +{ + winid new_win; + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MESSAGE: + (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */ + toplines[TBUFSZ - 1] = 0; + append_message(wp, str); + break; + case NHW_STATUS: + adjust_status(wp, str); + break; + case NHW_MAP: + impossible("putstr: called on map window \"%s\"", str); + break; + case NHW_MENU: + if (wp->menu_information->is_menu) { + impossible( + "putstr: called on a menu window, \"%s\" discarded", + str); + break; + } + /* + * Change this menu window into a text window by creating a + * new text window, then copying it to this winid. + */ + new_win = X11_create_nhwindow(NHW_TEXT); + X11_destroy_nhwindow(window); + *wp = window_list[new_win]; + window_list[new_win].type = NHW_NONE; /* allow re-use */ + /* fall though to add text */ + case NHW_TEXT: + add_to_text_window(wp, attr, str); + break; + default: + impossible("putstr: unknown window type [%d] \"%s\"", + wp->type, str); + } +} + +/* We do event processing as a callback, so this is a null routine. */ +void X11_get_nh_event() { return; } + +int +X11_nhgetch() +{ + return input_event(EXIT_ON_KEY_PRESS); +} + + +int +X11_nh_poskey(x, y, mod) + int *x, *y, *mod; +{ + int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS); + + if (val == 0) { /* user clicked on a map window */ + *x = click_x; + *y = click_y; + *mod = click_button; + } + return val; +} + + +winid +X11_create_nhwindow(type) + int type; +{ + winid window; + struct xwindow *wp; + + if (!x_inited) + panic("create_nhwindow: windows not initialized"); + + /* + * We have already created the standard message, map, and status + * windows in the window init routine. The first window of that + * type to be created becomes the standard. + * + * A better way to do this would be to say that init_nhwindows() + * has already defined these three windows. + */ + if (type == NHW_MAP && map_win != WIN_ERR) { + window = map_win; + map_win = WIN_ERR; + return window; + } + if (type == NHW_MESSAGE && message_win != WIN_ERR) { + window = message_win; + message_win = WIN_ERR; + return window; + } + if (type == NHW_STATUS && status_win != WIN_ERR) { + window = status_win; + status_win = WIN_ERR; + return window; + } + + window = find_free_window(); + wp = &window_list[window]; + + /* The create routines will set type, popup, w, and Win_info. */ + wp->prevx = wp->prevy = wp->cursx = wp->cursy = + wp->pixel_width = wp->pixel_height = 0; + wp->keep_window = FALSE; + + switch (type) { + case NHW_MAP: + create_map_window(wp, TRUE, (Widget) 0); + break; + case NHW_MESSAGE: + create_message_window(wp, TRUE, (Widget) 0); + break; + case NHW_STATUS: + create_status_window(wp, TRUE, (Widget) 0); + break; + case NHW_MENU: + create_menu_window(wp); + break; + case NHW_TEXT: + create_text_window(wp); + break; + default: + panic("create_nhwindow: unknown type [%d]\n", type); + break; + } + return window; +} + +void +X11_clear_nhwindow(window) + winid window; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MAP: clear_map_window(wp); break; + case NHW_TEXT: clear_text_window(wp); break; + case NHW_STATUS: + case NHW_MENU: + case NHW_MESSAGE: + /* do nothing for these window types */ + break; + default: + panic("clear_nhwindow: unknown window type [%d]\n", wp->type); + break; + } +} + +void +X11_display_nhwindow(window, blocking) + winid window; + boolean blocking; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MAP: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + display_map_window(wp); /* flush map */ + /* + * We need to flush the message window here due to the way the tty + * port is set up. To flush a window, you need to call this + * routine. However, the tty port _pauses_ with a --more-- if we + * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call + * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we + * get a --more-- after every line. + * + * Perhaps the window document should mention that when the map + * is flushed, everything on the three main windows should be + * flushed. Note: we don't need to flush the status window + * because we don't buffer changes. + */ + if (WIN_MESSAGE != WIN_ERR) + display_message_window(&window_list[WIN_MESSAGE]); + if (blocking) + (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS); + break; + case NHW_MESSAGE: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + display_message_window(wp); /* flush messages */ + break; + case NHW_STATUS: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + break; /* no flushing necessary */ + case NHW_MENU: { + int n; + menu_item *selected; + + /* pop up menu */ + n = X11_select_menu(window, PICK_NONE, &selected); + if (n) { + impossible("perminvent: %d selected??", n); + free((genericptr_t)selected); + } + break; + } + case NHW_TEXT: + display_text_window(wp, blocking); /* pop up text window */ + break; + default: + panic("display_nhwindow: unknown window type [%d]\n", wp->type); + break; + } +} + +void +X11_destroy_nhwindow(window) + winid window; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + /* + * "Zap" known windows, but don't destroy them. We need to keep the + * toplevel widget popped up so that later windows (e.g. tombstone) + * are visible on DECWindow systems. This is due to the virtual + * roots that the DECWindow wm creates. + */ + if (window == WIN_MESSAGE) { + wp->keep_window = TRUE; + WIN_MESSAGE = WIN_ERR; + iflags.window_inited = 0; + } else if (window == WIN_MAP) { + wp->keep_window = TRUE; + WIN_MAP = WIN_ERR; + } else if (window == WIN_STATUS) { + wp->keep_window = TRUE; + WIN_STATUS = WIN_ERR; + } else if (window == WIN_INVEN) { + /* don't need to keep this one */ + WIN_INVEN = WIN_ERR; + } + + switch (wp->type) { + case NHW_MAP: + destroy_map_window(wp); + break; + case NHW_MENU: + destroy_menu_window(wp); + break; + case NHW_TEXT: + destroy_text_window(wp); + break; + case NHW_STATUS: + destroy_status_window(wp); + break; + case NHW_MESSAGE: + destroy_message_window(wp); + break; + default: + panic("destroy_nhwindow: unknown window type [%d]", wp->type); + break; + } +} + +void +X11_update_inventory() +{ + if (x_inited && window_list[WIN_INVEN].menu_information->is_up) { + updated_inventory = 1; /* hack to avoid mapping&raising window */ + (void) display_inventory((char *)0, FALSE); + updated_inventory = 0; + } +} + +/* The current implementation has all of the saved lines on the screen. */ +int X11_doprev_message() { return 0; } + +void +X11_nhbell() +{ + /* We can't use XBell until toplevel has been initialized. */ + if (x_inited) + XBell(XtDisplay(toplevel), 0); + /* else print ^G ?? */ +} + +void X11_mark_synch() +{ + if (x_inited) { + /* + * The window document is unclear about the status of text + * that has been pline()d but not displayed w/display_nhwindow(). + * Both the main and tty code assume that a pline() followed + * by mark_synch() results in the text being seen, even if + * display_nhwindow() wasn't called. Duplicate this behavior. + */ + if (WIN_MESSAGE != WIN_ERR) + display_message_window(&window_list[WIN_MESSAGE]); + XSync(XtDisplay(toplevel), False); + } +} + +void X11_wait_synch() { if (x_inited) XFlush(XtDisplay(toplevel)); } + + +/* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */ +void X11_resume_nhwindows() { return; } + +/* ARGSUSED */ +void X11_suspend_nhwindows(str) const char *str; { return; } + +/* Under X, we don't need to initialize the number pad. */ +/* ARGSUSED */ +void X11_number_pad(state) int state; { return; } /* called from options.c */ + + +void X11_start_screen() { return; } /* called from setftty() in unixtty.c */ +void X11_end_screen() { return; } /* called from settty() in unixtty.c */ + +#ifdef GRAPHIC_TOMBSTONE +void X11_outrip(window, how) + winid window; + int how; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + if (wp->type == NHW_TEXT) { + wp->text_information->is_rip = TRUE; + } else { + panic("ripout on non-text window (window type [%d])\n", wp->type); + } + + calculate_rip_text(how); +} +#endif + +/* init and exit nhwindows ------------------------------------------------- */ + +XtAppContext app_context; /* context of application */ +Widget toplevel = (Widget) 0; /* toplevel widget */ +Atom wm_delete_window; /* pop down windows */ + +static XtActionsRec actions[] = { + {"dismiss_file", dismiss_file}, /* action for file viewing widget */ + {"delete_file", delete_file}, /* action for file delete-window */ + {"dismiss_text", dismiss_text}, /* button action for text widget */ + {"delete_text", delete_text}, /* delete action for text widget */ + {"key_dismiss_text",key_dismiss_text},/* key action for text widget */ +#ifdef GRAPHIC_TOMBSTONE + {"rip_dismiss_text",rip_dismiss_text},/* action for rip in text widget */ +#endif + {"menu_key", menu_key}, /* action for menu accelerators */ + {"yn_key", yn_key}, /* action for yn accelerators */ + {"yn_delete", yn_delete}, /* action for yn delete-window */ + {"askname_delete", askname_delete},/* action for askname delete-window */ + {"getline_delete", getline_delete},/* action for getline delete-window */ + {"menu_delete", menu_delete}, /* action for menu delete-window */ + {"ec_key", ec_key}, /* action for extended commands */ + {"ec_delete", ec_delete}, /* action for ext-com menu delete */ + {"ps_key", ps_key}, /* action for player selection */ + {"race_key", race_key}, /* action for race selection */ + {"gend_key", gend_key}, /* action for gender selection */ + {"algn_key", algn_key}, /* action for alignment selection */ + {"X11_hangup", X11_hangup}, /* action for delete of top-level */ + {"input", map_input}, /* action for key input */ + {"scroll", nh_keyscroll}, /* action for scrolling by keys */ +}; + +static XtResource resources[] = { + { "slow", "Slow", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,slow), XtRString, "False" }, + { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,autofocus), XtRString, "False" }, + { "message_line", "Message_line", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,message_line), XtRString, "False" }, + { "double_tile_size", "Double_tile_size", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,double_tile_size), XtRString, "False" }, + { "tile_file", "Tile_file", XtRString, sizeof(String), + XtOffset(AppResources *,tile_file), XtRString, "" }, + { "icon", "Icon", XtRString, sizeof(String), + XtOffset(AppResources *,icon), XtRString, "nh72" }, + { "message_lines", "Message_lines", XtRInt, sizeof(int), + XtOffset(AppResources *,message_lines), XtRString, "12" }, + { "pet_mark_bitmap", "Pet_mark_bitmap", XtRString, sizeof(String), + XtOffset(AppResources *,pet_mark_bitmap), XtRString, "pet_mark.xbm" }, + { "pet_mark_color", "Pet_mark_color", XtRPixel, sizeof(XtRPixel), + XtOffset(AppResources *,pet_mark_color), XtRString, "Red" }, +#ifdef GRAPHIC_TOMBSTONE + { "tombstone", "Tombstone", XtRString, sizeof(String), + XtOffset(AppResources *,tombstone), XtRString, "rip.xpm" }, + { "tombtext_x", "Tombtext_x", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_x), XtRString, "155" }, + { "tombtext_y", "Tombtext_y", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_y), XtRString, "78" }, + { "tombtext_dx", "Tombtext_dx", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_dx), XtRString, "0" }, + { "tombtext_dy", "Tombtext_dy", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_dy), XtRString, "13" }, +#endif +}; + +void +X11_init_nhwindows(argcp,argv) +int* argcp; +char** argv; +{ + static const char *banner_text[] = { + COPYRIGHT_BANNER_A, + COPYRIGHT_BANNER_B, + COPYRIGHT_BANNER_C, + "", + "", + 0 + }; + register const char **pp; + int i; + Cardinal num_args; + Arg args[4]; + uid_t savuid; + + /* Init windows to nothing. */ + for (i = 0; i < MAX_WINDOWS; i++) + window_list[i].type = NHW_NONE; + + /* + * setuid hack: make sure that if nethack is setuid, to use real uid + * when opening X11 connections, in case the user is using xauth, since + * the "games" or whatever user probably doesn't have permission to open + * a window on the user's display. This code is harmless if the binary + * is not installed setuid. See include/system.h on compilation failures. + */ + savuid = geteuid(); + (void) seteuid(getuid()); + + XSetIOErrorHandler((XIOErrorHandler) hangup); + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + toplevel = XtAppInitialize( + &app_context, + "NetHack", /* application class */ + (XrmOptionDescList)0, 0, /* options list */ + argcp, (String *)argv, /* command line args */ + (String *)0, /* fallback resources */ + (ArgList)args, num_args); + XtOverrideTranslations(toplevel, + XtParseTranslationTable("WM_PROTOCOLS: X11_hangup()")); + + /* We don't need to realize the top level widget. */ + +#ifdef TEXTCOLOR + /* add new color converter to deal with overused colormaps */ + XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel, + (XtConvertArgList)nhcolorConvertArgs, + XtNumber(nhcolorConvertArgs), + XtCacheByDisplay, nhFreePixel); +#endif /* TEXTCOLOR */ + + /* Register the actions mentioned in "actions". */ + XtAppAddActions(app_context, actions, XtNumber(actions)); + + /* Get application-wide resources */ + XtGetApplicationResources(toplevel, (XtPointer)&appResources, + resources, XtNumber(resources), + (ArgList)0, ZERO); + + /* Initialize other things. */ + init_standard_windows(); + + /* Give the window manager an icon to use; toplevel must be realized. */ + if (appResources.icon && *appResources.icon) { + struct icon_info *ip; + + for (ip = icon_data; ip->name; ip++) + if (!strcmp(appResources.icon, ip->name)) { + icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel), + XtWindow(toplevel), + (genericptr_t)ip->bits, ip->width, ip->height); + if (icon_pixmap != None) { + XWMHints hints; + + (void) memset((genericptr_t)&hints, 0, sizeof(XWMHints)); + hints.flags = IconPixmapHint; + hints.icon_pixmap = icon_pixmap; + XSetWMHints(XtDisplay(toplevel), + XtWindow(toplevel), &hints); + } + break; + } + } + + /* end of setuid hack: reset uid back to the "games" uid */ + (void) seteuid(savuid); + + x_inited = TRUE; /* X is now initialized */ + + /* Display the startup banner in the message window. */ + for (pp = banner_text; *pp; pp++) + X11_putstr(WIN_MESSAGE, 0, *pp); +} + +/* + * All done. + */ +/* ARGSUSED */ +void X11_exit_nhwindows(dummy) + const char *dummy; +{ + extern Pixmap tile_pixmap; /* from winmap.c */ + + /* explicitly free the icon and tile pixmaps */ + if (icon_pixmap != None) { + XFreePixmap(XtDisplay(toplevel), icon_pixmap); + icon_pixmap = None; + } + if (tile_pixmap != None) { + XFreePixmap(XtDisplay(toplevel), tile_pixmap); + tile_pixmap = None; + } + if (WIN_INVEN != WIN_ERR) + X11_destroy_nhwindow(WIN_INVEN); + if (WIN_STATUS != WIN_ERR) + X11_destroy_nhwindow(WIN_STATUS); + if (WIN_MAP != WIN_ERR) + X11_destroy_nhwindow(WIN_MAP); + if (WIN_MESSAGE != WIN_ERR) + X11_destroy_nhwindow(WIN_MESSAGE); +} + + +/* delay_output ------------------------------------------------------------ */ + +/* + * Timeout callback for delay_output(). Send a fake message to the map + * window. + */ +/* ARGSUSED */ +static void +d_timeout(client_data, id) + XtPointer client_data; + XtIntervalId *id; +{ + XEvent event; + XClientMessageEvent *mesg; + + /* Set up a fake message to the event handler. */ + mesg = (XClientMessageEvent *) &event; + mesg->type = ClientMessage; + mesg->message_type = XA_STRING; + mesg->format = 8; + XSendEvent(XtDisplay(window_list[WIN_MAP].w), + XtWindow(window_list[WIN_MAP].w), + False, + NoEventMask, + (XEvent*) mesg); +} + +/* + * Delay for 50ms. This is not implemented asynch. Maybe later. + * Start the timeout, then wait in the event loop. The timeout + * function will send an event to the map window which will be waiting + * for a sent event. + */ +void +X11_delay_output() +{ + if (!x_inited) return; + + (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0); + + /* The timeout function will enable the event loop exit. */ + (void) x_event(EXIT_ON_SENT_EVENT); +} + +/* X11_hangup -------------------------------------------------------------- */ +/* ARGSUSED */ +static void +X11_hangup(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */ +} + +/* askname ----------------------------------------------------------------- */ +/* ARGSUSED */ +static void +askname_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + nh_XtPopdown(w); + (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */ + exit_x_event = TRUE; +} + +/* Callback for askname dialog widget. */ +/* ARGSUSED */ +static void +askname_done(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + int len; + char *s; + Widget dialog = (Widget) client_data; + + s = (char *) GetDialogResponse(dialog); + + len = strlen(s); + if (len == 0) { + X11_nhbell(); + return; + } + + /* Truncate name if necessary */ + if (len >= sizeof(plname)-1) + len = sizeof(plname)-1; + + (void) strncpy(plname, s, len); + plname[len] = '\0'; + XtFree(s); + + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + +void +X11_askname() +{ + Widget popup, dialog; + Arg args[1]; + + XtSetArg(args[0], XtNallowShellResize, True); + + popup = XtCreatePopupShell("askname", transientShellWidgetClass, + toplevel, args, ONE); + XtOverrideTranslations(popup, + XtParseTranslationTable("WM_PROTOCOLS: askname_delete()")); + + dialog = CreateDialog(popup, "dialog", + askname_done, (XtCallbackProc) 0); + + SetDialogPrompt(dialog, "What is your name?"); /* set prompt */ + SetDialogResponse(dialog, ""); /* set default answer */ + + XtRealizeWidget(popup); + positionpopup(popup, TRUE); /* center,bottom */ + + nh_XtPopup(popup, (int)XtGrabExclusive, dialog); + + /* The callback will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); +} + + +/* getline ----------------------------------------------------------------- */ +/* This uses Tim Theisen's dialog widget set (from GhostView). */ + +static Widget getline_popup, getline_dialog; + +#define CANCEL_STR "\033" +static char *getline_input; + + +/* Callback for getline dialog widget. */ +/* ARGSUSED */ +static void +done_button(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + char *s; + Widget dialog = (Widget) client_data; + + s = (char *) GetDialogResponse(dialog); + Strcpy(getline_input, s); + XtFree(s); + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + +/* ARGSUSED */ +static void +getline_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Strcpy(getline_input, CANCEL_STR); + nh_XtPopdown(w); + exit_x_event = TRUE; +} + +/* Callback for getline dialog widget. */ +/* ARGSUSED */ +static void +abort_button(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Widget dialog = (Widget) client_data; + + Strcpy(getline_input, CANCEL_STR); + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + + +void +X11_getlin(question, input) + const char *question; + char *input; +{ + static boolean need_to_init = True; + + getline_input = input; + + flush_screen(1); + if (need_to_init) { + Arg args[1]; + + need_to_init = False; + + XtSetArg(args[0], XtNallowShellResize, True); + + getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass, + toplevel, args, ONE); + XtOverrideTranslations(getline_popup, + XtParseTranslationTable("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\ + q: dismiss_file()\n\ + Escape: dismiss_file()\n\ + : 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("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\ + : 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 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("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*/