OSDN Git Service

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