OSDN Git Service

e17cc76000e2d9867ca5b97b49228f335a9b8c51
[jnethack/source.git] / win / X11 / winX.c
1 /* NetHack 3.6  winX.c  $NHDT-Date: 1552441031 2019/03/13 01:37:11 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.73 $ */
2 /* Copyright (c) Dean Luick, 1992                                 */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata 1994-1999                                      */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2022            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 /*
11  * "Main" file for the X window-port.  This contains most of the interface
12  * routines.  Please see doc/window.doc for an description of the window
13  * interface.
14  */
15
16 #ifndef SYSV
17 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
18 #endif
19
20 #ifdef MSDOS /* from compiler */
21 #define SHORT_FILENAMES
22 #endif
23
24 #include <X11/Intrinsic.h>
25 #include <X11/StringDefs.h>
26 #include <X11/Shell.h>
27 #include <X11/Xaw/AsciiText.h>
28 #include <X11/Xaw/Label.h>
29 #include <X11/Xaw/Form.h>
30 #include <X11/Xaw/Scrollbar.h>
31 #include <X11/Xaw/Paned.h>
32 #include <X11/Xaw/Cardinals.h>
33 #include <X11/Xatom.h>
34 #include <X11/Xos.h>
35
36 /* for color support */
37 #ifdef SHORT_FILENAMES
38 #include <X11/IntrinsP.h>
39 #else
40 #include <X11/IntrinsicP.h>
41 #endif
42
43 #ifdef PRESERVE_NO_SYSV
44 #ifdef SYSV
45 #undef SYSV
46 #endif
47 #undef PRESERVE_NO_SYSV
48 #endif
49
50 #ifdef SHORT_FILENAMES
51 #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
52 #endif
53
54 #include "hack.h"
55 #include "winX.h"
56 #include "dlb.h"
57 #include "xwindow.h"
58
59 #ifndef NO_SIGNAL
60 #include <signal.h>
61 #endif
62
63 /* Should be defined in <X11/Intrinsic.h> but you never know */
64 #ifndef XtSpecificationRelease
65 #define XtSpecificationRelease 0
66 #endif
67
68 /*
69  * Icons.
70  */
71 #include "../win/X11/nh72icon"
72 #include "../win/X11/nh56icon"
73 #include "../win/X11/nh32icon"
74
75 static struct icon_info {
76     const char *name;
77     unsigned char *bits;
78     unsigned width, height;
79 } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
80                   { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
81                   { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
82                   { (const char *) 0, (unsigned char *) 0, 0, 0 } };
83
84 /*
85  * Private global variables (shared among the window port files).
86  */
87 struct xwindow window_list[MAX_WINDOWS];
88 AppResources appResources;
89 void FDECL((*input_func), (Widget, XEvent *, String *, Cardinal *));
90 int click_x, click_y, click_button; /* Click position on a map window   */
91                                     /* (filled by set_button_values()). */
92 int updated_inventory;
93
94 static int (*old_error_handler) (Display *, XErrorEvent *);
95
96 #if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
97 #if XtSpecificationRelease >= 6
98 #define X11_HANGUP_SIGNAL
99 static XtSignalId X11_sig_id;
100 #endif
101 #endif
102
103 /* Interface definition, for windows.c */
104 struct window_procs X11_procs = {
105     "X11",
106     (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP
107      | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT),
108 #if defined(STATUS_HILITES)
109     WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS |
110 #endif
111     0L,
112     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
113     X11_init_nhwindows,
114     X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows,
115     X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow,
116     X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs,
117     X11_putstr, genl_putmixed, X11_display_file, X11_start_menu, X11_add_menu,
118     X11_end_menu, X11_select_menu,
119     genl_message_menu, /* no need for X-specific handling */
120     X11_update_inventory, X11_mark_synch, X11_wait_synch,
121 #ifdef CLIPPING
122     X11_cliparound,
123 #endif
124 #ifdef POSITIONBAR
125     donull,
126 #endif
127     X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch,
128     X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function,
129     X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output,
130 #ifdef CHANGE_COLOR /* only a Mac option currently */
131     donull, donull,
132 #endif
133     /* other defs that really should go away (they're tty specific) */
134     X11_start_screen, X11_end_screen,
135 #ifdef GRAPHIC_TOMBSTONE
136     X11_outrip,
137 #else
138     genl_outrip,
139 #endif
140     X11_preference_update, X11_getmsghistory, X11_putmsghistory,
141     X11_status_init, X11_status_finish, X11_status_enablefield,
142     X11_status_update,
143     genl_can_suspend_no, /* XXX may not always be correct */
144 };
145
146 /*
147  * Local functions.
148  */
149 static winid NDECL(find_free_window);
150 #ifdef TEXTCOLOR
151 static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer,
152                                 XrmValuePtr, Cardinal *));
153 #endif
154 static boolean FDECL(new_resource_macro, (String, unsigned));
155 static void NDECL(load_default_resources);
156 static void NDECL(release_default_resources);
157 static int FDECL(panic_on_error, (Display *, XErrorEvent *));
158 #ifdef X11_HANGUP_SIGNAL
159 static void FDECL(X11_sig, (int));
160 static void FDECL(X11_sig_cb, (XtPointer, XtSignalId *));
161 #endif
162 static void FDECL(d_timeout, (XtPointer, XtIntervalId *));
163 static void FDECL(X11_hangup, (Widget, XEvent *, String *, Cardinal *));
164 static void FDECL(askname_delete, (Widget, XEvent *, String *, Cardinal *));
165 static void FDECL(askname_done, (Widget, XtPointer, XtPointer));
166 static void FDECL(done_button, (Widget, XtPointer, XtPointer));
167 static void FDECL(getline_delete, (Widget, XEvent *, String *, Cardinal *));
168 static void FDECL(abort_button, (Widget, XtPointer, XtPointer));
169 static void NDECL(release_getline_widgets);
170 static void FDECL(yn_delete, (Widget, XEvent *, String *, Cardinal *));
171 static void FDECL(yn_key, (Widget, XEvent *, String *, Cardinal *));
172 static void NDECL(release_yn_widgets);
173 static int FDECL(input_event, (int));
174 static void FDECL(win_visible, (Widget, XtPointer, XEvent *, Boolean *));
175 static void NDECL(init_standard_windows);
176
177 #ifdef  INSTALLCOLORMAP
178 Colormap     cmap;
179 #endif
180
181 /*
182  * Local variables.
183  */
184 static boolean x_inited = FALSE;    /* TRUE if window system is set up.     */
185 static winid message_win = WIN_ERR, /* These are the winids of the message, */
186              map_win = WIN_ERR,     /*   map, and status windows, when they */
187              status_win = WIN_ERR;  /*   are created in init_windows().     */
188 static Pixmap icon_pixmap = None;   /* Pixmap for icon.                     */
189
190 void
191 X11_putmsghistory(msg, is_restoring)
192 const char *msg;
193 boolean is_restoring;
194 {
195     if (WIN_MESSAGE != WIN_ERR) {
196         struct xwindow *wp = &window_list[WIN_MESSAGE];
197         debugpline2("X11_putmsghistory('%s',%i)", msg, is_restoring);
198         if (msg)
199             append_message(wp, msg);
200     }
201 }
202
203 char *
204 X11_getmsghistory(init)
205 boolean init;
206 {
207     if (WIN_MESSAGE != WIN_ERR) {
208         static struct line_element *curr = (struct line_element *) 0;
209         static int numlines = 0;
210         struct xwindow *wp = &window_list[WIN_MESSAGE];
211
212         if (init)
213             curr = (struct line_element *) 0;
214
215         if (!curr) {
216             curr = wp->mesg_information->head;
217             numlines = 0;
218         }
219
220         if (numlines < wp->mesg_information->num_lines) {
221             curr = curr->next;
222             numlines++;
223             debugpline2("X11_getmsghistory(%i)='%s'", init, curr->line);
224             return curr->line;
225         }
226     }
227     return (char *) 0;
228 }
229
230 /*
231  * Find the window structure that corresponds to the given widget.  Note
232  * that this is not the popup widget, nor the viewport, but the child.
233  */
234 struct xwindow *
235 find_widget(w)
236 Widget w;
237 {
238     int windex;
239     struct xwindow *wp;
240
241     /*
242      * Search to find the corresponding window.  Look at the main widget,
243      * popup, the parent of the main widget, then parent of the widget.
244      */
245     for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
246         if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w
247                                      || (wp->w && (XtParent(wp->w)) == w)
248                                      || (wp->popup == XtParent(w))))
249             break;
250
251     if (windex == MAX_WINDOWS)
252         panic("find_widget:  can't match widget");
253     return wp;
254 }
255
256 /*
257  * Find a free window slot for use.
258  */
259 static winid
260 find_free_window()
261 {
262     int windex;
263     struct xwindow *wp;
264
265     for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS;
266          windex++, wp++)
267         if (wp->type == NHW_NONE)
268             break;
269
270     if (windex == MAX_WINDOWS)
271         panic("find_free_window: no free windows!");
272     return (winid) windex;
273 }
274
275
276 XColor
277 get_nhcolor(wp, clr)
278 struct xwindow *wp;
279 int clr;
280 {
281     init_menu_nhcolors(wp);
282     /* FIXME: init_menu_nhcolors may fail */
283
284     if (clr >= 0 && clr < CLR_MAX)
285         return wp->nh_colors[clr];
286
287     return wp->nh_colors[0];
288 }
289
290 void
291 init_menu_nhcolors(wp)
292 struct xwindow *wp;
293 {
294     static const char *mapCLR_to_res[CLR_MAX] = {
295         XtNblack,
296         XtNred,
297         XtNgreen,
298         XtNbrown,
299         XtNblue,
300         XtNmagenta,
301         XtNcyan,
302         XtNgray,
303         XtNforeground,
304         XtNorange,
305         XtNbright_green,
306         XtNyellow,
307         XtNbright_blue,
308         XtNbright_magenta,
309         XtNbright_cyan,
310         XtNwhite,
311     };
312     Display *dpy;
313     Colormap screen_colormap;
314     XrmDatabase rDB;
315     XrmValue value;
316     Status rc;
317     int color;
318     char *ret_type[32];
319     char clr_name[BUFSZ];
320     char clrclass[BUFSZ];
321     const char *wintypenames[NHW_TEXT] = {
322         "message", "status", "map", "menu", "text"
323     };
324     const char *wtn;
325     char wtn_up[BUFSZ];
326
327     if (wp->nh_colors_inited || !wp->type)
328         return;
329
330     wtn = wintypenames[wp->type - 1];
331     Strcpy(wtn_up, wtn);
332     (void) upstart(wtn_up);
333
334     dpy = XtDisplay(wp->w);
335     screen_colormap = DefaultColormap(dpy, DefaultScreen(dpy));
336     rDB = XrmGetDatabase(dpy);
337
338     for (color = 0; color < CLR_MAX; color++) {
339 /*JP
340         Sprintf(clr_name, "nethack.%s.%s", wtn, mapCLR_to_res[color]);
341 */
342         Sprintf(clr_name, "jnethack.%s.%s", wtn, mapCLR_to_res[color]);
343 /*JP
344         Sprintf(clrclass, "NetHack.%s.%s", wtn_up, mapCLR_to_res[color]);
345 */
346         Sprintf(clrclass, "JNetHack.%s.%s", wtn_up, mapCLR_to_res[color]);
347
348         if (!XrmGetResource(rDB, clr_name, clrclass, ret_type, &value)) {
349 /*JP
350             Sprintf(clr_name, "nethack.map.%s", mapCLR_to_res[color]);
351 */
352             Sprintf(clr_name, "jnethack.map.%s", mapCLR_to_res[color]);
353 /*JP
354             Sprintf(clrclass, "NetHack.Map.%s", mapCLR_to_res[color]);
355 */
356             Sprintf(clrclass, "JNetHack.Map.%s", mapCLR_to_res[color]);
357         }
358
359         if (!XrmGetResource(rDB, clr_name, clrclass, ret_type, &value)) {
360             impossible("XrmGetResource error (%s)", clr_name);
361         } else if (!strcmp(ret_type[0], "String")) {
362             char tmpbuf[256];
363
364             if (value.size >= sizeof tmpbuf)
365                 value.size = sizeof tmpbuf - 1;
366             (void) strncpy(tmpbuf, (char *) value.addr, (int) value.size);
367             tmpbuf[value.size] = '\0';
368             /* tmpbuf now contains the color name from the named resource */
369
370             rc = XAllocNamedColor(dpy, screen_colormap, tmpbuf,
371                                   &wp->nh_colors[color],
372                                   &wp->nh_colors[color]);
373             if (rc == 0) {
374                 impossible("XAllocNamedColor failed for color %i (%s)",
375                            color, clr_name);
376             }
377         }
378     }
379
380     wp->nh_colors_inited = TRUE;
381 }
382
383 /*
384  * Color conversion.  The default X11 color converters don't try very
385  * hard to find matching colors in PseudoColor visuals.  If they can't
386  * allocate the exact color, they puke and give you something stupid.
387  * This is an attempt to find some close readonly cell and use it.
388  */
389 XtConvertArgRec const nhcolorConvertArgs[] = {
390     { XtWidgetBaseOffset,
391       (XtPointer) (ptrdiff_t) XtOffset(Widget, core.screen),
392       sizeof (Screen *) },
393     { XtWidgetBaseOffset,
394       (XtPointer) (ptrdiff_t) XtOffset(Widget, core.colormap),
395       sizeof (Colormap) }
396 };
397
398 #define done(type, value)                             \
399     {                                                 \
400         if (toVal->addr != 0) {                       \
401             if (toVal->size < sizeof(type)) {         \
402                 toVal->size = sizeof(type);           \
403                 return False;                         \
404             }                                         \
405             *(type *)(toVal->addr) = (value);         \
406         } else {                                      \
407             static type static_val;                   \
408             static_val = (value);                     \
409             toVal->addr = (genericptr_t) &static_val; \
410         }                                             \
411         toVal->size = sizeof(type);                   \
412         return True;                                  \
413     }
414
415 /*
416  * Find a color that approximates the color named in "str".
417  * The "str" color may be a color name ("red") or number ("#7f0000").
418  * If str is Null, then "color" is assumed to contain the RGB color wanted.
419  * The approximate color found is returned in color as well.
420  * Return True if something close was found.
421  */
422 Boolean
423 nhApproxColor(screen, colormap, str, color)
424 Screen *screen;    /* screen to use */
425 Colormap colormap; /* the colormap to use */
426 char *str;         /* color name */
427 XColor *color;     /* the X color structure; changed only if successful */
428 {
429     int ncells;
430     long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
431     XColor tmp;
432     static XColor *table = 0;
433     register int i, j;
434     register long tdiff;
435
436     /* if the screen doesn't have a big colormap, don't waste our time
437        or if it's huge, and _some_ match should have been possible */
438     if ((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
439         return False;
440
441     if (str != (char *) 0) {
442         if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
443             return False;
444     } else {
445         tmp = *color;
446         tmp.flags = 7; /* force to use all 3 of RGB */
447     }
448
449     if (!table) {
450         table = (XColor *) XtCalloc(ncells, sizeof(XColor));
451         for (i = 0; i < ncells; i++)
452             table[i].pixel = i;
453         XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
454     }
455
456 /* go thru cells and look for the one with smallest diff        */
457 /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
458 /* a more knowledgeable color person might improve this -dlc    */
459 try_again:
460     for (i = 0; i < ncells; i++) {
461         if (table[i].flags == tmp.flags) {
462             j = (int) table[i].red - (int) tmp.red;
463             if (j < 0)
464                 j = -j;
465             tdiff = j;
466             j = (int) table[i].green - (int) tmp.green;
467             if (j < 0)
468                 j = -j;
469             tdiff += j;
470             j = (int) table[i].blue - (int) tmp.blue;
471             if (j < 0)
472                 j = -j;
473             tdiff += j;
474             if (tdiff < cdiff) {
475                 cdiff = tdiff;
476                 tmp.pixel = i; /* table[i].pixel == i */
477             }
478         }
479     }
480
481     if (cdiff == 16777216)
482         return False; /* nothing found?! */
483
484     /*
485      * Found something.  Return it and mark this color as used to avoid
486      * reuse.  Reuse causes major contrast problems :-)
487      */
488     *color = table[tmp.pixel];
489     table[tmp.pixel].flags = 0;
490     /* try to alloc the color, so no one else can change it */
491     if (!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
492         cdiff = 16777216;
493         goto try_again;
494     }
495     return True;
496 }
497
498 #ifdef INSTALLCOLORMAP
499 Boolean
500 nhCvtStringToColormap(dpy, args, num_args, fromVal, toVal, data)
501 Display*        dpy;
502 XrmValuePtr     args;
503 Cardinal        *num_args;
504 XrmValuePtr     fromVal;
505 XrmValuePtr     toVal;
506 XtPointer       data;
507 {
508   XColor        black, white;
509   int screen = DefaultScreen(dpy);
510   Colormap dcmap = DefaultColormap(dpy, screen);
511   
512   if(strncmp(fromVal->addr, "install", 7)){
513     return False;
514   }
515   if(!cmap){
516     cmap = XCreateColormap(dpy,
517                            RootWindow(dpy, screen),
518                            DefaultVisual(dpy, screen),
519                            AllocNone);
520     /*
521       \8eè\94²\82«
522      */
523     black.red = 0;
524     black.green = 0;
525     black.blue = 0;
526
527     white.red = 0xffff;
528     white.green = 0xffff;
529     white.blue = 0xffff;
530
531     XAllocColor(dpy, dcmap, &black);
532     XAllocColor(dpy, dcmap, &white);
533
534     XAllocColor(dpy, cmap, &black);
535     XAllocColor(dpy, cmap, &white);
536   }
537   
538   done(Colormap, cmap);
539 }
540 #endif /*INSTALLCOLORMAP*/
541
542 Boolean
543 nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
544 Display *dpy;
545 XrmValuePtr args;
546 Cardinal *num_args;
547 XrmValuePtr fromVal;
548 XrmValuePtr toVal;
549 XtPointer *closure_ret;
550 {
551     String str = (String) fromVal->addr;
552     XColor screenColor;
553     XColor exactColor;
554     Screen *screen;
555     XtAppContext app = XtDisplayToApplicationContext(dpy);
556     Colormap colormap;
557     Status status;
558     String params[1];
559     Cardinal num_params = 1;
560
561     if (*num_args != 2) {
562         XtAppWarningMsg(app, "wrongParameters",
563                         "cvtStringToPixel", "XtToolkitError",
564              "String to pixel conversion needs screen and colormap arguments",
565                         (String *) 0, (Cardinal *) 0);
566         return False;
567     }
568
569     screen = *((Screen **) args[0].addr);
570     colormap = *((Colormap *) args[1].addr);
571
572 /* If Xt colors, use the Xt routine and hope for the best */
573 #if (XtSpecificationRelease >= 5)
574     if ((strcmpi(str, XtDefaultBackground) == 0)
575         || (strcmpi(str, XtDefaultForeground) == 0)) {
576         return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal,
577                                   closure_ret);
578     }
579 #else
580     if (strcmpi(str, XtDefaultBackground) == 0) {
581         *closure_ret = (char *) False;
582         done(Pixel, WhitePixelOfScreen(screen));
583     }
584     if (strcmpi(str, XtDefaultForeground) == 0) {
585         *closure_ret = (char *) False;
586         done(Pixel, BlackPixelOfScreen(screen));
587     }
588 #endif
589
590     status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char *) str,
591                               &screenColor, &exactColor);
592     if (status == 0) {
593         String msg, type;
594
595         /* some versions of XAllocNamedColor don't allow #xxyyzz names */
596         if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap,
597                                          str, &exactColor)
598             && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
599             *closure_ret = (char *) True;
600             done(Pixel, exactColor.pixel);
601         }
602
603         params[0] = str;
604         /* Server returns a specific error code but Xlib discards it.  Ugh */
605         if (XLookupColor(DisplayOfScreen(screen), colormap, (char *) str,
606                          &exactColor, &screenColor)) {
607             /* try to find another color that will do */
608             if (nhApproxColor(screen, colormap, (char *) str, &screenColor)) {
609                 *closure_ret = (char *) True;
610                 done(Pixel, screenColor.pixel);
611             }
612             type = nhStr("noColormap");
613             msg = nhStr("Cannot allocate colormap entry for \"%s\"");
614         } else {
615             /* some versions of XLookupColor also don't allow #xxyyzz names */
616             if (str[0] == '#'
617                 && (nhApproxColor(screen, colormap, (char *) str,
618                                   &screenColor))) {
619                 *closure_ret = (char *) True;
620                 done(Pixel, screenColor.pixel);
621             }
622             type = nhStr("badValue");
623             msg = nhStr("Color name \"%s\" is not defined");
624         }
625
626         XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg,
627                         params, &num_params);
628         *closure_ret = False;
629         return False;
630     } else {
631         *closure_ret = (char *) True;
632         done(Pixel, screenColor.pixel);
633     }
634 }
635
636 /* Ask the WM for window frame size */
637 void
638 get_window_frame_extents(w, top, bottom, left, right)
639 Widget w;
640 long *top, *bottom, *left, *right;
641 {
642     XEvent event;
643     Display *dpy = XtDisplay(w);
644     Window win = XtWindow(w);
645     Atom prop, retprop;
646     int retfmt;
647     unsigned long nitems;
648     unsigned long nbytes;
649     unsigned char *data = 0;
650     long *extents;
651
652     prop = XInternAtom(dpy, "_NET_FRAME_EXTENTS", True);
653
654     while (XGetWindowProperty(dpy, win, prop,
655                               0, 4, False, AnyPropertyType,
656                               &retprop, &retfmt,
657                               &nitems, &nbytes, &data) != Success
658            || nitems != 4 || nbytes != 0)
659         {
660             XNextEvent(dpy, &event);
661         }
662
663     extents = (long *) data;
664
665     *left = extents[0];
666     *right = extents[1];
667     *top = extents[2];
668     *bottom = extents[3];
669 }
670
671 void
672 get_widget_window_geometry(w, x,y, width, height)
673 Widget w;
674 int *x, *y, *width, *height;
675 {
676     long top, bottom, left, right;
677     Arg args[5];
678     XtSetArg(args[0], nhStr(XtNx), x);
679     XtSetArg(args[1], nhStr(XtNy), y);
680     XtSetArg(args[2], nhStr(XtNwidth), width);
681     XtSetArg(args[3], nhStr(XtNheight), height);
682     XtGetValues(w, args, 4);
683     get_window_frame_extents(w, &top, &bottom, &left, &right);
684     *x -= left;
685     *y -= top;
686 }
687
688 /* Change the full font name string so the weight is "bold" */
689 char *
690 fontname_boldify(fontname)
691 const char *fontname;
692 {
693     static char buf[BUFSZ];
694     char *bufp = buf;
695     int idx = 0;
696
697     while (*fontname) {
698         if (*fontname == '-')
699             idx++;
700         *bufp = *fontname;
701         if (idx == 3) {
702             strcat(buf, "bold");
703             bufp += 5;
704             do {
705                 fontname++;
706             } while (*fontname && *fontname != '-');
707         } else {
708             bufp++;
709             fontname++;
710         }
711     }
712     *bufp = '\0';
713     return buf;
714 }
715
716 void
717 load_boldfont(wp, w)
718 struct xwindow *wp;
719 Widget w;
720 {
721     Arg args[1];
722     XFontStruct *fs;
723     unsigned long ret;
724     char *fontname;
725     Display *dpy;
726
727     if (wp->boldfs)
728         return;
729
730     XtSetArg(args[0], nhStr(XtNfont), &fs);
731     XtGetValues(w, args, 1);
732
733     if (!XGetFontProperty(fs, XA_FONT, &ret))
734         return;
735
736     wp->boldfs_dpy = dpy = XtDisplay(w);
737     fontname = fontname_boldify(XGetAtomName(dpy, (Atom)ret));
738     wp->boldfs = XLoadQueryFont(dpy, fontname);
739 }
740
741 #ifdef TEXTCOLOR
742 /* ARGSUSED */
743 static void
744 nhFreePixel(app, toVal, closure, args, num_args)
745 XtAppContext app;
746 XrmValuePtr toVal;
747 XtPointer closure;
748 XrmValuePtr args;
749 Cardinal *num_args;
750 {
751     Screen *screen;
752     Colormap colormap;
753
754     if (*num_args != 2) {
755         XtAppWarningMsg(app, "wrongParameters", "freePixel", "XtToolkitError",
756                      "Freeing a pixel requires screen and colormap arguments",
757                         (String *) 0, (Cardinal *) 0);
758         return;
759     }
760
761     screen = *((Screen **) args[0].addr);
762     colormap = *((Colormap *) args[1].addr);
763
764     if (closure) {
765         XFreeColors(DisplayOfScreen(screen), colormap,
766                     (unsigned long *) toVal->addr, 1, (unsigned long) 0);
767     }
768 }
769 #endif /*TEXTCOLOR*/
770
771 /* [ALI] Utility function to ask Xaw for font height, since the previous
772  * assumption of ascent + descent is not always valid.
773  */
774 Dimension
775 nhFontHeight(w)
776 Widget w;
777 {
778 #ifdef _XawTextSink_h
779     Widget sink;
780     XawTextPosition pos = 0;
781     int resWidth, resHeight;
782     Arg args[1];
783
784     XtSetArg(args[0], XtNtextSink, &sink);
785     XtGetValues(w, args, 1);
786
787     XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
788     return resHeight;
789 #else
790     XFontStruct *fs;
791     Arg args[1];
792
793     XtSetArg(args[0], XtNfont, &fs);
794     XtGetValues(w, args, 1);
795
796     /* Assume font height is ascent + descent. */
797     return = fs->ascent + fs->descent;
798 #endif
799 }
800
801 static String *default_resource_data = 0, /* NULL-terminated arrays */
802               *def_rsrc_macr = 0, /* macro names */
803               *def_rsrc_valu = 0; /* macro values */
804
805 /* caller found "#define"; parse into macro name and its expansion value */
806 static boolean
807 new_resource_macro(inbuf, numdefs)
808 String inbuf; /* points past '#define' rather than to start of buffer */
809 unsigned numdefs; /* array slot to fill */
810 {
811     String p, q;
812
813     /* we expect inbuf to be terminated by newline; get rid of it */
814     q = eos(inbuf);
815     if (q > inbuf && q[-1] == '\n')
816         q[-1] = '\0';
817
818     /* figure out macro's name */
819     for (p = inbuf; *p == ' ' || *p == '\t'; ++p)
820         continue; /* skip whitespace */
821     for (q = p; *q && *q != ' ' && *q != '\t'; ++q)
822         continue; /* token consists of non-whitespace */
823     Strcat(q, " "); /* guarantee something beyond '#define FOO' */
824     *q++ = '\0'; /* p..(q-1) contains macro name */
825     if (!*p) /* invalid definition: '#define' followed by nothing */
826         return FALSE;
827     def_rsrc_macr[numdefs] = dupstr(p);
828
829     /* figure out macro's value; empty value is supported but not expected */
830     while (*q == ' ' || *q == '\t')
831         ++q; /* skip whitespace between name and value */
832     for (p = eos(q); --p > q && (*p == ' ' || *p == '\t'); )
833         continue; /* discard trailing whitespace */
834     *++p = '\0'; /* q..p containes macro value */
835     def_rsrc_valu[numdefs] = dupstr(q);
836     return TRUE;
837 }
838
839 /* read the template NetHack.ad into default_resource_data[] to supply
840    fallback resources to XtAppInitialize() */
841 static void
842 load_default_resources()
843 {
844     FILE *fp;
845     String inbuf;
846     unsigned insiz, linelen, longlen, numlines, numdefs, midx;
847     boolean comment, isdef;
848
849     /*
850      * Running nethack via the shell script adds $HACKDIR to the path used
851      * by X to find resources, but running it directly doesn't.  So, if we
852      * can find the template file for NetHack.ad in the current directory,
853      * load its contents into memory so that the application startup call
854      * in X11_init_nhwindows() can use them as fallback resources.
855      *
856      * No attempt to support the 'include' directive has been made, nor
857      * backslash+newline continuation lines.  Macro expansion (at most
858      * one substitution per line) is supported.  '#define' to introduce
859      * a macro must be at start of line (no whitespace before or after
860      * the '#' character).
861      */
862     fp = fopen("./NetHack.ad", "r");
863     if (!fp)
864         return;
865
866     /* measure the file without retaining its contents */
867     insiz = BUFSIZ; /* stdio BUFSIZ, not nethack BUFSZ */
868     inbuf = (String) alloc(insiz);
869     linelen = longlen = 0;
870     numlines = numdefs = 0;
871     comment = isdef = FALSE; /* lint suppression */
872     while (fgets(inbuf, insiz, fp)) {
873         if (!linelen) {
874             /* !linelen: inbuf has start of record; treat empty as comment */
875             comment = (*inbuf == '!' || *inbuf == '\n');
876             isdef = !strncmp(inbuf, "#define", 7);
877             ++numdefs;
878         }
879         linelen += strlen(inbuf);
880         if (!index(inbuf, '\n'))
881             continue;
882         if (linelen > longlen)
883             longlen = linelen;
884         linelen = 0;
885         if (!comment && !isdef)
886             ++numlines;
887     }
888     insiz = longlen + 1;
889     if (numdefs) { /* don't alloc if 0; no need for any terminator */
890         def_rsrc_macr = (String *) alloc(numdefs * sizeof (String));
891         def_rsrc_valu = (String *) alloc(numdefs * sizeof (String));
892         insiz += BUFSIZ; /* include room for macro expansion within buffer */
893     }
894     if (insiz > BUFSIZ) {
895         free((genericptr_t) inbuf);
896         inbuf = (String) alloc(insiz);
897     }
898     ++numlines; /* room for terminator */
899     default_resource_data = (String *) alloc(numlines * sizeof (String));
900
901     /* now re-read the file, storing its contents into the allocated array
902        after performing macro substitutions */
903     (void) rewind(fp);
904     numlines = numdefs = 0;
905     while (fgets(inbuf, insiz, fp)) {
906         if (!strncmp(inbuf, "#define", 7)) {
907             if (new_resource_macro(&inbuf[7], numdefs))
908                 ++numdefs;
909         } else if (*inbuf != '!' && *inbuf != '\n') {
910             if (numdefs) {
911                 /*
912                  * Macro expansion:  we assume at most one substitution
913                  * per line.  That's all that our sample NetHack.ad uses.
914                  *
915                  * If we ever need more, this will have to become a lot
916                  * more sophisticated.  It will need to find the first
917                  * instance within inbuf[] rather than first macro which
918                  * appears, and to avoid finding names within substituted
919                  * expansion values.
920                  *
921                  * Any substitution which would exceed the buffer size is
922                  * skipped.  A sophisticated implementation would need to
923                  * be prepared to allocate a bigger buffer when needed.
924                  */
925                 linelen = strlen(inbuf);
926                 for (midx = 0; midx < numdefs; ++midx) {
927                     if ((linelen + strlen(def_rsrc_valu[midx])
928                          < insiz - strlen(def_rsrc_macr[midx]))
929                         && strNsubst(inbuf, def_rsrc_macr[midx],
930                                      def_rsrc_valu[midx], 1))
931                         break;
932                 }
933             }
934             default_resource_data[numlines++] = dupstr(inbuf);
935         }
936     }
937     default_resource_data[numlines] = (String) 0;
938     (void) fclose(fp);
939     free((genericptr_t) inbuf);
940     if (def_rsrc_macr) { /* implies def_rsrc_valu is non-Null too */
941         for (midx = 0; midx < numdefs; ++midx) {
942             free((genericptr_t) def_rsrc_macr[midx]);
943             free((genericptr_t) def_rsrc_valu[midx]);
944         }
945         free((genericptr_t) def_rsrc_macr), def_rsrc_macr = 0;
946         free((genericptr_t) def_rsrc_valu), def_rsrc_valu = 0;
947     }
948 }
949
950 static void
951 release_default_resources()
952 {
953     if (default_resource_data) {
954         unsigned idx;
955
956         for (idx = 0; default_resource_data[idx]; idx++)
957             free((genericptr_t) default_resource_data[idx]);
958         free((genericptr_t) default_resource_data), default_resource_data = 0;
959     }
960     /* def_rsrc_macr[] and def_rsrc_valu[] have already been released */
961 }
962
963 /* Global Functions ======================================================= */
964 void
965 X11_raw_print(str)
966 const char *str;
967 {
968 /*JP
969     (void) puts(str);
970 */
971     (void) jputs(str);
972 }
973
974 void
975 X11_raw_print_bold(str)
976 const char *str;
977 {
978 /*JP
979     (void) puts(str);
980 */
981     (void) jputs(str);
982 }
983
984 void
985 X11_curs(window, x, y)
986 winid window;
987 int x, y;
988 {
989     check_winid(window);
990
991     if (x < 0 || x >= COLNO) {
992         impossible("curs:  bad x value [%d]", x);
993         x = 0;
994     }
995     if (y < 0 || y >= ROWNO) {
996         impossible("curs:  bad y value [%d]", y);
997         y = 0;
998     }
999
1000     window_list[window].cursx = x;
1001     window_list[window].cursy = y;
1002 }
1003
1004 void
1005 X11_putstr(window, attr, str)
1006 winid window;
1007 int attr;
1008 const char *str;
1009 {
1010     winid new_win;
1011     struct xwindow *wp;
1012
1013     check_winid(window);
1014     wp = &window_list[window];
1015
1016     switch (wp->type) {
1017     case NHW_MESSAGE:
1018         (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
1019         toplines[TBUFSZ - 1] = 0;
1020         append_message(wp, str);
1021         break;
1022 #ifndef STATUS_HILITES
1023     case NHW_STATUS:
1024         adjust_status(wp, str);
1025         break;
1026 #endif
1027     case NHW_MAP:
1028         impossible("putstr: called on map window \"%s\"", str);
1029         break;
1030     case NHW_MENU:
1031         if (wp->menu_information->is_menu) {
1032             impossible("putstr:  called on a menu window, \"%s\" discarded",
1033                        str);
1034             break;
1035         }
1036         /*
1037          * Change this menu window into a text window by creating a
1038          * new text window, then copying it to this winid.
1039          */
1040         new_win = X11_create_nhwindow(NHW_TEXT);
1041         X11_destroy_nhwindow(window);
1042         *wp = window_list[new_win];
1043         window_list[new_win].type = NHW_NONE; /* allow re-use */
1044     /* fall though to add text */
1045     case NHW_TEXT:
1046         add_to_text_window(wp, attr, str);
1047         break;
1048     default:
1049         impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str);
1050     }
1051 }
1052
1053 /* We do event processing as a callback, so this is a null routine. */
1054 void
1055 X11_get_nh_event()
1056 {
1057     return;
1058 }
1059
1060 int
1061 X11_nhgetch()
1062 {
1063     return input_event(EXIT_ON_KEY_PRESS);
1064 }
1065
1066 int
1067 X11_nh_poskey(x, y, mod)
1068 int *x, *y, *mod;
1069 {
1070     int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
1071
1072     if (val == 0) { /* user clicked on a map window */
1073         *x = click_x;
1074         *y = click_y;
1075         *mod = click_button;
1076     }
1077     return val;
1078 }
1079
1080 winid
1081 X11_create_nhwindow(type)
1082 int type;
1083 {
1084     winid window;
1085     struct xwindow *wp;
1086
1087     if (!x_inited)
1088         panic("create_nhwindow:  windows not initialized");
1089
1090 #ifdef X11_HANGUP_SIGNAL
1091     /* set up our own signal handlers on the first call.  Can't do this in
1092      * X11_init_nhwindows because unixmain sets its handler after calling
1093      * all the init routines. */
1094     if (X11_sig_id == 0) {
1095         X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer) 0);
1096 #ifdef SA_RESTART
1097         {
1098             struct sigaction sact;
1099
1100             (void) memset((char *) &sact, 0, sizeof(struct sigaction));
1101             sact.sa_handler = (SIG_RET_TYPE) X11_sig;
1102             (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
1103 #ifdef SIGXCPU
1104             (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
1105 #endif
1106         }
1107 #else
1108         (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
1109 #ifdef SIGXCPU
1110         (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
1111 #endif
1112 #endif
1113     }
1114 #endif
1115
1116     /*
1117      * We have already created the standard message, map, and status
1118      * windows in the window init routine.  The first window of that
1119      * type to be created becomes the standard.
1120      *
1121      * A better way to do this would be to say that init_nhwindows()
1122      * has already defined these three windows.
1123      */
1124     if (type == NHW_MAP && map_win != WIN_ERR) {
1125         window = map_win;
1126         map_win = WIN_ERR;
1127         return window;
1128     }
1129     if (type == NHW_MESSAGE && message_win != WIN_ERR) {
1130         window = message_win;
1131         message_win = WIN_ERR;
1132         return window;
1133     }
1134     if (type == NHW_STATUS && status_win != WIN_ERR) {
1135         window = status_win;
1136         status_win = WIN_ERR;
1137         return window;
1138     }
1139
1140     window = find_free_window();
1141     wp = &window_list[window];
1142
1143     /* The create routines will set type, popup, w, and Win_info. */
1144     wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width =
1145         wp->pixel_height = 0;
1146     wp->keep_window = FALSE;
1147     wp->nh_colors_inited = FALSE;
1148     wp->boldfs = (XFontStruct *) 0;
1149     wp->boldfs_dpy = (Display *) 0;
1150     wp->title = (char *) 0;
1151
1152     switch (type) {
1153     case NHW_MAP:
1154         create_map_window(wp, TRUE, (Widget) 0);
1155         break;
1156     case NHW_MESSAGE:
1157         create_message_window(wp, TRUE, (Widget) 0);
1158         break;
1159     case NHW_STATUS:
1160         create_status_window(wp, TRUE, (Widget) 0);
1161         break;
1162     case NHW_MENU:
1163         create_menu_window(wp);
1164         break;
1165     case NHW_TEXT:
1166         create_text_window(wp);
1167         break;
1168     default:
1169         panic("create_nhwindow: unknown type [%d]", type);
1170         break;
1171     }
1172     return window;
1173 }
1174
1175 void
1176 X11_clear_nhwindow(window)
1177 winid window;
1178 {
1179     struct xwindow *wp;
1180
1181     check_winid(window);
1182     wp = &window_list[window];
1183
1184     switch (wp->type) {
1185     case NHW_MAP:
1186         clear_map_window(wp);
1187         break;
1188     case NHW_TEXT:
1189         clear_text_window(wp);
1190         break;
1191     case NHW_STATUS:
1192     case NHW_MENU:
1193     case NHW_MESSAGE:
1194         /* do nothing for these window types */
1195         break;
1196     default:
1197         panic("clear_nhwindow: unknown window type [%d]", wp->type);
1198         break;
1199     }
1200 }
1201
1202 void
1203 X11_display_nhwindow(window, blocking)
1204 winid window;
1205 boolean blocking;
1206 {
1207     struct xwindow *wp;
1208
1209     check_winid(window);
1210     wp = &window_list[window];
1211
1212     switch (wp->type) {
1213     case NHW_MAP:
1214         if (wp->popup)
1215             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1216         display_map_window(wp); /* flush map */
1217
1218         /*
1219          * We need to flush the message window here due to the way the tty
1220          * port is set up.  To flush a window, you need to call this
1221          * routine.  However, the tty port _pauses_ with a --more-- if we
1222          * do a display_nhwindow(WIN_MESSAGE, FALSE).  Thus, we can't call
1223          * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
1224          * get a --more-- after every line.
1225          *
1226          * Perhaps the window document should mention that when the map
1227          * is flushed, everything on the three main windows should be
1228          * flushed.  Note: we don't need to flush the status window
1229          * because we don't buffer changes.
1230          */
1231         if (WIN_MESSAGE != WIN_ERR)
1232             display_message_window(&window_list[WIN_MESSAGE]);
1233         if (blocking)
1234             (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
1235         break;
1236     case NHW_MESSAGE:
1237         if (wp->popup)
1238             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1239         display_message_window(wp); /* flush messages */
1240         break;
1241     case NHW_STATUS:
1242         if (wp->popup)
1243             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1244         break; /* no flushing necessary */
1245     case NHW_MENU: {
1246         int n;
1247         menu_item *selected;
1248
1249         /* pop up menu */
1250         n = X11_select_menu(window, PICK_NONE, &selected);
1251         if (n) {
1252             impossible("perminvent: %d selected??", n);
1253             free((genericptr_t) selected);
1254         }
1255         break;
1256     }
1257     case NHW_TEXT:
1258         display_text_window(wp, blocking); /* pop up text window */
1259         break;
1260     default:
1261         panic("display_nhwindow: unknown window type [%d]", wp->type);
1262         break;
1263     }
1264 }
1265
1266 void
1267 X11_destroy_nhwindow(window)
1268 winid window;
1269 {
1270     struct xwindow *wp;
1271
1272     check_winid(window);
1273     wp = &window_list[window];
1274     /*
1275      * "Zap" known windows, but don't destroy them.  We need to keep the
1276      * toplevel widget popped up so that later windows (e.g. tombstone)
1277      * are visible on DECWindow systems.  This is due to the virtual
1278      * roots that the DECWindow wm creates.
1279      */
1280     if (window == WIN_MESSAGE) {
1281         wp->keep_window = TRUE;
1282         WIN_MESSAGE = WIN_ERR;
1283         iflags.window_inited = 0;
1284     } else if (window == WIN_MAP) {
1285         wp->keep_window = TRUE;
1286         WIN_MAP = WIN_ERR;
1287     } else if (window == WIN_STATUS) {
1288         wp->keep_window = TRUE;
1289         WIN_STATUS = WIN_ERR;
1290     } else if (window == WIN_INVEN) {
1291         /* don't need to keep this one */
1292         WIN_INVEN = WIN_ERR;
1293     }
1294
1295     if (wp->boldfs) {
1296         XFreeFont(wp->boldfs_dpy, wp->boldfs);
1297         wp->boldfs = (XFontStruct *) 0;
1298         wp->boldfs_dpy = (Display *) 0;
1299     }
1300
1301     if (wp->title) {
1302         free(wp->title);
1303         wp->title = (char *) 0;
1304     }
1305
1306     switch (wp->type) {
1307     case NHW_MAP:
1308         destroy_map_window(wp);
1309         break;
1310     case NHW_MENU:
1311         destroy_menu_window(wp);
1312         break;
1313     case NHW_TEXT:
1314         destroy_text_window(wp);
1315         break;
1316     case NHW_STATUS:
1317         destroy_status_window(wp);
1318         break;
1319     case NHW_MESSAGE:
1320         destroy_message_window(wp);
1321         break;
1322     default:
1323         panic("destroy_nhwindow: unknown window type [%d]", wp->type);
1324         break;
1325     }
1326 }
1327
1328 void
1329 X11_update_inventory()
1330 {
1331     if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
1332         updated_inventory = 1; /* hack to avoid mapping&raising window */
1333         (void) display_inventory((char *) 0, FALSE);
1334         updated_inventory = 0;
1335     }
1336 }
1337
1338 /* The current implementation has all of the saved lines on the screen. */
1339 int
1340 X11_doprev_message()
1341 {
1342     return 0;
1343 }
1344
1345 void
1346 X11_nhbell()
1347 {
1348     /* We can't use XBell until toplevel has been initialized. */
1349     if (x_inited)
1350         XBell(XtDisplay(toplevel), 0);
1351     /* else print ^G ?? */
1352 }
1353
1354 void
1355 X11_mark_synch()
1356 {
1357     if (x_inited) {
1358         /*
1359          * The window document is unclear about the status of text
1360          * that has been pline()d but not displayed w/display_nhwindow().
1361          * Both the main and tty code assume that a pline() followed
1362          * by mark_synch() results in the text being seen, even if
1363          * display_nhwindow() wasn't called.  Duplicate this behavior.
1364          */
1365         if (WIN_MESSAGE != WIN_ERR)
1366             display_message_window(&window_list[WIN_MESSAGE]);
1367         XSync(XtDisplay(toplevel), False);
1368     }
1369 }
1370
1371 void
1372 X11_wait_synch()
1373 {
1374     if (x_inited)
1375         XFlush(XtDisplay(toplevel));
1376 }
1377
1378 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
1379 void
1380 X11_resume_nhwindows()
1381 {
1382     return;
1383 }
1384 /* ARGSUSED */
1385 void
1386 X11_suspend_nhwindows(str)
1387 const char *str;
1388 {
1389     nhUse(str);
1390
1391     return;
1392 }
1393
1394 /* Under X, we don't need to initialize the number pad. */
1395 /* ARGSUSED */
1396 void
1397 X11_number_pad(state) /* called from options.c */
1398 int state;
1399 {
1400     nhUse(state);
1401
1402     return;
1403 }
1404
1405 /* called from setftty() in unixtty.c */
1406 void
1407 X11_start_screen()
1408 {
1409     return;
1410 }
1411
1412 /* called from settty() in unixtty.c */
1413 void
1414 X11_end_screen()
1415 {
1416     return;
1417 }
1418
1419 #ifdef GRAPHIC_TOMBSTONE
1420 void
1421 X11_outrip(window, how, when)
1422 winid window;
1423 int how;
1424 time_t when;
1425 {
1426     struct xwindow *wp;
1427     FILE *rip_fp = 0;
1428
1429     check_winid(window);
1430     wp = &window_list[window];
1431
1432     /* make sure the graphical tombstone is available; it's not easy to
1433        revert to the ASCII-art text tombstone once we're past this point */
1434     if (appResources.tombstone && *appResources.tombstone)
1435         rip_fp = fopen(appResources.tombstone, "r"); /* "rip.xpm" */
1436     if (!rip_fp) {
1437         genl_outrip(window, how, when);
1438         return;
1439     }
1440     (void) fclose(rip_fp);
1441
1442     if (wp->type == NHW_TEXT) {
1443         wp->text_information->is_rip = TRUE;
1444     } else {
1445         panic("ripout on non-text window (window type [%d])", wp->type);
1446     }
1447
1448     calculate_rip_text(how, when);
1449 }
1450 #endif
1451
1452 /* init and exit nhwindows ------------------------------------------------ */
1453
1454 XtAppContext app_context;     /* context of application */
1455 Widget toplevel = (Widget) 0; /* toplevel widget */
1456 Atom wm_delete_window;        /* pop down windows */
1457
1458 static XtActionsRec actions[] = {
1459     { nhStr("dismiss_text"), dismiss_text }, /* text widget button action */
1460     { nhStr("delete_text"), delete_text },   /* text widget delete action */
1461     { nhStr("key_dismiss_text"), key_dismiss_text }, /* text key action   */
1462 #ifdef GRAPHIC_TOMBSTONE
1463     { nhStr("rip_dismiss_text"), rip_dismiss_text }, /* rip in text widget */
1464 #endif
1465     { nhStr("menu_key"), menu_key },             /* menu accelerators */
1466     { nhStr("yn_key"), yn_key },                 /* yn accelerators */
1467     { nhStr("yn_delete"), yn_delete },           /* yn delete-window */
1468     { nhStr("askname_delete"), askname_delete }, /* askname delete-window */
1469     { nhStr("getline_delete"), getline_delete }, /* getline delete-window */
1470     { nhStr("menu_delete"), menu_delete },       /* menu delete-window */
1471     { nhStr("ec_key"), ec_key },                 /* extended commands */
1472     { nhStr("ec_delete"), ec_delete },           /* ext-com menu delete */
1473     { nhStr("ps_key"), ps_key },                 /* player selection */
1474     { nhStr("plsel_quit"), plsel_quit },   /* player selection dialog */
1475     { nhStr("plsel_play"), plsel_play },   /* player selection dialog */
1476     { nhStr("plsel_rnd"), plsel_randomize }, /* player selection dialog */
1477     { nhStr("race_key"), race_key },             /* race selection */
1478     { nhStr("gend_key"), gend_key },             /* gender selection */
1479     { nhStr("algn_key"), algn_key },             /* alignment selection */
1480     { nhStr("X11_hangup"), X11_hangup },         /* delete of top-level */
1481     { nhStr("input"), map_input },               /* key input */
1482     { nhStr("scroll"), nh_keyscroll },           /* scrolling by keys */
1483 };
1484
1485 static XtResource resources[] = {
1486     { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean),
1487       XtOffset(AppResources *, slow), XtRString, nhStr("True") },
1488     { nhStr("fancy_status"), nhStr("Fancy_status"), XtRBoolean, sizeof(Boolean),
1489       XtOffset(AppResources *, fancy_status), XtRString, nhStr("True") },
1490     { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean),
1491       XtOffset(AppResources *, autofocus), XtRString, nhStr("False") },
1492     { nhStr("message_line"), nhStr("Message_line"), XtRBoolean,
1493       sizeof(Boolean), XtOffset(AppResources *, message_line), XtRString,
1494       nhStr("False") },
1495     { nhStr("highlight_prompt"), nhStr("Highlight_prompt"), XtRBoolean,
1496       sizeof(Boolean), XtOffset(AppResources *, highlight_prompt), XtRString,
1497       nhStr("True") },
1498     { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean,
1499       sizeof(Boolean), XtOffset(AppResources *, double_tile_size), XtRString,
1500       nhStr("False") },
1501     { nhStr("tile_file"), nhStr("Tile_file"), XtRString, sizeof(String),
1502       XtOffset(AppResources *, tile_file), XtRString, nhStr("x11tiles") },
1503     { nhStr("icon"), nhStr("Icon"), XtRString, sizeof(String),
1504       XtOffset(AppResources *, icon), XtRString, nhStr("nh72") },
1505     { nhStr("message_lines"), nhStr("Message_lines"), XtRInt, sizeof(int),
1506       XtOffset(AppResources *, message_lines), XtRString, nhStr("12") },
1507     { nhStr("extcmd_height_delta"), nhStr("Extcmd_height_delta"),
1508       XtRInt, sizeof (int),
1509       XtOffset(AppResources *, extcmd_height_delta), XtRString, nhStr("0") },
1510     { nhStr("pet_mark_bitmap"), nhStr("Pet_mark_bitmap"), XtRString,
1511       sizeof(String), XtOffset(AppResources *, pet_mark_bitmap), XtRString,
1512       nhStr("pet_mark.xbm") },
1513     { nhStr("pet_mark_color"), nhStr("Pet_mark_color"), XtRPixel,
1514       sizeof(XtRPixel), XtOffset(AppResources *, pet_mark_color), XtRString,
1515       nhStr("Red") },
1516     { nhStr("pilemark_bitmap"), nhStr("Pilemark_bitmap"), XtRString,
1517       sizeof(String), XtOffset(AppResources *, pilemark_bitmap), XtRString,
1518       nhStr("pilemark.xbm") },
1519     { nhStr("pilemark_color"), nhStr("Pilemark_color"), XtRPixel,
1520       sizeof(XtRPixel), XtOffset(AppResources *, pilemark_color), XtRString,
1521       nhStr("Green") },
1522 #ifdef GRAPHIC_TOMBSTONE
1523     { nhStr("tombstone"), nhStr("Tombstone"), XtRString, sizeof(String),
1524       XtOffset(AppResources *, tombstone), XtRString, nhStr("rip.xpm") },
1525     { nhStr("tombtext_x"), nhStr("Tombtext_x"), XtRInt, sizeof(int),
1526       XtOffset(AppResources *, tombtext_x), XtRString, nhStr("155") },
1527     { nhStr("tombtext_y"), nhStr("Tombtext_y"), XtRInt, sizeof(int),
1528       XtOffset(AppResources *, tombtext_y), XtRString, nhStr("78") },
1529     { nhStr("tombtext_dx"), nhStr("Tombtext_dx"), XtRInt, sizeof(int),
1530       XtOffset(AppResources *, tombtext_dx), XtRString, nhStr("0") },
1531     { nhStr("tombtext_dy"), nhStr("Tombtext_dy"), XtRInt, sizeof(int),
1532       XtOffset(AppResources *, tombtext_dy), XtRString, nhStr("13") },
1533 #endif
1534 };
1535
1536 static int
1537 panic_on_error(display, error)
1538 Display *display;
1539 XErrorEvent *error;
1540 {
1541     char buf[BUFSZ];
1542     XGetErrorText(display, error->error_code, buf, BUFSZ);
1543     fprintf(stderr, "X Error: code %i (%s), request %i, minor %i, serial %lu\n",
1544             error->error_code, buf,
1545             error->request_code, error->minor_code,
1546             error->serial);
1547     panic("X Error");
1548     return 0;
1549 }
1550
1551 void
1552 X11_init_nhwindows(argcp, argv)
1553 int *argcp;
1554 char **argv;
1555 {
1556     int i;
1557     Cardinal num_args;
1558     Arg args[4];
1559     uid_t savuid;
1560
1561     /* Init windows to nothing. */
1562     for (i = 0; i < MAX_WINDOWS; i++)
1563         window_list[i].type = NHW_NONE;
1564
1565     /* add another option that can be set */
1566     set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
1567     set_option_mod_status("mouse_support", SET_IN_GAME);
1568
1569     load_default_resources(); /* create default_resource_data[] */
1570
1571     /*
1572      * setuid hack: make sure that if nethack is setuid, to use real uid
1573      * when opening X11 connections, in case the user is using xauth, since
1574      * the "games" or whatever user probably doesn't have permission to open
1575      * a window on the user's display.  This code is harmless if the binary
1576      * is not installed setuid.  See include/system.h on compilation failures.
1577      */
1578     savuid = geteuid();
1579     (void) seteuid(getuid());
1580
1581     XSetIOErrorHandler((XIOErrorHandler) hangup);
1582
1583 #ifdef XI18N
1584     XtSetLanguageProc(NULL, NULL, NULL);
1585 #endif
1586 #if 1 /*JP*/
1587     XSetIOErrorHandler((XIOErrorHandler) hangup);
1588 #endif
1589 #ifdef INSTALLCOLORMAP
1590     XtSetTypeConverter(XtRString, XtRColormap, nhCvtStringToColormap, 
1591                        0, 0, XtCacheByDisplay, 0);
1592 #endif
1593     num_args = 0;
1594     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
1595     XtSetArg(args[num_args], XtNtitle, "NetHack"); num_args++;
1596
1597 #if 0 /*JP*/
1598     toplevel = XtAppInitialize(&app_context, "NetHack",     /* application  */
1599 #else
1600     toplevel = XtAppInitialize(&app_context, "JNetHack",    /* application  */
1601 #endif
1602                                (XrmOptionDescList) 0, 0,    /* options list */
1603                                argcp, (String *) argv,      /* command line */
1604                                default_resource_data, /* fallback resources */
1605                                (ArgList) args, num_args);
1606     XtOverrideTranslations(toplevel,
1607               XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
1608
1609     /* We don't need to realize the top level widget. */
1610
1611     old_error_handler = XSetErrorHandler(panic_on_error);
1612
1613 #ifdef TEXTCOLOR
1614     /* add new color converter to deal with overused colormaps */
1615     XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
1616                        (XtConvertArgList) nhcolorConvertArgs,
1617                        XtNumber(nhcolorConvertArgs), XtCacheByDisplay,
1618                        nhFreePixel);
1619 #endif /* TEXTCOLOR */
1620
1621     /* Register the actions mentioned in "actions". */
1622     XtAppAddActions(app_context, actions, XtNumber(actions));
1623
1624     /* Get application-wide resources */
1625     XtGetApplicationResources(toplevel, (XtPointer) &appResources, resources,
1626                               XtNumber(resources), (ArgList) 0, ZERO);
1627
1628     /* Initialize other things. */
1629     init_standard_windows();
1630
1631     /* Give the window manager an icon to use;  toplevel must be realized. */
1632     if (appResources.icon && *appResources.icon) {
1633         struct icon_info *ip;
1634
1635         for (ip = icon_data; ip->name; ip++)
1636             if (!strcmp(appResources.icon, ip->name)) {
1637                 icon_pixmap = XCreateBitmapFromData(
1638                     XtDisplay(toplevel), XtWindow(toplevel),
1639                     (genericptr_t) ip->bits, ip->width, ip->height);
1640                 if (icon_pixmap != None) {
1641                     XWMHints hints;
1642
1643                     (void) memset((genericptr_t) &hints, 0, sizeof(XWMHints));
1644                     hints.flags = IconPixmapHint;
1645                     hints.icon_pixmap = icon_pixmap;
1646                     XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel),
1647                                 &hints);
1648                 }
1649                 break;
1650             }
1651     }
1652
1653     /* end of setuid hack: reset uid back to the "games" uid */
1654     (void) seteuid(savuid);
1655
1656     x_inited = TRUE; /* X is now initialized */
1657     plsel_ask_name = FALSE;
1658
1659     release_default_resources();
1660
1661     /* Display the startup banner in the message window. */
1662     for (i = 1; i <= 4 + 2; ++i) /* (values beyond 4 yield blank lines) */
1663         X11_putstr(WIN_MESSAGE, 0, copyright_banner_line(i));
1664 }
1665
1666 /*
1667  * All done.
1668  */
1669 /* ARGSUSED */
1670 void
1671 X11_exit_nhwindows(dummy)
1672 const char *dummy;
1673 {
1674     extern Pixmap tile_pixmap; /* from winmap.c */
1675
1676     nhUse(dummy);
1677
1678     /* explicitly free the icon and tile pixmaps */
1679     if (icon_pixmap != None) {
1680         XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1681         icon_pixmap = None;
1682     }
1683     if (tile_pixmap != None) {
1684         XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1685         tile_pixmap = None;
1686     }
1687     if (WIN_INVEN != WIN_ERR)
1688         X11_destroy_nhwindow(WIN_INVEN);
1689     if (WIN_STATUS != WIN_ERR)
1690         X11_destroy_nhwindow(WIN_STATUS);
1691     if (WIN_MAP != WIN_ERR)
1692         X11_destroy_nhwindow(WIN_MAP);
1693     if (WIN_MESSAGE != WIN_ERR)
1694         X11_destroy_nhwindow(WIN_MESSAGE);
1695
1696     release_getline_widgets();
1697     release_yn_widgets();
1698 }
1699
1700 #ifdef X11_HANGUP_SIGNAL
1701 static void
1702 X11_sig(sig) /* Unix signal handler */
1703 int sig;
1704 {
1705     XtNoticeSignal(X11_sig_id);
1706     hangup(sig);
1707 }
1708
1709 static void
1710 X11_sig_cb(not_used, id)
1711 XtPointer not_used;
1712 XtSignalId *id;
1713 {
1714     XEvent event;
1715     XClientMessageEvent *mesg;
1716
1717     nhUse(not_used);
1718     nhUse(id);
1719
1720     /* Set up a fake message to the event handler. */
1721     mesg = (XClientMessageEvent *) &event;
1722     mesg->type = ClientMessage;
1723     mesg->message_type = XA_STRING;
1724     mesg->format = 8;
1725
1726     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1727                XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1728                (XEvent *) mesg);
1729 }
1730 #endif
1731
1732 /* delay_output ----------------------------------------------------------- */
1733
1734 /*
1735  * Timeout callback for delay_output().  Send a fake message to the map
1736  * window.
1737  */
1738 /* ARGSUSED */
1739 static void
1740 d_timeout(client_data, id)
1741 XtPointer client_data;
1742 XtIntervalId *id;
1743 {
1744     XEvent event;
1745     XClientMessageEvent *mesg;
1746
1747     nhUse(client_data);
1748     nhUse(id);
1749
1750     /* Set up a fake message to the event handler. */
1751     mesg = (XClientMessageEvent *) &event;
1752     mesg->type = ClientMessage;
1753     mesg->message_type = XA_STRING;
1754     mesg->format = 8;
1755     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1756                XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1757                (XEvent *) mesg);
1758 }
1759
1760 /*
1761  * Delay for 50ms.  This is not implemented asynch.  Maybe later.
1762  * Start the timeout, then wait in the event loop.  The timeout
1763  * function will send an event to the map window which will be waiting
1764  * for a sent event.
1765  */
1766 void
1767 X11_delay_output()
1768 {
1769     if (!x_inited)
1770         return;
1771
1772     (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1773
1774     /* The timeout function will enable the event loop exit. */
1775     (void) x_event(EXIT_ON_SENT_EVENT);
1776 }
1777
1778 /* X11_hangup ------------------------------------------------------------- */
1779 /* ARGSUSED */
1780 static void
1781 X11_hangup(w, event, params, num_params)
1782 Widget w;
1783 XEvent *event;
1784 String *params;
1785 Cardinal *num_params;
1786 {
1787     nhUse(w);
1788     nhUse(event);
1789     nhUse(params);
1790     nhUse(num_params);
1791
1792     hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1793     exit_x_event = TRUE;
1794 }
1795
1796 /* askname ---------------------------------------------------------------- */
1797 /* ARGSUSED */
1798 static void
1799 askname_delete(w, event, params, num_params)
1800 Widget w;
1801 XEvent *event;
1802 String *params;
1803 Cardinal *num_params;
1804 {
1805     nhUse(event);
1806     nhUse(params);
1807     nhUse(num_params);
1808
1809     nh_XtPopdown(w);
1810     (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1811     exit_x_event = TRUE;
1812 }
1813
1814 /* Callback for askname dialog widget. */
1815 /* ARGSUSED */
1816 static void
1817 askname_done(w, client_data, call_data)
1818 Widget w;
1819 XtPointer client_data;
1820 XtPointer call_data;
1821 {
1822     unsigned len;
1823     char *s;
1824     Widget dialog = (Widget) client_data;
1825
1826     nhUse(w);
1827     nhUse(call_data);
1828
1829     s = (char *) GetDialogResponse(dialog);
1830
1831     len = strlen(s);
1832     if (len == 0) {
1833         X11_nhbell();
1834         return;
1835     }
1836
1837     /* Truncate name if necessary */
1838     if (len >= sizeof plname - 1)
1839         len = sizeof plname - 1;
1840
1841     (void) strncpy(plname, s, len);
1842     plname[len] = '\0';
1843     XtFree(s);
1844
1845     nh_XtPopdown(XtParent(dialog));
1846     exit_x_event = TRUE;
1847 }
1848
1849 /* ask player for character's name to replace generic name "player" (or other
1850    values; see config.h) after 'nethack -u player' or OPTIONS=name:player */
1851 void
1852 X11_askname()
1853 {
1854     Widget popup, dialog;
1855     Arg args[1];
1856
1857     if (iflags.wc_player_selection == VIA_DIALOG) {
1858         /* X11_player_selection_dialog() handles name query */
1859         plsel_ask_name = TRUE;
1860         iflags.defer_plname = TRUE;
1861         return;
1862     } /* else iflags.wc_player_selection == VIA_PROMPTS */
1863
1864     XtSetArg(args[0], XtNallowShellResize, True);
1865
1866     popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel,
1867                                args, ONE);
1868     XtOverrideTranslations(popup,
1869           XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1870
1871     dialog = CreateDialog(popup, nhStr("dialog"), askname_done,
1872                           (XtCallbackProc) 0);
1873
1874     SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */
1875     SetDialogResponse(dialog, plname, PL_NSIZ); /* set default answer */
1876
1877     XtRealizeWidget(popup);
1878     positionpopup(popup, TRUE); /* center,bottom */
1879
1880     nh_XtPopup(popup, (int) XtGrabExclusive, dialog);
1881
1882     /* The callback will enable the event loop exit. */
1883     (void) x_event(EXIT_ON_EXIT);
1884
1885     XtDestroyWidget(dialog);
1886     XtDestroyWidget(popup);
1887 }
1888
1889 /* getline ---------------------------------------------------------------- */
1890 /* This uses Tim Theisen's dialog widget set (from GhostView). */
1891
1892 static Widget getline_popup, getline_dialog;
1893
1894 #define CANCEL_STR "\033"
1895 static char *getline_input;
1896
1897 /* Callback for getline dialog widget. */
1898 /* ARGSUSED */
1899 static void
1900 done_button(w, client_data, call_data)
1901 Widget w;
1902 XtPointer client_data;
1903 XtPointer call_data;
1904 {
1905     int len;
1906     char *s;
1907     Widget dialog = (Widget) client_data;
1908
1909     nhUse(w);
1910     nhUse(call_data);
1911
1912     s = (char *) GetDialogResponse(dialog);
1913     len = strlen(s);
1914
1915     /* Truncate input if necessary */
1916     if (len >= BUFSZ)
1917         len = BUFSZ - 1;
1918
1919     (void) strncpy(getline_input, s, len);
1920     getline_input[len] = '\0';
1921     XtFree(s);
1922
1923     nh_XtPopdown(XtParent(dialog));
1924     exit_x_event = TRUE;
1925 }
1926
1927 /* ARGSUSED */
1928 static void
1929 getline_delete(w, event, params, num_params)
1930 Widget w;
1931 XEvent *event;
1932 String *params;
1933 Cardinal *num_params;
1934 {
1935     nhUse(event);
1936     nhUse(params);
1937     nhUse(num_params);
1938
1939     Strcpy(getline_input, CANCEL_STR);
1940     nh_XtPopdown(w);
1941     exit_x_event = TRUE;
1942 }
1943
1944 /* Callback for getline dialog widget. */
1945 /* ARGSUSED */
1946 static void
1947 abort_button(w, client_data, call_data)
1948 Widget w;
1949 XtPointer client_data;
1950 XtPointer call_data;
1951 {
1952     Widget dialog = (Widget) client_data;
1953
1954     nhUse(w);
1955     nhUse(call_data);
1956
1957     Strcpy(getline_input, CANCEL_STR);
1958     nh_XtPopdown(XtParent(dialog));
1959     exit_x_event = TRUE;
1960 }
1961
1962 static void
1963 release_getline_widgets()
1964 {
1965     if (getline_dialog)
1966         XtDestroyWidget(getline_dialog), getline_dialog = (Widget) 0;
1967     if (getline_popup)
1968         XtDestroyWidget(getline_popup), getline_popup = (Widget) 0;
1969 }
1970
1971 void
1972 X11_getlin(question, input)
1973 const char *question;
1974 char *input;
1975 {
1976     getline_input = input;
1977
1978     flush_screen(1);
1979     if (!getline_popup) {
1980         Arg args[1];
1981
1982         XtSetArg(args[0], XtNallowShellResize, True);
1983
1984         getline_popup = XtCreatePopupShell("getline",
1985                                            transientShellWidgetClass,
1986                                            toplevel, args, ONE);
1987         XtOverrideTranslations(getline_popup,
1988                                XtParseTranslationTable(
1989                                   "<Message>WM_PROTOCOLS: getline_delete()"));
1990
1991         getline_dialog = CreateDialog(getline_popup, nhStr("dialog"),
1992                                       done_button, abort_button);
1993
1994 #if 0 /*JP*//*\95\8e\9a\97ñ\90Ý\92è\82Ì\8cã\82ë\82É\89ñ\82·*/
1995         XtRealizeWidget(getline_popup);
1996         XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1997                         &wm_delete_window, 1);
1998 #endif
1999     }
2000     SetDialogPrompt(getline_dialog, (String) question); /* set prompt */
2001     /* 60:  make the answer widget be wide enough to hold 60 characters,
2002        or the length of the prompt string, whichever is bigger.  User's
2003        response can be longer--when limit is reached, value-so-far will
2004        slide left hiding some chars at the beginning of the response but
2005        making room to type more.  [Prior to 3.6.1, width wasn't specifiable
2006        and answer box always got sized to match the width of the prompt.] */
2007 #ifdef EDIT_GETLIN
2008     SetDialogResponse(getline_dialog, input, 60); /* set default answer */
2009 #else
2010     SetDialogResponse(getline_dialog, nhStr(""), 60); /* set default answer */
2011 #endif
2012 #if 1 /*JP*//*\95\8e\9a\97ñ\90Ý\92è\82Ì\8cã\82ë\82É\89ñ\82·*/
2013     XtRealizeWidget(getline_popup);
2014     XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
2015                     &wm_delete_window, 1);
2016 #endif
2017     positionpopup(getline_popup, TRUE);           /* center,bottom */
2018
2019     nh_XtPopup(getline_popup, (int) XtGrabExclusive, getline_dialog);
2020
2021     /* The callback will enable the event loop exit. */
2022     (void) x_event(EXIT_ON_EXIT);
2023
2024 #if 1 /*JP*//*\96\88\89ñ\94j\8aü\82·\82é*/
2025     release_getline_widgets();
2026 #endif
2027 }
2028
2029 /* Display file ----------------------------------------------------------- */
2030
2031 /* uses a menu (with no selectors specified) rather than a text window
2032    to allow previous_page and first_menu actions to move backwards */
2033 void
2034 X11_display_file(str, complain)
2035 const char *str;
2036 boolean complain;
2037 {
2038     dlb *fp;
2039     winid newwin;
2040     struct xwindow *wp;
2041     anything any;
2042     menu_item *menu_list;
2043 #define LLEN 128
2044     char line[LLEN];
2045 #ifdef XI18N
2046     XFontSet fontset;
2047     XFontSetExtents *extent;
2048 #endif
2049
2050     /* Use the port-independent file opener to see if the file exists. */
2051     fp = dlb_fopen(str, RDTMODE);
2052     if (!fp) {
2053         if (complain)
2054             pline("Cannot open %s.  Sorry.", str);
2055         return; /* it doesn't exist, ignore */
2056     }
2057
2058     newwin = X11_create_nhwindow(NHW_MENU);
2059     wp = &window_list[newwin];
2060     X11_start_menu(newwin);
2061
2062     any = zeroany;
2063     while (dlb_fgets(line, LLEN, fp)) {
2064         X11_add_menu(newwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2065                      line, MENU_UNSELECTED);
2066     }
2067     (void) dlb_fclose(fp);
2068
2069     /* show file name as the window title */
2070     if (str)
2071         wp->title = dupstr(str);
2072
2073     wp->menu_information->permi = FALSE;
2074     wp->menu_information->disable_mcolors = TRUE;
2075     (void) X11_select_menu(newwin, PICK_NONE, &menu_list);
2076     X11_destroy_nhwindow(newwin);
2077 }
2078
2079 /* yn_function ------------------------------------------------------------ */
2080 /* (not threaded) */
2081
2082 static const char *yn_quitchars = " \n\r";
2083 static const char *yn_choices; /* string of acceptable input */
2084 static char yn_def;
2085 static char yn_return;           /* return value */
2086 static char yn_esc_map;          /* ESC maps to this char. */
2087 static Widget yn_popup;          /* popup for the yn fuction (created once) */
2088 static Widget yn_label;          /* label for yn function (created once) */
2089 static boolean yn_getting_num;   /* TRUE if accepting digits */
2090 static boolean yn_preserve_case; /* default is to force yn to lower case */
2091 static int yn_ndigits;           /* digit count */
2092 static long yn_val;              /* accumulated value */
2093
2094 static const char yn_translations[] = "#override\n\
2095      <Key>: yn_key()";
2096
2097 /*
2098  * Convert the given key event into a character.  If the key maps to
2099  * more than one character only the first is returned.  If there is
2100  * no conversion (i.e. just the CTRL key hit) a NUL is returned.
2101  */
2102 char
2103 key_event_to_char(key)
2104 XKeyEvent *key;
2105 {
2106     char keystring[MAX_KEY_STRING];
2107     int nbytes;
2108     boolean meta = !!(key->state & Mod1Mask);
2109
2110     nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0,
2111                            (XComposeStatus *) 0);
2112
2113     /* Modifier keys return a zero lengh string when pressed. */
2114     if (nbytes == 0)
2115         return '\0';
2116
2117     return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
2118 }
2119
2120 /*
2121  * Called when we get a WM_DELETE_WINDOW event on a yn window.
2122  */
2123 /* ARGSUSED */
2124 static void
2125 yn_delete(w, event, params, num_params)
2126 Widget w;
2127 XEvent *event;
2128 String *params;
2129 Cardinal *num_params;
2130 {
2131     nhUse(w);
2132     nhUse(event);
2133     nhUse(params);
2134     nhUse(num_params);
2135
2136     yn_getting_num = FALSE;
2137     /* Only use yn_esc_map if we have choices.  Otherwise, return ESC. */
2138     yn_return = yn_choices ? yn_esc_map : '\033';
2139     exit_x_event = TRUE; /* exit our event handler */
2140 }
2141
2142 /*
2143  * Called when we get a key press event on a yn window.
2144  */
2145 /* ARGSUSED */
2146 static void
2147 yn_key(w, event, params, num_params)
2148 Widget w;
2149 XEvent *event;
2150 String *params;
2151 Cardinal *num_params;
2152 {
2153     char ch;
2154
2155     if (appResources.slow && !input_func)
2156         map_input(w, event, params, num_params);
2157
2158     ch = key_event_to_char((XKeyEvent *) event);
2159
2160     if (ch == '\0') { /* don't accept nul char or modifier event */
2161         /* no bell */
2162         return;
2163     }
2164
2165     if (!yn_choices) { /* accept any input */
2166         yn_return = ch;
2167     } else {
2168         if (!yn_preserve_case)
2169             ch = lowc(ch); /* move to lower case */
2170
2171         if (ch == '\033') {
2172             yn_getting_num = FALSE;
2173             yn_return = yn_esc_map;
2174         } else if (index(yn_quitchars, ch)) {
2175             yn_return = yn_def;
2176         } else if (index(yn_choices, ch)) {
2177             if (ch == '#') {
2178                 if (yn_getting_num) { /* don't select again */
2179                     X11_nhbell();
2180                     return;
2181                 }
2182                 yn_getting_num = TRUE;
2183                 yn_ndigits = 0;
2184                 yn_val = 0;
2185                 return; /* wait for more input */
2186             }
2187             yn_return = ch;
2188             if (ch != 'y')
2189                 yn_getting_num = FALSE;
2190         } else {
2191             if (yn_getting_num) {
2192                 if (digit(ch)) {
2193                     yn_ndigits++;
2194                     yn_val = (yn_val * 10) + (long) (ch - '0');
2195                     return; /* wait for more input */
2196                 }
2197                 if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) {
2198                     yn_ndigits--;
2199                     yn_val = yn_val / 10;
2200                     return; /* wait for more input */
2201                 }
2202             }
2203             X11_nhbell(); /* no match */
2204             return;
2205         }
2206
2207         if (yn_getting_num) {
2208             yn_return = '#';
2209             if (yn_val < 0)
2210                 yn_val = 0;
2211             yn_number = yn_val; /* assign global */
2212         }
2213     }
2214     exit_x_event = TRUE; /* exit our event handler */
2215 }
2216
2217 /* called at exit time */
2218 static void
2219 release_yn_widgets()
2220 {
2221     if (yn_label)
2222         XtDestroyWidget(yn_label), yn_label = (Widget) 0;
2223     if (yn_popup)
2224         XtDestroyWidget(yn_popup), yn_popup = (Widget) 0;
2225 }
2226
2227 /* X11-specific edition of yn_function(), the routine called by the core
2228    to show a prompt and get a single keystroke answer, often 'y' vs 'n' */
2229 char
2230 X11_yn_function(ques, choices, def)
2231 const char *ques;
2232 const char *choices; /* string of possible response chars; any char if Null */
2233 char def;            /* default response if user hits <space> or <return> */
2234 {
2235     char buf[BUFSZ];
2236     Arg args[4];
2237     Cardinal num_args;
2238
2239     yn_choices = choices; /* set up globals for callback to use */
2240     yn_def = def;
2241     yn_preserve_case = !choices; /* preserve case when an arbitrary
2242                                     response is allowed */
2243
2244     /*
2245      * This is sort of a kludge.  There are quite a few places in the main
2246      * nethack code where a pline containing information is followed by a
2247      * call to yn_function().  There is no flush of the message window
2248      * (it is implicit in the tty window port), so the line never shows
2249      * up for us!  Solution: do our own flush.
2250      */
2251     if (WIN_MESSAGE != WIN_ERR)
2252         display_message_window(&window_list[WIN_MESSAGE]);
2253
2254     if (choices) {
2255         char *cb, choicebuf[QBUFSZ];
2256
2257         Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
2258         /* default when choices are present is to force yn answer to
2259            lowercase unless one or more choices are explicitly uppercase;
2260            check this before stripping the hidden choices */
2261         for (cb = choicebuf; *cb; ++cb)
2262             if ('A' <= *cb && *cb <= 'Z') {
2263                 yn_preserve_case = TRUE;
2264                 break;
2265             }
2266         if ((cb = index(choicebuf, '\033')) != 0)
2267             *cb = '\0';
2268         /* ques [choices] (def) */
2269         if ((int) (1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ)
2270             panic("X11_yn_function:  question too long");
2271         (void) strncpy(buf, ques, QBUFSZ - 1);
2272         buf[QBUFSZ - 1] = '\0';
2273         Sprintf(eos(buf), " [%s]", choicebuf);
2274         if (def)
2275             Sprintf(eos(buf), " (%c)", def);
2276         Strcat(buf, " ");
2277
2278         /* escape maps to 'q' or 'n' or default, in that order */
2279         yn_esc_map = (index(choices, 'q') ? 'q'
2280                       : index(choices, 'n') ? 'n'
2281                         : def);
2282     } else {
2283         if ((int) (1 + strlen(ques) + 1) >= BUFSZ)
2284             panic("X11_yn_function:  question too long");
2285         Strcpy(buf, ques);
2286         Strcat(buf, " ");
2287     }
2288
2289     /*
2290      * The 'slow' resource is misleadingly named.  When it is True, we
2291      * re-use the same one-line widget above the map for all yn prompt
2292      * responses instead of re-using a popup widget for each one.  It's
2293      * crucial for client/server configs where the server is slow putting
2294      * up a popup or requires click-to-focus to respond to the popup, but
2295      * is also useful for players who just want to be able to look at the
2296      * same location to read questions they're being asked to answer.
2297      */
2298
2299     if (appResources.slow) {
2300         /*
2301          * 'slow':  the yn_label widget was created when the map and
2302          * status widgets were, and is positioned between them.  It
2303          * will persist until end of game.  All we need to do for
2304          * yn_function is direct keystroke input to the yn response
2305          * handler and reset its label to be the prompt text (below).
2306          */
2307         input_func = yn_key;
2308         highlight_yn(FALSE); /* expose yn_label as separate from map */
2309     } else if (!yn_label) {
2310         /*
2311          * Not 'slow'; create a persistent widget that will be popped up
2312          * as needed, then down again, and last until end of game.  The
2313          * associated yn_label widget is used to track whether it exists.
2314          */
2315         XtSetArg(args[0], XtNallowShellResize, True);
2316         yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
2317                                       toplevel, args, ONE);
2318         XtOverrideTranslations(yn_popup,
2319                XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
2320
2321         num_args = 0;
2322         XtSetArg(args[num_args], XtNtranslations,
2323                  XtParseTranslationTable(yn_translations)); num_args++;
2324 #if defined(X11R6) && defined(XI18N)
2325         XtSetArg(args[num_args], XtNinternational, True); num_args++;
2326 #endif
2327         yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass,
2328                                          yn_popup, args, num_args);
2329
2330         XtRealizeWidget(yn_popup);
2331         XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
2332                         &wm_delete_window, 1);
2333     }
2334
2335     /* set the label of the yn widget to be the prompt text */
2336     num_args = 0;
2337     XtSetArg(args[num_args], XtNlabel, buf); num_args++;
2338     XtSetValues(yn_label, args, num_args);
2339
2340     if (!appResources.slow) {
2341         /*
2342          * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
2343          * need to set the label twice to get the size to change.
2344          */
2345         num_args = 0;
2346         XtSetArg(args[num_args], XtNlabel, buf); num_args++;
2347         XtSetValues(yn_label, args, num_args);
2348
2349         positionpopup(yn_popup, TRUE);
2350         nh_XtPopup(yn_popup, (int) XtGrabExclusive, yn_label);
2351     }
2352
2353     yn_getting_num = FALSE;
2354     (void) x_event(EXIT_ON_EXIT); /* get keystroke(s) */
2355
2356     if (appResources.slow) {
2357         /* keystrokes now belong to the map */
2358         input_func = 0;
2359         /* erase the prompt */
2360         num_args = 0;
2361         XtSetArg(args[num_args], XtNlabel, " "); num_args++;
2362         XtSetValues(yn_label, args, num_args);
2363         highlight_yn(FALSE); /* disguise yn_label as part of map */
2364     } else {
2365         nh_XtPopdown(yn_popup); /* this removes the event grab */
2366     }
2367
2368     pline("%s%c", buf, (yn_return != '\033') ? yn_return : '\0');
2369
2370     return yn_return;
2371 }
2372
2373 /* used when processing window-capability-specific run-time options;
2374    we support toggling tiles on and off via iflags.wc_tiled_map */
2375 void
2376 X11_preference_update(pref)
2377 const char *pref;
2378 {
2379     if (!strcmp(pref, "tiled_map")) {
2380         if (WIN_MAP != WIN_ERR)
2381             display_map_window(&window_list[WIN_MAP]);
2382     }
2383 }
2384
2385 /* End global functions =================================================== */
2386
2387 /*
2388  * Before we wait for input via nhgetch() and nh_poskey(), we need to
2389  * do some pre-processing.
2390  */
2391 static int
2392 input_event(exit_condition)
2393 int exit_condition;
2394 {
2395     if (appResources.fancy_status && WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
2396         check_turn_events();
2397     if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
2398         check_cursor_visibility(&window_list[WIN_MAP]);
2399     if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
2400         set_last_pause(&window_list[WIN_MESSAGE]);
2401
2402     return x_event(exit_condition);
2403 }
2404
2405 /*ARGSUSED*/
2406 void
2407 msgkey(w, data, event)
2408 Widget w;
2409 XtPointer data;
2410 XEvent *event;
2411 {
2412     Cardinal num = 0;
2413
2414     nhUse(w);
2415     nhUse(data);
2416
2417     map_input(window_list[WIN_MAP].w, event, (String *) 0, &num);
2418 }
2419
2420 /* only called for autofocus */
2421 /*ARGSUSED*/
2422 static void
2423 win_visible(w, data, event, flag)
2424 Widget w;
2425 XtPointer data; /* client_data not used */
2426 XEvent *event;
2427 Boolean *flag; /* continue_to_dispatch flag not used */
2428 {
2429     XVisibilityEvent *vis_event = (XVisibilityEvent *) event;
2430
2431     nhUse(data);
2432     nhUse(flag);
2433
2434     if (vis_event->state != VisibilityFullyObscured) {
2435         /* one-time operation; cancel ourself */
2436         XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
2437                              win_visible, (XtPointer) 0);
2438         /* grab initial input focus */
2439         XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
2440     }
2441 }
2442
2443 /* if 'slow' and 'highlight_prompt', set the yn_label widget to look like
2444    part of the map when idle or to invert background and foreground when
2445    a prompt is active */
2446 void
2447 highlight_yn(init)
2448 boolean init;
2449 {
2450     struct xwindow *xmap;
2451
2452     if (!appResources.slow || !appResources.highlight_prompt)
2453         return;
2454
2455     /* first time through, WIN_MAP isn't fully initiialized yet */
2456     xmap = ((map_win != WIN_ERR) ? &window_list[map_win]
2457                : (WIN_MAP != WIN_ERR) ? &window_list[WIN_MAP] : 0);
2458
2459     if (init && xmap) {
2460         Arg args[2];
2461         XGCValues vals;
2462         unsigned long fg_bg = (GCForeground | GCBackground);
2463         GC gc = (xmap->map_information->is_tile
2464                     ? xmap->map_information->tile_map.white_gc
2465                     : xmap->map_information->text_map.copy_gc);
2466
2467         (void) memset((genericptr_t) &vals, 0, sizeof vals);
2468         if (XGetGCValues(XtDisplay(xmap->w), gc, fg_bg, &vals)) {
2469             XtSetArg(args[0], XtNforeground, vals.foreground);
2470             XtSetArg(args[1], XtNbackground, vals.background);
2471             XtSetValues(yn_label, args, TWO);
2472         }
2473     } else
2474         swap_fg_bg(yn_label);
2475 }
2476
2477 /*
2478  * Set up the playing console.  This has three major parts:  the
2479  * message window, the map, and the status window.
2480  *
2481  * For configs specifying the 'slow' resource, the yn_label widget
2482  * is placed above the map and below the message window.  Prompts
2483  * requiring a single character response are displayed there rather
2484  * than using a popup.
2485  */
2486 static void
2487 init_standard_windows()
2488 {
2489     Widget form, message_viewport, map_viewport, status;
2490     Arg args[8];
2491     Cardinal num_args;
2492     Dimension message_vp_width, map_vp_width, status_width, max_width;
2493     int map_vp_hd, status_hd;
2494     struct xwindow *wp;
2495
2496     num_args = 0;
2497     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
2498 #if 0 /*JP*/
2499     form = XtCreateManagedWidget("nethack", panedWidgetClass, toplevel, args,
2500                                  num_args);
2501 #else
2502     form = XtCreateManagedWidget("jnethack", panedWidgetClass, toplevel, args,
2503                                  num_args);
2504 #endif
2505
2506     XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey,
2507                       (XtPointer) 0);
2508
2509     if (appResources.autofocus)
2510         XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible,
2511                           (XtPointer) 0);
2512
2513     /*
2514      * Create message window.
2515      */
2516     WIN_MESSAGE = message_win = find_free_window();
2517     wp = &window_list[message_win];
2518     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2519     wp->popup = (Widget) 0;
2520     create_message_window(wp, FALSE, form);
2521     message_viewport = XtParent(wp->w);
2522
2523     /* Tell the form that contains it that resizes are OK. */
2524     num_args = 0;
2525     XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2526     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2527     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
2528     XtSetValues(message_viewport, args, num_args);
2529
2530     if (appResources.slow) {
2531         num_args = 0;
2532         XtSetArg(args[num_args], XtNtranslations,
2533                  XtParseTranslationTable(yn_translations)); num_args++;
2534 #if defined(X11R6) && defined(XI18N)
2535         XtSetArg(args[num_args], XtNinternational, True);
2536         num_args++;
2537 #endif
2538         yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form,
2539                                          args, num_args);
2540         num_args = 0;
2541         XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2542                                                                    num_args++;
2543         XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft);
2544                                                                   num_args++;
2545         XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2546         XtSetArg(args[num_args], nhStr(XtNlabel), " "); num_args++;
2547         XtSetValues(yn_label, args, num_args);
2548     }
2549
2550     /*
2551      * Create the map window & viewport and chain the viewport beneath the
2552      * message_viewport.
2553      */
2554     map_win = find_free_window();
2555     wp = &window_list[map_win];
2556     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2557     wp->popup = (Widget) 0;
2558     create_map_window(wp, FALSE, form);
2559     map_viewport = XtParent(wp->w);
2560
2561     /* Chain beneath message_viewport or yn window. */
2562     num_args = 0;
2563     if (appResources.slow) {
2564         XtSetArg(args[num_args], nhStr(XtNfromVert), yn_label); num_args++;
2565     } else {
2566         XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2567                                                                    num_args++;
2568     }
2569     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2570     XtSetValues(map_viewport, args, num_args);
2571
2572     /* Create the status window, with the form as it's parent. */
2573     status_win = find_free_window();
2574     wp = &window_list[status_win];
2575     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2576     wp->popup = (Widget) 0;
2577     create_status_window(wp, FALSE, form);
2578     status = wp->w;
2579
2580     /*
2581      * Chain the status window beneath the viewport.  Mark the left and right
2582      * edges so that they stay a fixed distance from the left edge of the
2583      * parent, as well as the top and bottom edges so that they stay a fixed
2584      * distance from the bottom of the parent.  We do this so that the status
2585      * will never expand or contract.
2586      */
2587     num_args = 0;
2588     XtSetArg(args[num_args], nhStr(XtNfromVert), map_viewport); num_args++;
2589     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2590     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
2591     XtSetArg(args[num_args], nhStr(XtNtop), XtChainBottom); num_args++;
2592     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2593     XtSetValues(status, args, num_args);
2594
2595     /*
2596      * Realize the popup so that the status widget knows it's size.
2597      *
2598      * If we unset MappedWhenManaged then the DECwindow driver doesn't
2599      * attach the nethack toplevel to the highest virtual root window.
2600      * So don't do it.
2601      */
2602     /* XtSetMappedWhenManaged(toplevel, False); */
2603     XtRealizeWidget(toplevel);
2604     wm_delete_window = XInternAtom(XtDisplay(toplevel),
2605                                    "WM_DELETE_WINDOW", False);
2606     XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
2607                     &wm_delete_window, 1);
2608
2609     /*
2610      * Resize to at most full-screen.
2611      */
2612     {
2613 #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
2614
2615         int screen_width = WidthOfScreen(XtScreen(wp->w));
2616         int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
2617         Dimension form_width, form_height;
2618
2619         XtSetArg(args[0], XtNwidth, &form_width);
2620         XtSetArg(args[1], XtNheight, &form_height);
2621         XtGetValues(toplevel, args, TWO);
2622
2623         if (form_width > screen_width || form_height > screen_height) {
2624             XtSetArg(args[0], XtNwidth, min(form_width, screen_width));
2625             XtSetArg(args[1], XtNheight, min(form_height, screen_height));
2626             XtSetValues(toplevel, args, TWO);
2627             XMoveWindow(XtDisplay(toplevel), XtWindow(toplevel), 0,
2628                         TITLEBAR_SPACE);
2629         }
2630 #undef TITLEBAR_SPACE
2631     }
2632
2633     post_process_tiles(); /* after toplevel is realized */
2634
2635     /*
2636      * Now get the default widths of the windows.
2637      */
2638     XtSetArg(args[0], nhStr(XtNwidth), &message_vp_width);
2639     XtGetValues(message_viewport, args, ONE);
2640     XtSetArg(args[0], nhStr(XtNwidth), &map_vp_width);
2641     XtSetArg(args[1], nhStr(XtNhorizDistance), &map_vp_hd);
2642     XtGetValues(map_viewport, args, TWO);
2643     XtSetArg(args[0], nhStr(XtNwidth), &status_width);
2644     XtSetArg(args[1], nhStr(XtNhorizDistance), &status_hd);
2645     XtGetValues(status, args, TWO);
2646
2647     /*
2648      * Adjust positions and sizes.  The message viewport widens out to the
2649      * widest width.  Both the map and status are centered by adjusting
2650      * their horizDistance.
2651      */
2652     if (map_vp_width < status_width || map_vp_width < message_vp_width) {
2653         if (status_width > message_vp_width) {
2654             XtSetArg(args[0], nhStr(XtNwidth), status_width);
2655             XtSetValues(message_viewport, args, ONE);
2656             max_width = status_width;
2657         } else {
2658             max_width = message_vp_width;
2659         }
2660         XtSetArg(args[0], nhStr(XtNhorizDistance),
2661                  map_vp_hd + ((int) (max_width - map_vp_width) / 2));
2662         XtSetValues(map_viewport, args, ONE);
2663
2664     } else { /* map is widest */
2665         XtSetArg(args[0], nhStr(XtNwidth), map_vp_width);
2666         XtSetValues(message_viewport, args, ONE);
2667     }
2668     /*
2669      * Clear all data values on the fancy status widget so that the values
2670      * used for spacing don't appear.  This needs to be called some time
2671      * after the fancy status widget is realized (above, with the game popup),
2672      * but before it is popped up.
2673      */
2674     if (appResources.fancy_status)
2675         null_out_status();
2676     /*
2677      * Set the map size to its standard size.  As with the message window
2678      * above, the map window needs to be set to its constrained size until
2679      * its parent (the viewport widget) was realized.
2680      *
2681      * Move the message window's slider to the bottom.
2682      */
2683     set_map_size(&window_list[map_win], COLNO, ROWNO);
2684     set_message_slider(&window_list[message_win]);
2685
2686     /* attempt to catch fatal X11 errors before the program quits */
2687     (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
2688
2689     highlight_yn(TRUE); /* switch foreground and background */
2690
2691     /* We can now print to the message window. */
2692     iflags.window_inited = 1;
2693 }
2694
2695 void
2696 nh_XtPopup(w, g, childwid)
2697 Widget w;        /* widget */
2698 int g;           /* type of grab */
2699 Widget childwid; /* child to receive focus (can be None) */
2700 {
2701     XtPopup(w, (XtGrabKind) g);
2702     XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
2703     if (appResources.autofocus)
2704         XtSetKeyboardFocus(toplevel, childwid);
2705 }
2706
2707 void
2708 nh_XtPopdown(w)
2709 Widget w;
2710 {
2711     XtPopdown(w);
2712     if (appResources.autofocus)
2713         XtSetKeyboardFocus(toplevel, None);
2714 }
2715
2716 void
2717 win_X11_init(dir)
2718 int dir;
2719 {
2720     if (dir != WININIT)
2721         return;
2722
2723 #ifdef OPENWINBUG
2724     /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
2725      * two routines will not be found when linking.  An apparently correct
2726      * executable is produced, along with nasty messages and a failure code
2727      * returned to make.  The routines are in the static libXmu.a and
2728      * libXmu.sa.4.0, but not in libXmu.so.4.0.  Rather than fiddle with
2729      * static linking, we do this.
2730      */
2731     if (rn2(2) > 2) { /* i.e., FALSE that an optimizer probably can't find */
2732         get_wmShellWidgetClass();
2733         get_applicationShellWidgetClass();
2734     }
2735 #endif
2736     return;
2737 }
2738
2739 void
2740 find_scrollbars(w, horiz, vert)
2741 Widget w;
2742 Widget *horiz, *vert;
2743 {
2744     if (w) {
2745         do {
2746             *horiz = XtNameToWidget(w, "*horizontal");
2747             *vert = XtNameToWidget(w, "*vertical");
2748             w = XtParent(w);
2749         } while (!*horiz && !*vert && w);
2750     }
2751 }
2752
2753 /* Callback
2754  * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2755  */
2756 /*ARGSUSED*/
2757 void
2758 nh_keyscroll(viewport, event, params, num_params)
2759 Widget viewport;
2760 XEvent *event;
2761 String *params;
2762 Cardinal *num_params;
2763 {
2764     Arg arg[2];
2765     Widget horiz_sb = (Widget) 0, vert_sb = (Widget) 0;
2766     float top, shown;
2767     Boolean do_call;
2768     int direction;
2769     Cardinal in_nparams = (num_params ? *num_params : 0);
2770
2771     nhUse(event);
2772
2773     if (in_nparams != 1)
2774         return; /* bad translation */
2775
2776     direction = atoi(params[0]);
2777
2778     find_scrollbars(viewport, &horiz_sb, &vert_sb);
2779
2780 #define H_DELTA 0.25 /* distance of horiz shift */
2781     /* vert shift is half of curr distance */
2782     /* The V_DELTA is 1/2 the value of shown. */
2783
2784     if (horiz_sb) {
2785         XtSetArg(arg[0], nhStr(XtNshown), &shown);
2786         XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2787         XtGetValues(horiz_sb, arg, TWO);
2788
2789         do_call = True;
2790
2791         switch (direction) {
2792         case 1:
2793         case 4:
2794         case 7:
2795             top -= H_DELTA;
2796             if (top < 0.0)
2797                 top = 0.0;
2798             break;
2799         case 3:
2800         case 6:
2801         case 9:
2802             top += H_DELTA;
2803             if (top + shown > 1.0)
2804                 top = 1.0 - shown;
2805             break;
2806         default:
2807             do_call = False;
2808         }
2809
2810         if (do_call) {
2811             XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2812         }
2813     }
2814
2815     if (vert_sb) {
2816         XtSetArg(arg[0], nhStr(XtNshown), &shown);
2817         XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2818         XtGetValues(vert_sb, arg, TWO);
2819
2820         do_call = True;
2821
2822         switch (direction) {
2823         case 7:
2824         case 8:
2825         case 9:
2826             top -= shown / 2.0;
2827             if (top < 0.0)
2828                 top = 0;
2829             break;
2830         case 1:
2831         case 2:
2832         case 3:
2833             top += shown / 2.0;
2834             if (top + shown > 1.0)
2835                 top = 1.0 - shown;
2836             break;
2837         default:
2838             do_call = False;
2839         }
2840
2841         if (do_call) {
2842             XtCallCallbacks(vert_sb, XtNjumpProc, &top);
2843         }
2844     }
2845 }
2846
2847 /*winX.c*/