OSDN Git Service

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