1 /* NetHack 3.6 winX.c $NHDT-Date: 1432512808 2015/05/25 00:13:28 $ $NHDT-Branch: master $:$NHDT-Revision: 1.33 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
6 * "Main" file for the X window-port. This contains most of the interface
7 * routines. Please see doc/window.doc for an description of the window
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
15 #ifdef MSDOS /* from compiler */
16 #define SHORT_FILENAMES
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Shell.h>
22 #include <X11/Xaw/AsciiText.h>
23 #include <X11/Xaw/Label.h>
24 #include <X11/Xaw/Form.h>
25 #include <X11/Xaw/Scrollbar.h>
26 #include <X11/Xaw/Paned.h>
27 #include <X11/Xaw/Cardinals.h>
28 #include <X11/Xatom.h>
31 /* for color support */
32 #ifdef SHORT_FILENAMES
33 #include <X11/IntrinsP.h>
35 #include <X11/IntrinsicP.h>
38 #ifdef PRESERVE_NO_SYSV
42 #undef PRESERVE_NO_SYSV
45 #ifdef SHORT_FILENAMES
46 #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
57 /* Should be defined in <X11/Intrinsic.h> but you never know */
58 #ifndef XtSpecificationRelease
59 #define XtSpecificationRelease 0
65 #include "../win/X11/nh72icon"
66 #include "../win/X11/nh56icon"
67 #include "../win/X11/nh32icon"
69 static struct icon_info {
72 unsigned width, height;
73 } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
74 { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
75 { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
76 { (const char *) 0, (unsigned char *) 0, 0, 0 } };
79 * Private global variables (shared among the window port files).
81 struct xwindow window_list[MAX_WINDOWS];
82 AppResources appResources;
83 void FDECL((*input_func), (Widget, XEvent *, String *, Cardinal *));
84 int click_x, click_y, click_button; /* Click position on a map window */
85 /* (filled by set_button_values()). */
86 int updated_inventory;
88 #if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
89 #if XtSpecificationRelease >= 6
90 #define X11_HANGUP_SIGNAL
91 static XtSignalId X11_sig_id;
95 /* this is only needed until X11_status_* routines are written */
96 extern NEARDATA winid WIN_STATUS;
98 /* Interface definition, for windows.c */
99 struct window_procs X11_procs = {
100 "X11", WC_COLOR | WC_HILITE_PET | WC_TILED_MAP, 0L, X11_init_nhwindows,
101 X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows,
102 X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow,
103 X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs,
104 X11_putstr, genl_putmixed, X11_display_file, X11_start_menu, X11_add_menu,
105 X11_end_menu, X11_select_menu,
106 genl_message_menu, /* no need for X-specific handling */
107 X11_update_inventory, X11_mark_synch, X11_wait_synch,
114 X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch,
115 X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function,
116 X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output,
117 #ifdef CHANGE_COLOR /* only a Mac option currently */
120 /* other defs that really should go away (they're tty specific) */
121 X11_start_screen, X11_end_screen,
122 #ifdef GRAPHIC_TOMBSTONE
127 X11_preference_update, genl_getmsghistory, genl_putmsghistory,
128 #ifdef STATUS_VIA_WINDOWPORT
129 genl_status_init, genl_status_finish, genl_status_enablefield,
131 #ifdef STATUS_HILITES
132 genl_status_threshold,
135 genl_can_suspend_no, /* XXX may not always be correct */
141 static void FDECL(dismiss_file, (Widget, XEvent *, String *, Cardinal *));
142 static void FDECL(delete_file, (Widget, XEvent *, String *, Cardinal *));
143 static void FDECL(yn_key, (Widget, XEvent *, String *, Cardinal *));
144 static void FDECL(yn_delete, (Widget, XEvent *, String *, Cardinal *));
145 static void FDECL(askname_delete, (Widget, XEvent *, String *, Cardinal *));
146 static void FDECL(getline_delete, (Widget, XEvent *, String *, Cardinal *));
147 static void FDECL(X11_hangup, (Widget, XEvent *, String *, Cardinal *));
148 static int FDECL(input_event, (int));
149 static void FDECL(win_visible, (Widget, XtPointer, XEvent *, Boolean *));
150 static void NDECL(init_standard_windows);
151 #ifdef X11_HANGUP_SIGNAL
152 static void FDECL(X11_sig, (int));
153 static void FDECL(X11_sig_cb, (XtPointer, XtSignalId *));
159 static boolean x_inited = FALSE; /* TRUE if window system is set up. */
160 static winid message_win = WIN_ERR, /* These are the winids of the */
161 map_win = WIN_ERR, /* message, map, and status */
162 status_win = WIN_ERR; /* windows, when they are created */
163 /* in init_windows(). */
164 static Pixmap icon_pixmap = None; /* Pixmap for icon. */
167 * Find the window structure that corresponds to the given widget. Note
168 * that this is not the popup widget, nor the viewport, but the child.
177 /* Search to find the corresponding window. Look at the main widget, */
178 /* popup, the parent of the main widget, then parent of the widget. */
179 for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
180 if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w
181 || (wp->w && (XtParent(wp->w)) == w)
182 || (wp->popup == XtParent(w))))
185 if (windex == MAX_WINDOWS)
186 panic("find_widget: can't match widget");
191 * Find a free window slot for use.
199 for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS;
201 if (wp->type == NHW_NONE)
204 if (windex == MAX_WINDOWS)
205 panic("find_free_window: no free windows!");
206 return (winid) windex;
210 * Color conversion. The default X11 color converters don't try very
211 * hard to find matching colors in PseudoColor visuals. If they can't
212 * allocate the exact color, they puke and give you something stupid.
213 * This is an attempt to find some close readonly cell and use it.
215 XtConvertArgRec const nhcolorConvertArgs[] = {
216 { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
218 { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.colormap),
222 #define done(type, value) \
224 if (toVal->addr != 0) { \
225 if (toVal->size < sizeof(type)) { \
226 toVal->size = sizeof(type); \
229 *(type *)(toVal->addr) = (value); \
231 static type static_val; \
232 static_val = (value); \
233 toVal->addr = (genericptr_t) &static_val; \
235 toVal->size = sizeof(type); \
240 * Find a color that approximates the color named in "str". The "str" color
241 * may be a color name ("red") or number ("#7f0000"). If str == NULL, then
242 * "color" is assumed to contain the RGB color wanted.
243 * The approximate color found is returned in color as well.
244 * Return True if something close was found.
247 nhApproxColor(screen, colormap, str, color)
248 Screen *screen; /* screen to use */
249 Colormap colormap; /* the colormap to use */
250 char *str; /* color name */
251 XColor *color; /* the X color structure; changed only if successful */
254 long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
256 static XColor *table = 0;
260 /* if the screen doesn't have a big colormap, don't waste our time */
261 /* or if it's huge, and _some_ match should have been possible */
262 if ((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
265 if (str != (char *) 0) {
266 if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
270 tmp.flags = 7; /* force to use all 3 of RGB */
274 table = (XColor *) XtCalloc(ncells, sizeof(XColor));
275 for (i = 0; i < ncells; i++)
277 XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
280 /* go thru cells and look for the one with smallest diff */
281 /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
282 /* a more knowledgeable color person might improve this -dlc */
284 for (i = 0; i < ncells; i++) {
285 if (table[i].flags == tmp.flags) {
286 j = (int) table[i].red - (int) tmp.red;
290 j = (int) table[i].green - (int) tmp.green;
294 j = (int) table[i].blue - (int) tmp.blue;
300 tmp.pixel = i; /* table[i].pixel == i */
305 if (cdiff == 16777216)
306 return False; /* nothing found?! */
309 * Found something. Return it and mark this color as used to avoid
310 * reuse. Reuse causes major contrast problems :-)
312 *color = table[tmp.pixel];
313 table[tmp.pixel].flags = 0;
314 /* try to alloc the color, so no one else can change it */
315 if (!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
323 nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
329 XtPointer *closure_ret;
331 String str = (String) fromVal->addr;
335 XtAppContext app = XtDisplayToApplicationContext(dpy);
339 Cardinal num_params = 1;
341 if (*num_args != 2) {
343 app, "wrongParameters", "cvtStringToPixel", "XtToolkitError",
344 "String to pixel conversion needs screen and colormap arguments",
345 (String *) 0, (Cardinal *) 0);
349 screen = *((Screen **) args[0].addr);
350 colormap = *((Colormap *) args[1].addr);
352 /* If Xt colors, use the Xt routine and hope for the best */
353 #if (XtSpecificationRelease >= 5)
354 if ((strcmpi(str, XtDefaultBackground) == 0)
355 || (strcmpi(str, XtDefaultForeground) == 0)) {
356 return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal,
360 if (strcmpi(str, XtDefaultBackground) == 0) {
361 *closure_ret = (char *) False;
362 done(Pixel, WhitePixelOfScreen(screen));
364 if (strcmpi(str, XtDefaultForeground) == 0) {
365 *closure_ret = (char *) False;
366 done(Pixel, BlackPixelOfScreen(screen));
370 status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char *) str,
371 &screenColor, &exactColor);
375 /* some versions of XAllocNamedColor don't allow #xxyyzz names */
376 if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap,
378 && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
379 *closure_ret = (char *) True;
380 done(Pixel, exactColor.pixel);
384 /* Server returns a specific error code but Xlib discards it. Ugh */
385 if (XLookupColor(DisplayOfScreen(screen), colormap, (char *) str,
386 &exactColor, &screenColor)) {
387 /* try to find another color that will do */
388 if (nhApproxColor(screen, colormap, (char *) str, &screenColor)) {
389 *closure_ret = (char *) True;
390 done(Pixel, screenColor.pixel);
392 type = nhStr("noColormap");
393 msg = nhStr("Cannot allocate colormap entry for \"%s\"");
395 /* some versions of XLookupColor also don't allow #xxyyzz names */
397 && (nhApproxColor(screen, colormap, (char *) str,
399 *closure_ret = (char *) True;
400 done(Pixel, screenColor.pixel);
402 type = nhStr("badValue");
403 msg = nhStr("Color name \"%s\" is not defined");
406 XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg,
407 params, &num_params);
408 *closure_ret = False;
411 *closure_ret = (char *) True;
412 done(Pixel, screenColor.pixel);
418 nhFreePixel(app, toVal, closure, args, num_args)
428 if (*num_args != 2) {
430 app, "wrongParameters", "freePixel", "XtToolkitError",
431 "Freeing a pixel requires screen and colormap arguments",
432 (String *) 0, (Cardinal *) 0);
436 screen = *((Screen **) args[0].addr);
437 colormap = *((Colormap *) args[1].addr);
440 XFreeColors(DisplayOfScreen(screen), colormap,
441 (unsigned long *) toVal->addr, 1, (unsigned long) 0);
445 /* [ALI] Utility function to ask Xaw for font height, since the previous
446 * assumption of ascent + descent is not always valid.
448 Dimension nhFontHeight(w) Widget w;
449 #ifdef _XawTextSink_h
452 XawTextPosition pos = 0;
453 int resWidth, resHeight;
456 XtSetArg(args[0], XtNtextSink, &sink);
457 XtGetValues(w, args, 1);
459 XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
467 XtSetArg(args[0], XtNfont, &fs);
468 XtGetValues(w, args, 1);
470 /* Assume font height is ascent + descent. */
471 return = fs->ascent + fs->descent;
475 /* Global Functions ========================================================
485 X11_raw_print_bold(str)
492 X11_curs(window, x, y)
498 if (x < 0 || x >= COLNO) {
499 impossible("curs: bad x value [%d]", x);
502 if (y < 0 || y >= ROWNO) {
503 impossible("curs: bad y value [%d]", y);
507 window_list[window].cursx = x;
508 window_list[window].cursy = y;
512 X11_putstr(window, attr, str)
521 wp = &window_list[window];
525 (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
526 toplines[TBUFSZ - 1] = 0;
527 append_message(wp, str);
530 adjust_status(wp, str);
533 impossible("putstr: called on map window \"%s\"", str);
536 if (wp->menu_information->is_menu) {
537 impossible("putstr: called on a menu window, \"%s\" discarded",
542 * Change this menu window into a text window by creating a
543 * new text window, then copying it to this winid.
545 new_win = X11_create_nhwindow(NHW_TEXT);
546 X11_destroy_nhwindow(window);
547 *wp = window_list[new_win];
548 window_list[new_win].type = NHW_NONE; /* allow re-use */
549 /* fall though to add text */
551 add_to_text_window(wp, attr, str);
554 impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str);
558 /* We do event processing as a callback, so this is a null routine. */
568 return input_event(EXIT_ON_KEY_PRESS);
572 X11_nh_poskey(x, y, mod)
575 int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
577 if (val == 0) { /* user clicked on a map window */
586 X11_create_nhwindow(type)
593 panic("create_nhwindow: windows not initialized");
595 #ifdef X11_HANGUP_SIGNAL
596 /* set up our own signal handlers on the first call. Can't do this in
597 * X11_init_nhwindows because unixmain sets its handler after calling
598 * all the init routines. */
599 if (X11_sig_id == 0) {
600 X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer) 0);
603 struct sigaction sact;
605 (void) memset((char *) &sact, 0, sizeof(struct sigaction));
606 sact.sa_handler = (SIG_RET_TYPE) X11_sig;
607 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
609 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
613 (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
615 (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
622 * We have already created the standard message, map, and status
623 * windows in the window init routine. The first window of that
624 * type to be created becomes the standard.
626 * A better way to do this would be to say that init_nhwindows()
627 * has already defined these three windows.
629 if (type == NHW_MAP && map_win != WIN_ERR) {
634 if (type == NHW_MESSAGE && message_win != WIN_ERR) {
635 window = message_win;
636 message_win = WIN_ERR;
639 if (type == NHW_STATUS && status_win != WIN_ERR) {
641 status_win = WIN_ERR;
645 window = find_free_window();
646 wp = &window_list[window];
648 /* The create routines will set type, popup, w, and Win_info. */
649 wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width =
650 wp->pixel_height = 0;
651 wp->keep_window = FALSE;
655 create_map_window(wp, TRUE, (Widget) 0);
658 create_message_window(wp, TRUE, (Widget) 0);
661 create_status_window(wp, TRUE, (Widget) 0);
664 create_menu_window(wp);
667 create_text_window(wp);
670 panic("create_nhwindow: unknown type [%d]", type);
677 X11_clear_nhwindow(window)
683 wp = &window_list[window];
687 clear_map_window(wp);
690 clear_text_window(wp);
695 /* do nothing for these window types */
698 panic("clear_nhwindow: unknown window type [%d]", wp->type);
704 X11_display_nhwindow(window, blocking)
711 wp = &window_list[window];
716 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
717 display_map_window(wp); /* flush map */
719 * We need to flush the message window here due to the way the tty
720 * port is set up. To flush a window, you need to call this
721 * routine. However, the tty port _pauses_ with a --more-- if we
722 * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call
723 * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
724 * get a --more-- after every line.
726 * Perhaps the window document should mention that when the map
727 * is flushed, everything on the three main windows should be
728 * flushed. Note: we don't need to flush the status window
729 * because we don't buffer changes.
731 if (WIN_MESSAGE != WIN_ERR)
732 display_message_window(&window_list[WIN_MESSAGE]);
734 (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
738 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
739 display_message_window(wp); /* flush messages */
743 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
744 break; /* no flushing necessary */
750 n = X11_select_menu(window, PICK_NONE, &selected);
752 impossible("perminvent: %d selected??", n);
753 free((genericptr_t) selected);
758 display_text_window(wp, blocking); /* pop up text window */
761 panic("display_nhwindow: unknown window type [%d]", wp->type);
767 X11_destroy_nhwindow(window)
773 wp = &window_list[window];
775 * "Zap" known windows, but don't destroy them. We need to keep the
776 * toplevel widget popped up so that later windows (e.g. tombstone)
777 * are visible on DECWindow systems. This is due to the virtual
778 * roots that the DECWindow wm creates.
780 if (window == WIN_MESSAGE) {
781 wp->keep_window = TRUE;
782 WIN_MESSAGE = WIN_ERR;
783 iflags.window_inited = 0;
784 } else if (window == WIN_MAP) {
785 wp->keep_window = TRUE;
787 } else if (window == WIN_STATUS) {
788 wp->keep_window = TRUE;
789 WIN_STATUS = WIN_ERR;
790 } else if (window == WIN_INVEN) {
791 /* don't need to keep this one */
797 destroy_map_window(wp);
800 destroy_menu_window(wp);
803 destroy_text_window(wp);
806 destroy_status_window(wp);
809 destroy_message_window(wp);
812 panic("destroy_nhwindow: unknown window type [%d]", wp->type);
818 X11_update_inventory()
820 if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
821 updated_inventory = 1; /* hack to avoid mapping&raising window */
822 (void) display_inventory((char *) 0, FALSE);
823 updated_inventory = 0;
827 /* The current implementation has all of the saved lines on the screen. */
837 /* We can't use XBell until toplevel has been initialized. */
839 XBell(XtDisplay(toplevel), 0);
840 /* else print ^G ?? */
848 * The window document is unclear about the status of text
849 * that has been pline()d but not displayed w/display_nhwindow().
850 * Both the main and tty code assume that a pline() followed
851 * by mark_synch() results in the text being seen, even if
852 * display_nhwindow() wasn't called. Duplicate this behavior.
854 if (WIN_MESSAGE != WIN_ERR)
855 display_message_window(&window_list[WIN_MESSAGE]);
856 XSync(XtDisplay(toplevel), False);
864 XFlush(XtDisplay(toplevel));
867 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
869 X11_resume_nhwindows()
875 X11_suspend_nhwindows(str)
883 /* Under X, we don't need to initialize the number pad. */
885 void X11_number_pad(state) /* called from options.c */
897 } /* called from setftty() in unixtty.c */
902 } /* called from settty() in unixtty.c */
904 #ifdef GRAPHIC_TOMBSTONE
906 X11_outrip(window, how, when)
914 wp = &window_list[window];
916 if (wp->type == NHW_TEXT) {
917 wp->text_information->is_rip = TRUE;
919 panic("ripout on non-text window (window type [%d])", wp->type);
922 calculate_rip_text(how, when);
926 /* init and exit nhwindows -------------------------------------------------
929 XtAppContext app_context; /* context of application */
930 Widget toplevel = (Widget) 0; /* toplevel widget */
931 Atom wm_delete_window; /* pop down windows */
933 static XtActionsRec actions[] = {
934 { nhStr("dismiss_file"), dismiss_file }, /* file viewing widget */
935 { nhStr("delete_file"), delete_file }, /* file delete-window */
936 { nhStr("dismiss_text"), dismiss_text }, /* text widget button action */
937 { nhStr("delete_text"), delete_text }, /* text widget delete action */
938 { nhStr("key_dismiss_text"),
939 key_dismiss_text }, /* text widget key action */
940 #ifdef GRAPHIC_TOMBSTONE
941 { nhStr("rip_dismiss_text"), rip_dismiss_text }, /* rip in text widget */
943 { nhStr("menu_key"), menu_key }, /* menu accelerators */
944 { nhStr("yn_key"), yn_key }, /* yn accelerators */
945 { nhStr("yn_delete"), yn_delete }, /* yn delete-window */
946 { nhStr("askname_delete"), askname_delete }, /* askname delete-window */
947 { nhStr("getline_delete"), getline_delete }, /* getline delete-window */
948 { nhStr("menu_delete"), menu_delete }, /* menu delete-window */
949 { nhStr("ec_key"), ec_key }, /* extended commands */
950 { nhStr("ec_delete"), ec_delete }, /* ext-com menu delete */
951 { nhStr("ps_key"), ps_key }, /* player selection */
952 { nhStr("race_key"), race_key }, /* race selection */
953 { nhStr("gend_key"), gend_key }, /* gender selection */
954 { nhStr("algn_key"), algn_key }, /* alignment selection */
955 { nhStr("X11_hangup"), X11_hangup }, /* delete of top-level */
956 { nhStr("input"), map_input }, /* key input */
957 { nhStr("scroll"), nh_keyscroll }, /* scrolling by keys */
960 static XtResource resources[] = {
961 { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean),
962 XtOffset(AppResources *, slow), XtRString, nhStr("True") },
963 { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean),
964 XtOffset(AppResources *, autofocus), XtRString, nhStr("False") },
965 { nhStr("message_line"), nhStr("Message_line"), XtRBoolean,
966 sizeof(Boolean), XtOffset(AppResources *, message_line), XtRString,
968 { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean,
969 sizeof(Boolean), XtOffset(AppResources *, double_tile_size), XtRString,
971 { nhStr("tile_file"), nhStr("Tile_file"), XtRString, sizeof(String),
972 XtOffset(AppResources *, tile_file), XtRString, nhStr("x11tiles") },
973 { nhStr("icon"), nhStr("Icon"), XtRString, sizeof(String),
974 XtOffset(AppResources *, icon), XtRString, nhStr("nh72") },
975 { nhStr("message_lines"), nhStr("Message_lines"), XtRInt, sizeof(int),
976 XtOffset(AppResources *, message_lines), XtRString, nhStr("12") },
977 { nhStr("pet_mark_bitmap"), nhStr("Pet_mark_bitmap"), XtRString,
978 sizeof(String), XtOffset(AppResources *, pet_mark_bitmap), XtRString,
979 nhStr("pet_mark.xbm") },
980 { nhStr("pet_mark_color"), nhStr("Pet_mark_color"), XtRPixel,
981 sizeof(XtRPixel), XtOffset(AppResources *, pet_mark_color), XtRString,
983 { nhStr("pilemark_bitmap"), nhStr("Pilemark_bitmap"), XtRString,
984 sizeof(String), XtOffset(AppResources *, pilemark_bitmap), XtRString,
985 nhStr("pilemark.xbm") },
986 { nhStr("pilemark_color"), nhStr("Pilemark_color"), XtRPixel,
987 sizeof(XtRPixel), XtOffset(AppResources *, pilemark_color), XtRString,
989 #ifdef GRAPHIC_TOMBSTONE
990 { nhStr("tombstone"), "Tombstone", XtRString, sizeof(String),
991 XtOffset(AppResources *, tombstone), XtRString, "rip.xpm" },
992 { nhStr("tombtext_x"), "Tombtext_x", XtRInt, sizeof(int),
993 XtOffset(AppResources *, tombtext_x), XtRString, "155" },
994 { nhStr("tombtext_y"), "Tombtext_y", XtRInt, sizeof(int),
995 XtOffset(AppResources *, tombtext_y), XtRString, "78" },
996 { nhStr("tombtext_dx"), "Tombtext_dx", XtRInt, sizeof(int),
997 XtOffset(AppResources *, tombtext_dx), XtRString, "0" },
998 { nhStr("tombtext_dy"), "Tombtext_dy", XtRInt, sizeof(int),
999 XtOffset(AppResources *, tombtext_dy), XtRString, "13" },
1004 X11_init_nhwindows(argcp, argv)
1013 /* Init windows to nothing. */
1014 for (i = 0; i < MAX_WINDOWS; i++)
1015 window_list[i].type = NHW_NONE;
1017 /* add another option that can be set */
1018 set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
1021 * setuid hack: make sure that if nethack is setuid, to use real uid
1022 * when opening X11 connections, in case the user is using xauth, since
1023 * the "games" or whatever user probably doesn't have permission to open
1024 * a window on the user's display. This code is harmless if the binary
1025 * is not installed setuid. See include/system.h on compilation failures.
1028 (void) seteuid(getuid());
1030 XSetIOErrorHandler((XIOErrorHandler) hangup);
1033 XtSetArg(args[num_args], XtNallowShellResize, True);
1036 XtAppInitialize(&app_context, "NetHack", /* application class */
1037 (XrmOptionDescList) 0, 0, /* options list */
1038 argcp, (String *) argv, /* command line args */
1039 (String *) 0, /* fallback resources */
1040 (ArgList) args, num_args);
1041 XtOverrideTranslations(
1043 XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
1045 /* We don't need to realize the top level widget. */
1048 /* add new color converter to deal with overused colormaps */
1049 XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
1050 (XtConvertArgList) nhcolorConvertArgs,
1051 XtNumber(nhcolorConvertArgs), XtCacheByDisplay,
1053 #endif /* TEXTCOLOR */
1055 /* Register the actions mentioned in "actions". */
1056 XtAppAddActions(app_context, actions, XtNumber(actions));
1058 /* Get application-wide resources */
1059 XtGetApplicationResources(toplevel, (XtPointer) &appResources, resources,
1060 XtNumber(resources), (ArgList) 0, ZERO);
1062 /* Initialize other things. */
1063 init_standard_windows();
1065 /* Give the window manager an icon to use; toplevel must be realized. */
1066 if (appResources.icon && *appResources.icon) {
1067 struct icon_info *ip;
1069 for (ip = icon_data; ip->name; ip++)
1070 if (!strcmp(appResources.icon, ip->name)) {
1071 icon_pixmap = XCreateBitmapFromData(
1072 XtDisplay(toplevel), XtWindow(toplevel),
1073 (genericptr_t) ip->bits, ip->width, ip->height);
1074 if (icon_pixmap != None) {
1077 (void) memset((genericptr_t) &hints, 0, sizeof(XWMHints));
1078 hints.flags = IconPixmapHint;
1079 hints.icon_pixmap = icon_pixmap;
1080 XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel),
1087 /* end of setuid hack: reset uid back to the "games" uid */
1088 (void) seteuid(savuid);
1090 x_inited = TRUE; /* X is now initialized */
1092 /* Display the startup banner in the message window. */
1093 for (i = 1; i <= 4 + 2; ++i) /* (values beyond 4 yield blank lines) */
1094 X11_putstr(WIN_MESSAGE, 0, copyright_banner_line(i));
1102 X11_exit_nhwindows(dummy)
1105 extern Pixmap tile_pixmap; /* from winmap.c */
1109 /* explicitly free the icon and tile pixmaps */
1110 if (icon_pixmap != None) {
1111 XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1114 if (tile_pixmap != None) {
1115 XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1118 if (WIN_INVEN != WIN_ERR)
1119 X11_destroy_nhwindow(WIN_INVEN);
1120 if (WIN_STATUS != WIN_ERR)
1121 X11_destroy_nhwindow(WIN_STATUS);
1122 if (WIN_MAP != WIN_ERR)
1123 X11_destroy_nhwindow(WIN_MAP);
1124 if (WIN_MESSAGE != WIN_ERR)
1125 X11_destroy_nhwindow(WIN_MESSAGE);
1128 #ifdef X11_HANGUP_SIGNAL
1129 static void X11_sig(sig) /* Unix signal handler */
1132 XtNoticeSignal(X11_sig_id);
1137 X11_sig_cb(not_used, id)
1142 XClientMessageEvent *mesg;
1147 /* Set up a fake message to the event handler. */
1148 mesg = (XClientMessageEvent *) &event;
1149 mesg->type = ClientMessage;
1150 mesg->message_type = XA_STRING;
1153 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1154 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1159 /* delay_output ------------------------------------------------------------
1163 * Timeout callback for delay_output(). Send a fake message to the map
1168 d_timeout(client_data, id)
1169 XtPointer client_data;
1173 XClientMessageEvent *mesg;
1178 /* Set up a fake message to the event handler. */
1179 mesg = (XClientMessageEvent *) &event;
1180 mesg->type = ClientMessage;
1181 mesg->message_type = XA_STRING;
1183 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1184 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1189 * Delay for 50ms. This is not implemented asynch. Maybe later.
1190 * Start the timeout, then wait in the event loop. The timeout
1191 * function will send an event to the map window which will be waiting
1200 (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1202 /* The timeout function will enable the event loop exit. */
1203 (void) x_event(EXIT_ON_SENT_EVENT);
1206 /* X11_hangup --------------------------------------------------------------
1210 X11_hangup(w, event, params, num_params)
1214 Cardinal *num_params;
1221 hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1222 exit_x_event = TRUE;
1225 /* askname -----------------------------------------------------------------
1229 askname_delete(w, event, params, num_params)
1233 Cardinal *num_params;
1240 (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1241 exit_x_event = TRUE;
1244 /* Callback for askname dialog widget. */
1247 askname_done(w, client_data, call_data)
1249 XtPointer client_data;
1250 XtPointer call_data;
1254 Widget dialog = (Widget) client_data;
1259 s = (char *) GetDialogResponse(dialog);
1261 len = (int) strlen(s);
1267 /* Truncate name if necessary */
1268 if (len >= (int) sizeof(plname) - 1)
1269 len = (int) sizeof(plname) - 1;
1271 (void) strncpy(plname, s, len);
1275 nh_XtPopdown(XtParent(dialog));
1276 exit_x_event = TRUE;
1282 Widget popup, dialog;
1285 XtSetArg(args[0], XtNallowShellResize, True);
1287 popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel,
1289 XtOverrideTranslations(
1291 XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1293 dialog = CreateDialog(popup, nhStr("dialog"), askname_done,
1294 (XtCallbackProc) 0);
1296 SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */
1297 SetDialogResponse(dialog, nhStr("")); /* set default answer */
1299 XtRealizeWidget(popup);
1300 positionpopup(popup, TRUE); /* center,bottom */
1302 nh_XtPopup(popup, (int) XtGrabExclusive, dialog);
1304 /* The callback will enable the event loop exit. */
1305 (void) x_event(EXIT_ON_EXIT);
1308 /* getline -----------------------------------------------------------------
1310 /* This uses Tim Theisen's dialog widget set (from GhostView). */
1312 static Widget getline_popup, getline_dialog;
1314 #define CANCEL_STR "\033"
1315 static char *getline_input;
1317 /* Callback for getline dialog widget. */
1320 done_button(w, client_data, call_data)
1322 XtPointer client_data;
1323 XtPointer call_data;
1327 Widget dialog = (Widget) client_data;
1332 s = (char *) GetDialogResponse(dialog);
1335 /* Truncate input if necessary */
1339 (void) strncpy(getline_input, s, len);
1340 getline_input[len] = '\0';
1343 nh_XtPopdown(XtParent(dialog));
1344 exit_x_event = TRUE;
1349 getline_delete(w, event, params, num_params)
1353 Cardinal *num_params;
1359 Strcpy(getline_input, CANCEL_STR);
1361 exit_x_event = TRUE;
1364 /* Callback for getline dialog widget. */
1367 abort_button(w, client_data, call_data)
1369 XtPointer client_data;
1370 XtPointer call_data;
1372 Widget dialog = (Widget) client_data;
1377 Strcpy(getline_input, CANCEL_STR);
1378 nh_XtPopdown(XtParent(dialog));
1379 exit_x_event = TRUE;
1383 X11_getlin(question, input)
1384 const char *question;
1387 static boolean need_to_init = True;
1389 getline_input = input;
1395 need_to_init = False;
1397 XtSetArg(args[0], XtNallowShellResize, True);
1399 getline_popup = XtCreatePopupShell(
1400 "getline", transientShellWidgetClass, toplevel, args, ONE);
1401 XtOverrideTranslations(
1402 getline_popup, XtParseTranslationTable(
1403 "<Message>WM_PROTOCOLS: getline_delete()"));
1405 getline_dialog = CreateDialog(getline_popup, nhStr("dialog"),
1406 done_button, abort_button);
1408 XtRealizeWidget(getline_popup);
1409 XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1410 &wm_delete_window, 1);
1412 SetDialogPrompt(getline_dialog, (String) question); /* set prompt */
1413 SetDialogResponse(getline_dialog, nhStr("")); /* set default answer */
1414 positionpopup(getline_popup, TRUE); /* center,bottom */
1416 nh_XtPopup(getline_popup, (int) XtGrabExclusive, getline_dialog);
1418 /* The callback will enable the event loop exit. */
1419 (void) x_event(EXIT_ON_EXIT);
1422 /* Display file ------------------------------------------------------------
1424 static const char display_translations[] = "#override\n\
1425 <Key>q: dismiss_file()\n\
1426 <Key>Escape: dismiss_file()\n\
1427 <BtnDown>: dismiss_file()";
1429 /* WM_DELETE_WINDOW callback for file dismissal. */
1432 delete_file(w, event, params, num_params)
1436 Cardinal *num_params;
1446 /* Callback for file dismissal. */
1449 dismiss_file(w, event, params, num_params)
1453 Cardinal *num_params;
1455 Widget popup = XtParent(w);
1461 nh_XtPopdown(popup);
1462 XtDestroyWidget(popup);
1466 X11_display_file(str, complain)
1473 Widget popup, dispfile;
1474 Position top_margin, bottom_margin, left_margin, right_margin;
1476 int new_width, new_height;
1483 /* Use the port-independent file opener to see if the file exists. */
1484 fp = dlb_fopen(str, RDTMODE);
1488 pline("Cannot open %s. Sorry.", str);
1490 return; /* it doesn't exist, ignore */
1494 * Count the number of lines and characters in the file.
1498 while (dlb_fgets(line, LLEN, fp)) {
1500 charcount += strlen(line);
1503 (void) dlb_fclose(fp);
1505 /* Ignore empty files */
1509 /* If over the max window size, truncate the window size to the max */
1510 if (num_lines >= DISPLAY_FILE_SIZE)
1511 num_lines = DISPLAY_FILE_SIZE;
1514 * Re-open the file and read the data into a buffer. Cannot use
1515 * the XawAsciiFile type of widget, because that is not DLB-aware.
1517 textlines = (char *) alloc((unsigned int) charcount);
1518 textlines[0] = '\0';
1520 fp = dlb_fopen(str, RDTMODE);
1522 while (dlb_fgets(line, LLEN, fp)) {
1523 (void) strcat(textlines, line);
1526 (void) dlb_fclose(fp);
1529 XtSetArg(args[num_args], nhStr(XtNtitle), str);
1532 popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
1533 toplevel, args, num_args);
1534 XtOverrideTranslations(
1536 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
1539 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
1540 XawtextScrollWhenNeeded);
1542 XtSetArg(args[num_args], nhStr(XtNscrollVertical), XawtextScrollAlways);
1544 XtSetArg(args[num_args], nhStr(XtNtype), XawAsciiString);
1546 XtSetArg(args[num_args], nhStr(XtNstring), textlines);
1548 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
1550 XtSetArg(args[num_args], nhStr(XtNtranslations),
1551 XtParseTranslationTable(display_translations));
1555 XtCreateManagedWidget("text", /* name */
1556 asciiTextWidgetClass, popup, /* parent widget */
1557 args, /* set some values */
1558 num_args); /* number of values to set */
1560 /* Get font and border information. */
1562 XtSetArg(args[num_args], nhStr(XtNfont), &fs);
1564 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
1566 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
1568 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
1570 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
1572 XtGetValues(dispfile, args, num_args);
1575 * The data files are currently set up assuming an 80 char wide window
1576 * and a fixed width font. Soo..
1579 num_lines * nhFontHeight(dispfile) + top_margin + bottom_margin;
1580 new_width = 80 * fs->max_bounds.width + left_margin + right_margin;
1582 /* Set the new width and height. */
1584 XtSetArg(args[num_args], XtNwidth, new_width);
1586 XtSetArg(args[num_args], XtNheight, new_height);
1588 XtSetValues(dispfile, args, num_args);
1590 nh_XtPopup(popup, (int) XtGrabNone, (Widget) 0);
1594 /* yn_function -------------------------------------------------------------
1596 /* (not threaded) */
1598 static const char *yn_quitchars = " \n\r";
1599 static const char *yn_choices; /* string of acceptable input */
1601 static char yn_return; /* return value */
1602 static char yn_esc_map; /* ESC maps to this char. */
1603 static Widget yn_popup; /* popup for the yn fuction (created once) */
1604 static Widget yn_label; /* label for yn function (created once) */
1605 static boolean yn_getting_num; /* TRUE if accepting digits */
1606 static boolean yn_preserve_case; /* default is to force yn to lower case */
1607 static int yn_ndigits; /* digit count */
1608 static long yn_val; /* accumulated value */
1610 static const char yn_translations[] = "#override\n\
1614 * Convert the given key event into a character. If the key maps to
1615 * more than one character only the first is returned. If there is
1616 * no conversion (i.e. just the CTRL key hit) a NUL is returned.
1619 key_event_to_char(key)
1622 char keystring[MAX_KEY_STRING];
1624 boolean meta = !!(key->state & Mod1Mask);
1626 nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0,
1627 (XComposeStatus *) 0);
1629 /* Modifier keys return a zero lengh string when pressed. */
1633 return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
1637 * Called when we get a WM_DELETE_WINDOW event on a yn window.
1641 yn_delete(w, event, params, num_params)
1645 Cardinal *num_params;
1652 yn_getting_num = FALSE;
1653 /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */
1654 yn_return = yn_choices ? yn_esc_map : '\033';
1655 exit_x_event = TRUE; /* exit our event handler */
1659 * Called when we get a key press event on a yn window.
1663 yn_key(w, event, params, num_params)
1667 Cardinal *num_params;
1671 if (appResources.slow && !input_func)
1672 map_input(w, event, params, num_params);
1674 ch = key_event_to_char((XKeyEvent *) event);
1676 if (ch == '\0') { /* don't accept nul char or modifier event */
1681 if (!yn_choices) { /* accept any input */
1684 if (!yn_preserve_case)
1685 ch = lowc(ch); /* move to lower case */
1688 yn_getting_num = FALSE;
1689 yn_return = yn_esc_map;
1690 } else if (index(yn_quitchars, ch)) {
1692 } else if (index(yn_choices, ch)) {
1694 if (yn_getting_num) { /* don't select again */
1698 yn_getting_num = TRUE;
1701 return; /* wait for more input */
1705 yn_getting_num = FALSE;
1707 if (yn_getting_num) {
1710 yn_val = (yn_val * 10) + (long) (ch - '0');
1711 return; /* wait for more input */
1713 if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) {
1715 yn_val = yn_val / 10;
1716 return; /* wait for more input */
1719 X11_nhbell(); /* no match */
1723 if (yn_getting_num) {
1727 yn_number = yn_val; /* assign global */
1730 exit_x_event = TRUE; /* exit our event handler */
1734 X11_yn_function(ques, choices, def)
1736 const char *choices;
1739 static Boolean need_to_init = True;
1744 yn_choices = choices; /* set up globals for callback to use */
1747 !choices; /* preserve when arbitrary response allowed */
1750 * This is sort of a kludge. There are quite a few places in the main
1751 * nethack code where a pline containing information is followed by a
1752 * call to yn_function(). There is no flush of the message window
1753 * (it is implicit in the tty window port), so the line never shows
1754 * up for us! Solution: do our own flush.
1756 if (WIN_MESSAGE != WIN_ERR)
1757 display_message_window(&window_list[WIN_MESSAGE]);
1760 char *cb, choicebuf[QBUFSZ];
1762 Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
1763 /* default when choices are present is to force yn answer to
1764 lowercase unless one or more choices are explicitly uppercase;
1765 check this before stripping the hidden choices */
1766 for (cb = choicebuf; *cb; ++cb)
1767 if ('A' <= *cb && *cb <= 'Z') {
1768 yn_preserve_case = TRUE;
1771 if ((cb = index(choicebuf, '\033')) != 0)
1773 /* ques [choices] (def) */
1774 if ((int) (1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ)
1775 panic("X11_yn_function: question too long");
1776 (void) strncpy(buf, ques, QBUFSZ - 1);
1777 buf[QBUFSZ - 1] = '\0';
1778 Sprintf(eos(buf), " [%s]", choicebuf);
1780 Sprintf(eos(buf), " (%c)", def);
1783 /* escape maps to 'q' or 'n' or default, in that order */
1785 (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1787 if ((int) (1 + strlen(ques) + 1) >= BUFSZ)
1788 panic("X11_yn_function: question too long");
1793 if (!appResources.slow && need_to_init) {
1794 need_to_init = False;
1796 XtSetArg(args[0], XtNallowShellResize, True);
1797 yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
1798 toplevel, args, ONE);
1799 XtOverrideTranslations(
1801 XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
1804 XtSetArg(args[num_args], XtNtranslations,
1805 XtParseTranslationTable(yn_translations));
1807 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass,
1808 yn_popup, args, num_args);
1810 XtRealizeWidget(yn_popup);
1811 XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
1812 &wm_delete_window, 1);
1815 if (appResources.slow)
1816 input_func = yn_key;
1819 XtSetArg(args[num_args], XtNlabel, buf);
1821 XtSetValues(yn_label, args, num_args);
1823 if (!appResources.slow) {
1825 * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
1826 * need to set the label twice to get the size to change.
1829 XtSetArg(args[num_args], XtNlabel, buf);
1831 XtSetValues(yn_label, args, num_args);
1833 positionpopup(yn_popup, TRUE);
1834 nh_XtPopup(yn_popup, (int) XtGrabExclusive, yn_label);
1837 yn_getting_num = FALSE;
1838 (void) x_event(EXIT_ON_EXIT);
1840 if (appResources.slow) {
1843 XtSetArg(args[num_args], XtNlabel, " ");
1845 XtSetValues(yn_label, args, num_args);
1847 nh_XtPopdown(yn_popup); /* this removes the event grab */
1855 X11_preference_update(pref)
1858 if (!strcmp(pref, "tiled_map")) {
1859 if (WIN_MAP != WIN_ERR)
1860 display_map_window(&window_list[WIN_MAP]);
1864 /* End global functions ====================================================
1868 * Before we wait for input via nhgetch() and nh_poskey(), we need to
1869 * do some pre-processing.
1872 input_event(exit_condition)
1875 if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
1876 check_turn_events();
1877 if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
1878 check_cursor_visibility(&window_list[WIN_MAP]);
1879 if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
1880 set_last_pause(&window_list[WIN_MESSAGE]);
1882 return x_event(exit_condition);
1887 msgkey(w, data, event)
1897 map_input(window_list[WIN_MAP].w, event, (String *) 0, &num);
1901 static void win_visible(w, data, event, flag) /* only called for autofocus */
1903 XtPointer data; /* client_data not used */
1905 Boolean *flag; /* continue_to_dispatch flag not used */
1907 XVisibilityEvent *vis_event = (XVisibilityEvent *) event;
1912 if (vis_event->state != VisibilityFullyObscured) {
1913 /* one-time operation; cancel ourself */
1914 XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
1915 win_visible, (XtPointer) 0);
1916 /* grab initial input focus */
1917 XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
1922 * Set up the playing console. This has three major parts: the
1923 * message window, the map, and the status window.
1926 init_standard_windows()
1928 Widget form, message_viewport, map_viewport, status;
1931 Dimension message_vp_width, map_vp_width, status_width, max_width;
1932 int map_vp_hd, status_hd;
1936 XtSetArg(args[num_args], XtNallowShellResize, True);
1938 form = XtCreateManagedWidget("nethack", panedWidgetClass, toplevel, args,
1941 XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey,
1944 if (appResources.autofocus)
1945 XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible,
1949 * Create message window.
1951 WIN_MESSAGE = message_win = find_free_window();
1952 wp = &window_list[message_win];
1953 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1954 wp->popup = (Widget) 0;
1955 create_message_window(wp, FALSE, form);
1956 message_viewport = XtParent(wp->w);
1958 /* Tell the form that contains it that resizes are OK. */
1960 XtSetArg(args[num_args], nhStr(XtNresizable), True);
1962 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
1964 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
1966 XtSetValues(message_viewport, args, num_args);
1968 if (appResources.slow) {
1970 XtSetArg(args[num_args], XtNtranslations,
1971 XtParseTranslationTable(yn_translations));
1973 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form,
1976 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
1978 XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft);
1980 XtSetArg(args[num_args], nhStr(XtNresizable), True);
1982 XtSetArg(args[num_args], nhStr(XtNlabel), " ");
1984 XtSetValues(yn_label, args, num_args);
1988 * Create the map window & viewport and chain the viewport beneath the
1991 map_win = find_free_window();
1992 wp = &window_list[map_win];
1993 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1994 wp->popup = (Widget) 0;
1995 create_map_window(wp, FALSE, form);
1996 map_viewport = XtParent(wp->w);
1998 /* Chain beneath message_viewport or yn window. */
2000 if (appResources.slow) {
2001 XtSetArg(args[num_args], nhStr(XtNfromVert), yn_label);
2004 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2007 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
2009 XtSetValues(map_viewport, args, num_args);
2011 /* Create the status window, with the form as it's parent. */
2012 status_win = find_free_window();
2013 wp = &window_list[status_win];
2014 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2015 wp->popup = (Widget) 0;
2016 create_status_window(wp, FALSE, form);
2020 * Chain the status window beneath the viewport. Mark the left and right
2021 * edges so that they stay a fixed distance from the left edge of the
2022 * parent, as well as the top and bottom edges so that they stay a fixed
2023 * distance from the bottom of the parent. We do this so that the status
2024 * will never expand or contract.
2027 XtSetArg(args[num_args], nhStr(XtNfromVert), map_viewport);
2029 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
2031 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
2033 XtSetArg(args[num_args], nhStr(XtNtop), XtChainBottom);
2035 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
2037 XtSetValues(status, args, num_args);
2040 * Realize the popup so that the status widget knows it's size.
2042 * If we unset MappedWhenManaged then the DECwindow driver doesn't
2043 * attach the nethack toplevel to the highest virtual root window.
2046 /* XtSetMappedWhenManaged(toplevel, False); */
2047 XtRealizeWidget(toplevel);
2049 XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW", False);
2050 XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
2051 &wm_delete_window, 1);
2054 * Resize to at most full-screen.
2057 #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
2059 int screen_width = WidthOfScreen(XtScreen(wp->w));
2060 int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
2061 Dimension form_width, form_height;
2063 XtSetArg(args[0], XtNwidth, &form_width);
2064 XtSetArg(args[1], XtNheight, &form_height);
2065 XtGetValues(toplevel, args, TWO);
2067 if (form_width > screen_width || form_height > screen_height) {
2068 XtSetArg(args[0], XtNwidth, min(form_width, screen_width));
2069 XtSetArg(args[1], XtNheight, min(form_height, screen_height));
2070 XtSetValues(toplevel, args, TWO);
2071 XMoveWindow(XtDisplay(toplevel), XtWindow(toplevel), 0,
2074 #undef TITLEBAR_SPACE
2077 post_process_tiles(); /* after toplevel is realized */
2080 * Now get the default widths of the windows.
2082 XtSetArg(args[0], nhStr(XtNwidth), &message_vp_width);
2083 XtGetValues(message_viewport, args, ONE);
2084 XtSetArg(args[0], nhStr(XtNwidth), &map_vp_width);
2085 XtSetArg(args[1], nhStr(XtNhorizDistance), &map_vp_hd);
2086 XtGetValues(map_viewport, args, TWO);
2087 XtSetArg(args[0], nhStr(XtNwidth), &status_width);
2088 XtSetArg(args[1], nhStr(XtNhorizDistance), &status_hd);
2089 XtGetValues(status, args, TWO);
2092 * Adjust positions and sizes. The message viewport widens out to the
2093 * widest width. Both the map and status are centered by adjusting
2094 * their horizDistance.
2096 if (map_vp_width < status_width || map_vp_width < message_vp_width) {
2097 if (status_width > message_vp_width) {
2098 XtSetArg(args[0], nhStr(XtNwidth), status_width);
2099 XtSetValues(message_viewport, args, ONE);
2100 max_width = status_width;
2102 max_width = message_vp_width;
2104 XtSetArg(args[0], nhStr(XtNhorizDistance),
2105 map_vp_hd + ((int) (max_width - map_vp_width) / 2));
2106 XtSetValues(map_viewport, args, ONE);
2108 } else { /* map is widest */
2109 XtSetArg(args[0], nhStr(XtNwidth), map_vp_width);
2110 XtSetValues(message_viewport, args, ONE);
2113 * Clear all data values on the fancy status widget so that the values
2114 * used for spacing don't appear. This needs to be called some time
2115 * after the fancy status widget is realized (above, with the game popup),
2116 * but before it is popped up.
2120 * Set the map size to its standard size. As with the message window
2121 * above, the map window needs to be set to its constrained size until
2122 * its parent (the viewport widget) was realized.
2124 * Move the message window's slider to the bottom.
2126 set_map_size(&window_list[map_win], COLNO, ROWNO);
2127 set_message_slider(&window_list[message_win]);
2129 /* attempt to catch fatal X11 errors before the program quits */
2130 (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
2132 /* We can now print to the message window. */
2133 iflags.window_inited = 1;
2137 nh_XtPopup(w, g, childwid)
2138 Widget w; /* widget */
2139 int g; /* type of grab */
2140 Widget childwid; /* child to recieve focus (can be None) */
2142 XtPopup(w, (XtGrabKind) g);
2143 XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
2144 if (appResources.autofocus)
2145 XtSetKeyboardFocus(toplevel, childwid);
2153 if (appResources.autofocus)
2154 XtSetKeyboardFocus(toplevel, None);
2165 /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
2166 * two routines will not be found when linking. An apparently correct
2167 * executable is produced, along with nasty messages and a failure code
2168 * returned to make. The routines are in the static libXmu.a and
2169 * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with
2170 * static linking, we do this.
2173 /* i.e., FALSE that an optimizer probably can't find */
2174 get_wmShellWidgetClass();
2175 get_applicationShellWidgetClass();
2182 * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2186 nh_keyscroll(viewport, event, params, num_params)
2190 Cardinal *num_params;
2193 Widget horiz_sb, vert_sb;
2197 Cardinal in_nparams = (num_params ? *num_params : 0);
2201 if (in_nparams != 1)
2202 return; /* bad translation */
2204 direction = atoi(params[0]);
2206 horiz_sb = XtNameToWidget(viewport, "*horizontal");
2207 vert_sb = XtNameToWidget(viewport, "*vertical");
2209 if (!horiz_sb && !vert_sb) {
2210 /* Perhaps the widget enclosing this has scrollbars (could use while)
2212 Widget parent = XtParent(viewport);
2214 horiz_sb = XtNameToWidget(parent, "horizontal");
2215 vert_sb = XtNameToWidget(parent, "vertical");
2219 #define H_DELTA 0.25 /* distance of horiz shift */
2220 /* vert shift is half of curr distance */
2221 /* The V_DELTA is 1/2 the value of shown. */
2224 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2225 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2226 XtGetValues(horiz_sb, arg, TWO);
2230 switch (direction) {
2242 if (top + shown > 1.0)
2250 XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2255 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2256 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2257 XtGetValues(vert_sb, arg, TWO);
2261 switch (direction) {
2273 if (top + shown > 1.0)
2281 XtCallCallbacks(vert_sb, XtNjumpProc, &top);