OSDN Git Service

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