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