OSDN Git Service

import nethack-3.6.0
[jnethack/source.git] / win / X11 / winX.c
1 /* NetHack 3.6  winX.c  $NHDT-Date: 1432512808 2015/05/25 00:13:28 $  $NHDT-Branch: master $:$NHDT-Revision: 1.33 $ */
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
53 #ifndef NO_SIGNAL
54 #include <signal.h>
55 #endif
56
57 /* Should be defined in <X11/Intrinsic.h> but you never know */
58 #ifndef XtSpecificationRelease
59 #define XtSpecificationRelease 0
60 #endif
61
62 /*
63  * Icons.
64  */
65 #include "../win/X11/nh72icon"
66 #include "../win/X11/nh56icon"
67 #include "../win/X11/nh32icon"
68
69 static struct icon_info {
70     const char *name;
71     unsigned char *bits;
72     unsigned width, height;
73 } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
74                   { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
75                   { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
76                   { (const char *) 0, (unsigned char *) 0, 0, 0 } };
77
78 /*
79  * Private global variables (shared among the window port files).
80  */
81 struct xwindow window_list[MAX_WINDOWS];
82 AppResources appResources;
83 void FDECL((*input_func), (Widget, XEvent *, String *, Cardinal *));
84 int click_x, click_y, click_button; /* Click position on a map window   */
85                                     /* (filled by set_button_values()). */
86 int updated_inventory;
87
88 #if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
89 #if XtSpecificationRelease >= 6
90 #define X11_HANGUP_SIGNAL
91 static XtSignalId X11_sig_id;
92 #endif
93 #endif
94
95 /* this is only needed until X11_status_* routines are written */
96 extern NEARDATA winid WIN_STATUS;
97
98 /* Interface definition, for windows.c */
99 struct window_procs X11_procs = {
100     "X11", WC_COLOR | WC_HILITE_PET | WC_TILED_MAP, 0L, X11_init_nhwindows,
101     X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows,
102     X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow,
103     X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs,
104     X11_putstr, genl_putmixed, X11_display_file, X11_start_menu, X11_add_menu,
105     X11_end_menu, X11_select_menu,
106     genl_message_menu, /* no need for X-specific handling */
107     X11_update_inventory, X11_mark_synch, X11_wait_synch,
108 #ifdef CLIPPING
109     X11_cliparound,
110 #endif
111 #ifdef POSITIONBAR
112     donull,
113 #endif
114     X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch,
115     X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function,
116     X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output,
117 #ifdef CHANGE_COLOR /* only a Mac option currently */
118     donull, donull,
119 #endif
120     /* other defs that really should go away (they're tty specific) */
121     X11_start_screen, X11_end_screen,
122 #ifdef GRAPHIC_TOMBSTONE
123     X11_outrip,
124 #else
125     genl_outrip,
126 #endif
127     X11_preference_update, genl_getmsghistory, genl_putmsghistory,
128 #ifdef STATUS_VIA_WINDOWPORT
129     genl_status_init, genl_status_finish, genl_status_enablefield,
130     genl_status_update,
131 #ifdef STATUS_HILITES
132     genl_status_threshold,
133 #endif
134 #endif
135     genl_can_suspend_no, /* XXX may not always be correct */
136 };
137
138 /*
139  * Local functions.
140  */
141 static void FDECL(dismiss_file, (Widget, XEvent *, String *, Cardinal *));
142 static void FDECL(delete_file, (Widget, XEvent *, String *, Cardinal *));
143 static void FDECL(yn_key, (Widget, XEvent *, String *, Cardinal *));
144 static void FDECL(yn_delete, (Widget, XEvent *, String *, Cardinal *));
145 static void FDECL(askname_delete, (Widget, XEvent *, String *, Cardinal *));
146 static void FDECL(getline_delete, (Widget, XEvent *, String *, Cardinal *));
147 static void FDECL(X11_hangup, (Widget, XEvent *, String *, Cardinal *));
148 static int FDECL(input_event, (int));
149 static void FDECL(win_visible, (Widget, XtPointer, XEvent *, Boolean *));
150 static void NDECL(init_standard_windows);
151 #ifdef X11_HANGUP_SIGNAL
152 static void FDECL(X11_sig, (int));
153 static void FDECL(X11_sig_cb, (XtPointer, XtSignalId *));
154 #endif
155
156 /*
157  * Local variables.
158  */
159 static boolean x_inited = FALSE;    /* TRUE if window system is set up. */
160 static winid message_win = WIN_ERR, /* These are the winids of the          */
161     map_win = WIN_ERR,              /*   message, map, and status           */
162     status_win = WIN_ERR;           /*   windows, when they are created */
163                                     /*   in init_windows().                 */
164 static Pixmap icon_pixmap = None;   /* Pixmap for icon.             */
165
166 /*
167  * Find the window structure that corresponds to the given widget.  Note
168  * that this is not the popup widget, nor the viewport, but the child.
169  */
170 struct xwindow *
171 find_widget(w)
172 Widget w;
173 {
174     int windex;
175     struct xwindow *wp;
176
177     /* Search to find the corresponding window.  Look at the main widget, */
178     /* popup, the parent of the main widget, then parent of the widget. */
179     for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
180         if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w
181                                      || (wp->w && (XtParent(wp->w)) == w)
182                                      || (wp->popup == XtParent(w))))
183             break;
184
185     if (windex == MAX_WINDOWS)
186         panic("find_widget:  can't match widget");
187     return wp;
188 }
189
190 /*
191  * Find a free window slot for use.
192  */
193 static winid
194 find_free_window()
195 {
196     int windex;
197     struct xwindow *wp;
198
199     for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS;
200          windex++, wp++)
201         if (wp->type == NHW_NONE)
202             break;
203
204     if (windex == MAX_WINDOWS)
205         panic("find_free_window: no free windows!");
206     return (winid) windex;
207 }
208
209 /*
210  * Color conversion.  The default X11 color converters don't try very
211  * hard to find matching colors in PseudoColor visuals.  If they can't
212  * allocate the exact color, they puke and give you something stupid.
213  * This is an attempt to find some close readonly cell and use it.
214  */
215 XtConvertArgRec const nhcolorConvertArgs[] = {
216     { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
217       sizeof(Screen *) },
218     { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.colormap),
219       sizeof(Colormap) }
220 };
221
222 #define done(type, value)                             \
223     {                                                 \
224         if (toVal->addr != 0) {                       \
225             if (toVal->size < sizeof(type)) {         \
226                 toVal->size = sizeof(type);           \
227                 return False;                         \
228             }                                         \
229             *(type *)(toVal->addr) = (value);         \
230         } else {                                      \
231             static type static_val;                   \
232             static_val = (value);                     \
233             toVal->addr = (genericptr_t) &static_val; \
234         }                                             \
235         toVal->size = sizeof(type);                   \
236         return True;                                  \
237     }
238
239 /*
240  * Find a color that approximates the color named in "str".  The "str" color
241  * may be a color name ("red") or number ("#7f0000").  If str == NULL, then
242  * "color" is assumed to contain the RGB color wanted.
243  * The approximate color found is returned in color as well.
244  * Return True if something close was found.
245  */
246 Boolean
247 nhApproxColor(screen, colormap, str, color)
248 Screen *screen;    /* screen to use */
249 Colormap colormap; /* the colormap to use */
250 char *str;         /* color name */
251 XColor *color;     /* the X color structure; changed only if successful */
252 {
253     int ncells;
254     long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
255     XColor tmp;
256     static XColor *table = 0;
257     register int i, j;
258     register long tdiff;
259
260     /* if the screen doesn't have a big colormap, don't waste our time */
261     /* or if it's huge, and _some_ match should have been possible */
262     if ((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
263         return False;
264
265     if (str != (char *) 0) {
266         if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
267             return False;
268     } else {
269         tmp = *color;
270         tmp.flags = 7; /* force to use all 3 of RGB */
271     }
272
273     if (!table) {
274         table = (XColor *) XtCalloc(ncells, sizeof(XColor));
275         for (i = 0; i < ncells; i++)
276             table[i].pixel = i;
277         XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
278     }
279
280 /* go thru cells and look for the one with smallest diff */
281 /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
282 /* a more knowledgeable color person might improve this -dlc */
283 try_again:
284     for (i = 0; i < ncells; i++) {
285         if (table[i].flags == tmp.flags) {
286             j = (int) table[i].red - (int) tmp.red;
287             if (j < 0)
288                 j = -j;
289             tdiff = j;
290             j = (int) table[i].green - (int) tmp.green;
291             if (j < 0)
292                 j = -j;
293             tdiff += j;
294             j = (int) table[i].blue - (int) tmp.blue;
295             if (j < 0)
296                 j = -j;
297             tdiff += j;
298             if (tdiff < cdiff) {
299                 cdiff = tdiff;
300                 tmp.pixel = i; /* table[i].pixel == i */
301             }
302         }
303     }
304
305     if (cdiff == 16777216)
306         return False; /* nothing found?! */
307
308     /*
309      * Found something.  Return it and mark this color as used to avoid
310      * reuse.  Reuse causes major contrast problems :-)
311      */
312     *color = table[tmp.pixel];
313     table[tmp.pixel].flags = 0;
314     /* try to alloc the color, so no one else can change it */
315     if (!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
316         cdiff = 16777216;
317         goto try_again;
318     }
319     return True;
320 }
321
322 Boolean
323 nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
324 Display *dpy;
325 XrmValuePtr args;
326 Cardinal *num_args;
327 XrmValuePtr fromVal;
328 XrmValuePtr toVal;
329 XtPointer *closure_ret;
330 {
331     String str = (String) fromVal->addr;
332     XColor screenColor;
333     XColor exactColor;
334     Screen *screen;
335     XtAppContext app = XtDisplayToApplicationContext(dpy);
336     Colormap colormap;
337     Status status;
338     String params[1];
339     Cardinal num_params = 1;
340
341     if (*num_args != 2) {
342         XtAppWarningMsg(
343             app, "wrongParameters", "cvtStringToPixel", "XtToolkitError",
344             "String to pixel conversion needs screen and colormap arguments",
345             (String *) 0, (Cardinal *) 0);
346         return False;
347     }
348
349     screen = *((Screen **) args[0].addr);
350     colormap = *((Colormap *) args[1].addr);
351
352 /* If Xt colors, use the Xt routine and hope for the best */
353 #if (XtSpecificationRelease >= 5)
354     if ((strcmpi(str, XtDefaultBackground) == 0)
355         || (strcmpi(str, XtDefaultForeground) == 0)) {
356         return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal,
357                                   closure_ret);
358     }
359 #else
360     if (strcmpi(str, XtDefaultBackground) == 0) {
361         *closure_ret = (char *) False;
362         done(Pixel, WhitePixelOfScreen(screen));
363     }
364     if (strcmpi(str, XtDefaultForeground) == 0) {
365         *closure_ret = (char *) False;
366         done(Pixel, BlackPixelOfScreen(screen));
367     }
368 #endif
369
370     status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char *) str,
371                               &screenColor, &exactColor);
372     if (status == 0) {
373         String msg, type;
374
375         /* some versions of XAllocNamedColor don't allow #xxyyzz names */
376         if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap,
377                                          str, &exactColor)
378             && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
379             *closure_ret = (char *) True;
380             done(Pixel, exactColor.pixel);
381         }
382
383         params[0] = str;
384         /* Server returns a specific error code but Xlib discards it.  Ugh */
385         if (XLookupColor(DisplayOfScreen(screen), colormap, (char *) str,
386                          &exactColor, &screenColor)) {
387             /* try to find another color that will do */
388             if (nhApproxColor(screen, colormap, (char *) str, &screenColor)) {
389                 *closure_ret = (char *) True;
390                 done(Pixel, screenColor.pixel);
391             }
392             type = nhStr("noColormap");
393             msg = nhStr("Cannot allocate colormap entry for \"%s\"");
394         } else {
395             /* some versions of XLookupColor also don't allow #xxyyzz names */
396             if (str[0] == '#'
397                 && (nhApproxColor(screen, colormap, (char *) str,
398                                   &screenColor))) {
399                 *closure_ret = (char *) True;
400                 done(Pixel, screenColor.pixel);
401             }
402             type = nhStr("badValue");
403             msg = nhStr("Color name \"%s\" is not defined");
404         }
405
406         XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg,
407                         params, &num_params);
408         *closure_ret = False;
409         return False;
410     } else {
411         *closure_ret = (char *) True;
412         done(Pixel, screenColor.pixel);
413     }
414 }
415
416 /* ARGSUSED */
417 static void
418 nhFreePixel(app, toVal, closure, args, num_args)
419 XtAppContext app;
420 XrmValuePtr toVal;
421 XtPointer closure;
422 XrmValuePtr args;
423 Cardinal *num_args;
424 {
425     Screen *screen;
426     Colormap colormap;
427
428     if (*num_args != 2) {
429         XtAppWarningMsg(
430             app, "wrongParameters", "freePixel", "XtToolkitError",
431             "Freeing a pixel requires screen and colormap arguments",
432             (String *) 0, (Cardinal *) 0);
433         return;
434     }
435
436     screen = *((Screen **) args[0].addr);
437     colormap = *((Colormap *) args[1].addr);
438
439     if (closure) {
440         XFreeColors(DisplayOfScreen(screen), colormap,
441                     (unsigned long *) toVal->addr, 1, (unsigned long) 0);
442     }
443 }
444
445 /* [ALI] Utility function to ask Xaw for font height, since the previous
446  * assumption of ascent + descent is not always valid.
447  */
448 Dimension nhFontHeight(w) Widget w;
449 #ifdef _XawTextSink_h
450 {
451     Widget sink;
452     XawTextPosition pos = 0;
453     int resWidth, resHeight;
454     Arg args[1];
455
456     XtSetArg(args[0], XtNtextSink, &sink);
457     XtGetValues(w, args, 1);
458
459     XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
460     return resHeight;
461 }
462 #else
463 {
464     XFontStruct *fs;
465     Arg args[1];
466
467     XtSetArg(args[0], XtNfont, &fs);
468     XtGetValues(w, args, 1);
469
470     /* Assume font height is ascent + descent. */
471     return = fs->ascent + fs->descent;
472 }
473 #endif
474
475 /* Global Functions ========================================================
476  */
477 void
478 X11_raw_print(str)
479 const char *str;
480 {
481     (void) puts(str);
482 }
483
484 void
485 X11_raw_print_bold(str)
486 const char *str;
487 {
488     (void) puts(str);
489 }
490
491 void
492 X11_curs(window, x, y)
493 winid window;
494 int x, y;
495 {
496     check_winid(window);
497
498     if (x < 0 || x >= COLNO) {
499         impossible("curs:  bad x value [%d]", x);
500         x = 0;
501     }
502     if (y < 0 || y >= ROWNO) {
503         impossible("curs:  bad y value [%d]", y);
504         y = 0;
505     }
506
507     window_list[window].cursx = x;
508     window_list[window].cursy = y;
509 }
510
511 void
512 X11_putstr(window, attr, str)
513 winid window;
514 int attr;
515 const char *str;
516 {
517     winid new_win;
518     struct xwindow *wp;
519
520     check_winid(window);
521     wp = &window_list[window];
522
523     switch (wp->type) {
524     case NHW_MESSAGE:
525         (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
526         toplines[TBUFSZ - 1] = 0;
527         append_message(wp, str);
528         break;
529     case NHW_STATUS:
530         adjust_status(wp, str);
531         break;
532     case NHW_MAP:
533         impossible("putstr: called on map window \"%s\"", str);
534         break;
535     case NHW_MENU:
536         if (wp->menu_information->is_menu) {
537             impossible("putstr:  called on a menu window, \"%s\" discarded",
538                        str);
539             break;
540         }
541         /*
542          * Change this menu window into a text window by creating a
543          * new text window, then copying it to this winid.
544          */
545         new_win = X11_create_nhwindow(NHW_TEXT);
546         X11_destroy_nhwindow(window);
547         *wp = window_list[new_win];
548         window_list[new_win].type = NHW_NONE; /* allow re-use */
549     /* fall though to add text */
550     case NHW_TEXT:
551         add_to_text_window(wp, attr, str);
552         break;
553     default:
554         impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str);
555     }
556 }
557
558 /* We do event processing as a callback, so this is a null routine. */
559 void
560 X11_get_nh_event()
561 {
562     return;
563 }
564
565 int
566 X11_nhgetch()
567 {
568     return input_event(EXIT_ON_KEY_PRESS);
569 }
570
571 int
572 X11_nh_poskey(x, y, mod)
573 int *x, *y, *mod;
574 {
575     int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
576
577     if (val == 0) { /* user clicked on a map window */
578         *x = click_x;
579         *y = click_y;
580         *mod = click_button;
581     }
582     return val;
583 }
584
585 winid
586 X11_create_nhwindow(type)
587 int type;
588 {
589     winid window;
590     struct xwindow *wp;
591
592     if (!x_inited)
593         panic("create_nhwindow:  windows not initialized");
594
595 #ifdef X11_HANGUP_SIGNAL
596     /* set up our own signal handlers on the first call.  Can't do this in
597      * X11_init_nhwindows because unixmain sets its handler after calling
598      * all the init routines. */
599     if (X11_sig_id == 0) {
600         X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer) 0);
601 #ifdef SA_RESTART
602         {
603             struct sigaction sact;
604
605             (void) memset((char *) &sact, 0, sizeof(struct sigaction));
606             sact.sa_handler = (SIG_RET_TYPE) X11_sig;
607             (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
608 #ifdef SIGXCPU
609             (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
610 #endif
611         }
612 #else
613         (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
614 #ifdef SIGXCPU
615         (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
616 #endif
617 #endif
618     }
619 #endif
620
621     /*
622      * We have already created the standard message, map, and status
623      * windows in the window init routine.  The first window of that
624      * type to be created becomes the standard.
625      *
626      * A better way to do this would be to say that init_nhwindows()
627      * has already defined these three windows.
628      */
629     if (type == NHW_MAP && map_win != WIN_ERR) {
630         window = map_win;
631         map_win = WIN_ERR;
632         return window;
633     }
634     if (type == NHW_MESSAGE && message_win != WIN_ERR) {
635         window = message_win;
636         message_win = WIN_ERR;
637         return window;
638     }
639     if (type == NHW_STATUS && status_win != WIN_ERR) {
640         window = status_win;
641         status_win = WIN_ERR;
642         return window;
643     }
644
645     window = find_free_window();
646     wp = &window_list[window];
647
648     /* The create routines will set type, popup, w, and Win_info. */
649     wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width =
650         wp->pixel_height = 0;
651     wp->keep_window = FALSE;
652
653     switch (type) {
654     case NHW_MAP:
655         create_map_window(wp, TRUE, (Widget) 0);
656         break;
657     case NHW_MESSAGE:
658         create_message_window(wp, TRUE, (Widget) 0);
659         break;
660     case NHW_STATUS:
661         create_status_window(wp, TRUE, (Widget) 0);
662         break;
663     case NHW_MENU:
664         create_menu_window(wp);
665         break;
666     case NHW_TEXT:
667         create_text_window(wp);
668         break;
669     default:
670         panic("create_nhwindow: unknown type [%d]", type);
671         break;
672     }
673     return window;
674 }
675
676 void
677 X11_clear_nhwindow(window)
678 winid window;
679 {
680     struct xwindow *wp;
681
682     check_winid(window);
683     wp = &window_list[window];
684
685     switch (wp->type) {
686     case NHW_MAP:
687         clear_map_window(wp);
688         break;
689     case NHW_TEXT:
690         clear_text_window(wp);
691         break;
692     case NHW_STATUS:
693     case NHW_MENU:
694     case NHW_MESSAGE:
695         /* do nothing for these window types */
696         break;
697     default:
698         panic("clear_nhwindow: unknown window type [%d]", wp->type);
699         break;
700     }
701 }
702
703 void
704 X11_display_nhwindow(window, blocking)
705 winid window;
706 boolean blocking;
707 {
708     struct xwindow *wp;
709
710     check_winid(window);
711     wp = &window_list[window];
712
713     switch (wp->type) {
714     case NHW_MAP:
715         if (wp->popup)
716             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
717         display_map_window(wp); /* flush map */
718                                 /*
719                                  * We need to flush the message window here due to the way the tty
720                                  * port is set up.  To flush a window, you need to call this
721                                  * routine.  However, the tty port _pauses_ with a --more-- if we
722                                  * do a display_nhwindow(WIN_MESSAGE, FALSE).  Thus, we can't call
723                                  * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
724                                  * get a --more-- after every line.
725                                  *
726                                  * Perhaps the window document should mention that when the map
727                                  * is flushed, everything on the three main windows should be
728                                  * flushed.  Note: we don't need to flush the status window
729                                  * because we don't buffer changes.
730                                  */
731         if (WIN_MESSAGE != WIN_ERR)
732             display_message_window(&window_list[WIN_MESSAGE]);
733         if (blocking)
734             (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
735         break;
736     case NHW_MESSAGE:
737         if (wp->popup)
738             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
739         display_message_window(wp); /* flush messages */
740         break;
741     case NHW_STATUS:
742         if (wp->popup)
743             nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
744         break; /* no flushing necessary */
745     case NHW_MENU: {
746         int n;
747         menu_item *selected;
748
749         /* pop up menu */
750         n = X11_select_menu(window, PICK_NONE, &selected);
751         if (n) {
752             impossible("perminvent: %d selected??", n);
753             free((genericptr_t) selected);
754         }
755         break;
756     }
757     case NHW_TEXT:
758         display_text_window(wp, blocking); /* pop up text window */
759         break;
760     default:
761         panic("display_nhwindow: unknown window type [%d]", wp->type);
762         break;
763     }
764 }
765
766 void
767 X11_destroy_nhwindow(window)
768 winid window;
769 {
770     struct xwindow *wp;
771
772     check_winid(window);
773     wp = &window_list[window];
774     /*
775      * "Zap" known windows, but don't destroy them.  We need to keep the
776      * toplevel widget popped up so that later windows (e.g. tombstone)
777      * are visible on DECWindow systems.  This is due to the virtual
778      * roots that the DECWindow wm creates.
779      */
780     if (window == WIN_MESSAGE) {
781         wp->keep_window = TRUE;
782         WIN_MESSAGE = WIN_ERR;
783         iflags.window_inited = 0;
784     } else if (window == WIN_MAP) {
785         wp->keep_window = TRUE;
786         WIN_MAP = WIN_ERR;
787     } else if (window == WIN_STATUS) {
788         wp->keep_window = TRUE;
789         WIN_STATUS = WIN_ERR;
790     } else if (window == WIN_INVEN) {
791         /* don't need to keep this one */
792         WIN_INVEN = WIN_ERR;
793     }
794
795     switch (wp->type) {
796     case NHW_MAP:
797         destroy_map_window(wp);
798         break;
799     case NHW_MENU:
800         destroy_menu_window(wp);
801         break;
802     case NHW_TEXT:
803         destroy_text_window(wp);
804         break;
805     case NHW_STATUS:
806         destroy_status_window(wp);
807         break;
808     case NHW_MESSAGE:
809         destroy_message_window(wp);
810         break;
811     default:
812         panic("destroy_nhwindow: unknown window type [%d]", wp->type);
813         break;
814     }
815 }
816
817 void
818 X11_update_inventory()
819 {
820     if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
821         updated_inventory = 1; /* hack to avoid mapping&raising window */
822         (void) display_inventory((char *) 0, FALSE);
823         updated_inventory = 0;
824     }
825 }
826
827 /* The current implementation has all of the saved lines on the screen. */
828 int
829 X11_doprev_message()
830 {
831     return 0;
832 }
833
834 void
835 X11_nhbell()
836 {
837     /* We can't use XBell until toplevel has been initialized. */
838     if (x_inited)
839         XBell(XtDisplay(toplevel), 0);
840     /* else print ^G ?? */
841 }
842
843 void
844 X11_mark_synch()
845 {
846     if (x_inited) {
847         /*
848          * The window document is unclear about the status of text
849          * that has been pline()d but not displayed w/display_nhwindow().
850          * Both the main and tty code assume that a pline() followed
851          * by mark_synch() results in the text being seen, even if
852          * display_nhwindow() wasn't called.  Duplicate this behavior.
853          */
854         if (WIN_MESSAGE != WIN_ERR)
855             display_message_window(&window_list[WIN_MESSAGE]);
856         XSync(XtDisplay(toplevel), False);
857     }
858 }
859
860 void
861 X11_wait_synch()
862 {
863     if (x_inited)
864         XFlush(XtDisplay(toplevel));
865 }
866
867 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
868 void
869 X11_resume_nhwindows()
870 {
871     return;
872 }
873 /* ARGSUSED */
874 void
875 X11_suspend_nhwindows(str)
876 const char *str;
877 {
878     nhUse(str);
879
880     return;
881 }
882
883 /* Under X, we don't need to initialize the number pad. */
884 /* ARGSUSED */
885 void X11_number_pad(state) /* called from options.c */
886 int state;
887 {
888     nhUse(state);
889
890     return;
891 }
892
893 void
894 X11_start_screen()
895 {
896     return;
897 } /* called from setftty() in unixtty.c */
898 void
899 X11_end_screen()
900 {
901     return;
902 } /* called from settty() in unixtty.c */
903
904 #ifdef GRAPHIC_TOMBSTONE
905 void
906 X11_outrip(window, how, when)
907 winid window;
908 int how;
909 time_t when;
910 {
911     struct xwindow *wp;
912
913     check_winid(window);
914     wp = &window_list[window];
915
916     if (wp->type == NHW_TEXT) {
917         wp->text_information->is_rip = TRUE;
918     } else {
919         panic("ripout on non-text window (window type [%d])", wp->type);
920     }
921
922     calculate_rip_text(how, when);
923 }
924 #endif
925
926 /* init and exit nhwindows -------------------------------------------------
927  */
928
929 XtAppContext app_context;     /* context of application */
930 Widget toplevel = (Widget) 0; /* toplevel widget */
931 Atom wm_delete_window;        /* pop down windows */
932
933 static XtActionsRec actions[] = {
934     { nhStr("dismiss_file"), dismiss_file }, /* file viewing widget */
935     { nhStr("delete_file"), delete_file },   /* file delete-window */
936     { nhStr("dismiss_text"), dismiss_text }, /* text widget button action */
937     { nhStr("delete_text"), delete_text },   /* text widget delete action */
938     { nhStr("key_dismiss_text"),
939       key_dismiss_text }, /* text widget key action */
940 #ifdef GRAPHIC_TOMBSTONE
941     { nhStr("rip_dismiss_text"), rip_dismiss_text }, /* rip in text widget */
942 #endif
943     { nhStr("menu_key"), menu_key },             /* menu accelerators */
944     { nhStr("yn_key"), yn_key },                 /* yn accelerators */
945     { nhStr("yn_delete"), yn_delete },           /* yn delete-window */
946     { nhStr("askname_delete"), askname_delete }, /* askname delete-window */
947     { nhStr("getline_delete"), getline_delete }, /* getline delete-window */
948     { nhStr("menu_delete"), menu_delete },       /* menu delete-window */
949     { nhStr("ec_key"), ec_key },                 /* extended commands */
950     { nhStr("ec_delete"), ec_delete },           /* ext-com menu delete */
951     { nhStr("ps_key"), ps_key },                 /* player selection */
952     { nhStr("race_key"), race_key },             /* race selection */
953     { nhStr("gend_key"), gend_key },             /* gender selection */
954     { nhStr("algn_key"), algn_key },             /* alignment selection */
955     { nhStr("X11_hangup"), X11_hangup },         /* delete of top-level */
956     { nhStr("input"), map_input },               /* key input */
957     { nhStr("scroll"), nh_keyscroll },           /* scrolling by keys */
958 };
959
960 static XtResource resources[] = {
961     { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean),
962       XtOffset(AppResources *, slow), XtRString, nhStr("True") },
963     { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean),
964       XtOffset(AppResources *, autofocus), XtRString, nhStr("False") },
965     { nhStr("message_line"), nhStr("Message_line"), XtRBoolean,
966       sizeof(Boolean), XtOffset(AppResources *, message_line), XtRString,
967       nhStr("False") },
968     { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean,
969       sizeof(Boolean), XtOffset(AppResources *, double_tile_size), XtRString,
970       nhStr("False") },
971     { nhStr("tile_file"), nhStr("Tile_file"), XtRString, sizeof(String),
972       XtOffset(AppResources *, tile_file), XtRString, nhStr("x11tiles") },
973     { nhStr("icon"), nhStr("Icon"), XtRString, sizeof(String),
974       XtOffset(AppResources *, icon), XtRString, nhStr("nh72") },
975     { nhStr("message_lines"), nhStr("Message_lines"), XtRInt, sizeof(int),
976       XtOffset(AppResources *, message_lines), XtRString, nhStr("12") },
977     { nhStr("pet_mark_bitmap"), nhStr("Pet_mark_bitmap"), XtRString,
978       sizeof(String), XtOffset(AppResources *, pet_mark_bitmap), XtRString,
979       nhStr("pet_mark.xbm") },
980     { nhStr("pet_mark_color"), nhStr("Pet_mark_color"), XtRPixel,
981       sizeof(XtRPixel), XtOffset(AppResources *, pet_mark_color), XtRString,
982       nhStr("Red") },
983     { nhStr("pilemark_bitmap"), nhStr("Pilemark_bitmap"), XtRString,
984       sizeof(String), XtOffset(AppResources *, pilemark_bitmap), XtRString,
985       nhStr("pilemark.xbm") },
986     { nhStr("pilemark_color"), nhStr("Pilemark_color"), XtRPixel,
987       sizeof(XtRPixel), XtOffset(AppResources *, pilemark_color), XtRString,
988       nhStr("Green") },
989 #ifdef GRAPHIC_TOMBSTONE
990     { nhStr("tombstone"), "Tombstone", XtRString, sizeof(String),
991       XtOffset(AppResources *, tombstone), XtRString, "rip.xpm" },
992     { nhStr("tombtext_x"), "Tombtext_x", XtRInt, sizeof(int),
993       XtOffset(AppResources *, tombtext_x), XtRString, "155" },
994     { nhStr("tombtext_y"), "Tombtext_y", XtRInt, sizeof(int),
995       XtOffset(AppResources *, tombtext_y), XtRString, "78" },
996     { nhStr("tombtext_dx"), "Tombtext_dx", XtRInt, sizeof(int),
997       XtOffset(AppResources *, tombtext_dx), XtRString, "0" },
998     { nhStr("tombtext_dy"), "Tombtext_dy", XtRInt, sizeof(int),
999       XtOffset(AppResources *, tombtext_dy), XtRString, "13" },
1000 #endif
1001 };
1002
1003 void
1004 X11_init_nhwindows(argcp, argv)
1005 int *argcp;
1006 char **argv;
1007 {
1008     int i;
1009     Cardinal num_args;
1010     Arg args[4];
1011     uid_t savuid;
1012
1013     /* Init windows to nothing. */
1014     for (i = 0; i < MAX_WINDOWS; i++)
1015         window_list[i].type = NHW_NONE;
1016
1017     /* add another option that can be set */
1018     set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
1019
1020     /*
1021      * setuid hack: make sure that if nethack is setuid, to use real uid
1022      * when opening X11 connections, in case the user is using xauth, since
1023      * the "games" or whatever user probably doesn't have permission to open
1024      * a window on the user's display.  This code is harmless if the binary
1025      * is not installed setuid.  See include/system.h on compilation failures.
1026      */
1027     savuid = geteuid();
1028     (void) seteuid(getuid());
1029
1030     XSetIOErrorHandler((XIOErrorHandler) hangup);
1031
1032     num_args = 0;
1033     XtSetArg(args[num_args], XtNallowShellResize, True);
1034     num_args++;
1035     toplevel =
1036         XtAppInitialize(&app_context, "NetHack",  /* application class */
1037                         (XrmOptionDescList) 0, 0, /* options list */
1038                         argcp, (String *) argv,   /* command line args */
1039                         (String *) 0,             /* fallback resources */
1040                         (ArgList) args, num_args);
1041     XtOverrideTranslations(
1042         toplevel,
1043         XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
1044
1045 /* We don't need to realize the top level widget. */
1046
1047 #ifdef TEXTCOLOR
1048     /* add new color converter to deal with overused colormaps */
1049     XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
1050                        (XtConvertArgList) nhcolorConvertArgs,
1051                        XtNumber(nhcolorConvertArgs), XtCacheByDisplay,
1052                        nhFreePixel);
1053 #endif /* TEXTCOLOR */
1054
1055     /* Register the actions mentioned in "actions". */
1056     XtAppAddActions(app_context, actions, XtNumber(actions));
1057
1058     /* Get application-wide resources */
1059     XtGetApplicationResources(toplevel, (XtPointer) &appResources, resources,
1060                               XtNumber(resources), (ArgList) 0, ZERO);
1061
1062     /* Initialize other things. */
1063     init_standard_windows();
1064
1065     /* Give the window manager an icon to use;  toplevel must be realized. */
1066     if (appResources.icon && *appResources.icon) {
1067         struct icon_info *ip;
1068
1069         for (ip = icon_data; ip->name; ip++)
1070             if (!strcmp(appResources.icon, ip->name)) {
1071                 icon_pixmap = XCreateBitmapFromData(
1072                     XtDisplay(toplevel), XtWindow(toplevel),
1073                     (genericptr_t) ip->bits, ip->width, ip->height);
1074                 if (icon_pixmap != None) {
1075                     XWMHints hints;
1076
1077                     (void) memset((genericptr_t) &hints, 0, sizeof(XWMHints));
1078                     hints.flags = IconPixmapHint;
1079                     hints.icon_pixmap = icon_pixmap;
1080                     XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel),
1081                                 &hints);
1082                 }
1083                 break;
1084             }
1085     }
1086
1087     /* end of setuid hack: reset uid back to the "games" uid */
1088     (void) seteuid(savuid);
1089
1090     x_inited = TRUE; /* X is now initialized */
1091
1092     /* Display the startup banner in the message window. */
1093     for (i = 1; i <= 4 + 2; ++i) /* (values beyond 4 yield blank lines) */
1094         X11_putstr(WIN_MESSAGE, 0, copyright_banner_line(i));
1095 }
1096
1097 /*
1098  * All done.
1099  */
1100 /* ARGSUSED */
1101 void
1102 X11_exit_nhwindows(dummy)
1103 const char *dummy;
1104 {
1105     extern Pixmap tile_pixmap; /* from winmap.c */
1106
1107     nhUse(dummy);
1108
1109     /* explicitly free the icon and tile pixmaps */
1110     if (icon_pixmap != None) {
1111         XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1112         icon_pixmap = None;
1113     }
1114     if (tile_pixmap != None) {
1115         XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1116         tile_pixmap = None;
1117     }
1118     if (WIN_INVEN != WIN_ERR)
1119         X11_destroy_nhwindow(WIN_INVEN);
1120     if (WIN_STATUS != WIN_ERR)
1121         X11_destroy_nhwindow(WIN_STATUS);
1122     if (WIN_MAP != WIN_ERR)
1123         X11_destroy_nhwindow(WIN_MAP);
1124     if (WIN_MESSAGE != WIN_ERR)
1125         X11_destroy_nhwindow(WIN_MESSAGE);
1126 }
1127
1128 #ifdef X11_HANGUP_SIGNAL
1129 static void X11_sig(sig) /* Unix signal handler */
1130 int sig;
1131 {
1132     XtNoticeSignal(X11_sig_id);
1133     hangup(sig);
1134 }
1135
1136 static void
1137 X11_sig_cb(not_used, id)
1138 XtPointer not_used;
1139 XtSignalId *id;
1140 {
1141     XEvent event;
1142     XClientMessageEvent *mesg;
1143
1144     nhUse(not_used);
1145     nhUse(id);
1146
1147     /* Set up a fake message to the event handler. */
1148     mesg = (XClientMessageEvent *) &event;
1149     mesg->type = ClientMessage;
1150     mesg->message_type = XA_STRING;
1151     mesg->format = 8;
1152
1153     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1154                XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1155                (XEvent *) mesg);
1156 }
1157 #endif
1158
1159 /* delay_output ------------------------------------------------------------
1160  */
1161
1162 /*
1163  * Timeout callback for delay_output().  Send a fake message to the map
1164  * window.
1165  */
1166 /* ARGSUSED */
1167 static void
1168 d_timeout(client_data, id)
1169 XtPointer client_data;
1170 XtIntervalId *id;
1171 {
1172     XEvent event;
1173     XClientMessageEvent *mesg;
1174
1175     nhUse(client_data);
1176     nhUse(id);
1177
1178     /* Set up a fake message to the event handler. */
1179     mesg = (XClientMessageEvent *) &event;
1180     mesg->type = ClientMessage;
1181     mesg->message_type = XA_STRING;
1182     mesg->format = 8;
1183     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1184                XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1185                (XEvent *) mesg);
1186 }
1187
1188 /*
1189  * Delay for 50ms.  This is not implemented asynch.  Maybe later.
1190  * Start the timeout, then wait in the event loop.  The timeout
1191  * function will send an event to the map window which will be waiting
1192  * for a sent event.
1193  */
1194 void
1195 X11_delay_output()
1196 {
1197     if (!x_inited)
1198         return;
1199
1200     (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1201
1202     /* The timeout function will enable the event loop exit. */
1203     (void) x_event(EXIT_ON_SENT_EVENT);
1204 }
1205
1206 /* X11_hangup --------------------------------------------------------------
1207  */
1208 /* ARGSUSED */
1209 static void
1210 X11_hangup(w, event, params, num_params)
1211 Widget w;
1212 XEvent *event;
1213 String *params;
1214 Cardinal *num_params;
1215 {
1216     nhUse(w);
1217     nhUse(event);
1218     nhUse(params);
1219     nhUse(num_params);
1220
1221     hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1222     exit_x_event = TRUE;
1223 }
1224
1225 /* askname -----------------------------------------------------------------
1226  */
1227 /* ARGSUSED */
1228 static void
1229 askname_delete(w, event, params, num_params)
1230 Widget w;
1231 XEvent *event;
1232 String *params;
1233 Cardinal *num_params;
1234 {
1235     nhUse(event);
1236     nhUse(params);
1237     nhUse(num_params);
1238
1239     nh_XtPopdown(w);
1240     (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1241     exit_x_event = TRUE;
1242 }
1243
1244 /* Callback for askname dialog widget. */
1245 /* ARGSUSED */
1246 static void
1247 askname_done(w, client_data, call_data)
1248 Widget w;
1249 XtPointer client_data;
1250 XtPointer call_data;
1251 {
1252     int len;
1253     char *s;
1254     Widget dialog = (Widget) client_data;
1255
1256     nhUse(w);
1257     nhUse(call_data);
1258
1259     s = (char *) GetDialogResponse(dialog);
1260
1261     len = (int) strlen(s);
1262     if (len == 0) {
1263         X11_nhbell();
1264         return;
1265     }
1266
1267     /* Truncate name if necessary */
1268     if (len >= (int) sizeof(plname) - 1)
1269         len = (int) sizeof(plname) - 1;
1270
1271     (void) strncpy(plname, s, len);
1272     plname[len] = '\0';
1273     XtFree(s);
1274
1275     nh_XtPopdown(XtParent(dialog));
1276     exit_x_event = TRUE;
1277 }
1278
1279 void
1280 X11_askname()
1281 {
1282     Widget popup, dialog;
1283     Arg args[1];
1284
1285     XtSetArg(args[0], XtNallowShellResize, True);
1286
1287     popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel,
1288                                args, ONE);
1289     XtOverrideTranslations(
1290         popup,
1291         XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1292
1293     dialog = CreateDialog(popup, nhStr("dialog"), askname_done,
1294                           (XtCallbackProc) 0);
1295
1296     SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */
1297     SetDialogResponse(dialog, nhStr("")); /* set default answer */
1298
1299     XtRealizeWidget(popup);
1300     positionpopup(popup, TRUE); /* center,bottom */
1301
1302     nh_XtPopup(popup, (int) XtGrabExclusive, dialog);
1303
1304     /* The callback will enable the event loop exit. */
1305     (void) x_event(EXIT_ON_EXIT);
1306 }
1307
1308 /* getline -----------------------------------------------------------------
1309  */
1310 /* This uses Tim Theisen's dialog widget set (from GhostView). */
1311
1312 static Widget getline_popup, getline_dialog;
1313
1314 #define CANCEL_STR "\033"
1315 static char *getline_input;
1316
1317 /* Callback for getline dialog widget. */
1318 /* ARGSUSED */
1319 static void
1320 done_button(w, client_data, call_data)
1321 Widget w;
1322 XtPointer client_data;
1323 XtPointer call_data;
1324 {
1325     int len;
1326     char *s;
1327     Widget dialog = (Widget) client_data;
1328
1329     nhUse(w);
1330     nhUse(call_data);
1331
1332     s = (char *) GetDialogResponse(dialog);
1333     len = strlen(s);
1334
1335     /* Truncate input if necessary */
1336     if (len >= BUFSZ)
1337         len = BUFSZ - 1;
1338
1339     (void) strncpy(getline_input, s, len);
1340     getline_input[len] = '\0';
1341     XtFree(s);
1342
1343     nh_XtPopdown(XtParent(dialog));
1344     exit_x_event = TRUE;
1345 }
1346
1347 /* ARGSUSED */
1348 static void
1349 getline_delete(w, event, params, num_params)
1350 Widget w;
1351 XEvent *event;
1352 String *params;
1353 Cardinal *num_params;
1354 {
1355     nhUse(event);
1356     nhUse(params);
1357     nhUse(num_params);
1358
1359     Strcpy(getline_input, CANCEL_STR);
1360     nh_XtPopdown(w);
1361     exit_x_event = TRUE;
1362 }
1363
1364 /* Callback for getline dialog widget. */
1365 /* ARGSUSED */
1366 static void
1367 abort_button(w, client_data, call_data)
1368 Widget w;
1369 XtPointer client_data;
1370 XtPointer call_data;
1371 {
1372     Widget dialog = (Widget) client_data;
1373
1374     nhUse(w);
1375     nhUse(call_data);
1376
1377     Strcpy(getline_input, CANCEL_STR);
1378     nh_XtPopdown(XtParent(dialog));
1379     exit_x_event = TRUE;
1380 }
1381
1382 void
1383 X11_getlin(question, input)
1384 const char *question;
1385 char *input;
1386 {
1387     static boolean need_to_init = True;
1388
1389     getline_input = input;
1390
1391     flush_screen(1);
1392     if (need_to_init) {
1393         Arg args[1];
1394
1395         need_to_init = False;
1396
1397         XtSetArg(args[0], XtNallowShellResize, True);
1398
1399         getline_popup = XtCreatePopupShell(
1400             "getline", transientShellWidgetClass, toplevel, args, ONE);
1401         XtOverrideTranslations(
1402             getline_popup, XtParseTranslationTable(
1403                                "<Message>WM_PROTOCOLS: getline_delete()"));
1404
1405         getline_dialog = CreateDialog(getline_popup, nhStr("dialog"),
1406                                       done_button, abort_button);
1407
1408         XtRealizeWidget(getline_popup);
1409         XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1410                         &wm_delete_window, 1);
1411     }
1412     SetDialogPrompt(getline_dialog, (String) question); /* set prompt */
1413     SetDialogResponse(getline_dialog, nhStr("")); /* set default answer */
1414     positionpopup(getline_popup, TRUE);           /* center,bottom */
1415
1416     nh_XtPopup(getline_popup, (int) XtGrabExclusive, getline_dialog);
1417
1418     /* The callback will enable the event loop exit. */
1419     (void) x_event(EXIT_ON_EXIT);
1420 }
1421
1422 /* Display file ------------------------------------------------------------
1423  */
1424 static const char display_translations[] = "#override\n\
1425      <Key>q: dismiss_file()\n\
1426      <Key>Escape: dismiss_file()\n\
1427      <BtnDown>: dismiss_file()";
1428
1429 /* WM_DELETE_WINDOW callback for file dismissal. */
1430 /*ARGSUSED*/
1431 static void
1432 delete_file(w, event, params, num_params)
1433 Widget w;
1434 XEvent *event;
1435 String *params;
1436 Cardinal *num_params;
1437 {
1438     nhUse(event);
1439     nhUse(params);
1440     nhUse(num_params);
1441
1442     nh_XtPopdown(w);
1443     XtDestroyWidget(w);
1444 }
1445
1446 /* Callback for file dismissal. */
1447 /*ARGSUSED*/
1448 static void
1449 dismiss_file(w, event, params, num_params)
1450 Widget w;
1451 XEvent *event;
1452 String *params;
1453 Cardinal *num_params;
1454 {
1455     Widget popup = XtParent(w);
1456
1457     nhUse(event);
1458     nhUse(params);
1459     nhUse(num_params);
1460
1461     nh_XtPopdown(popup);
1462     XtDestroyWidget(popup);
1463 }
1464
1465 void
1466 X11_display_file(str, complain)
1467 const char *str;
1468 boolean complain;
1469 {
1470     dlb *fp;
1471     Arg args[12];
1472     Cardinal num_args;
1473     Widget popup, dispfile;
1474     Position top_margin, bottom_margin, left_margin, right_margin;
1475     XFontStruct *fs;
1476     int new_width, new_height;
1477 #define LLEN 128
1478     char line[LLEN];
1479     int num_lines;
1480     char *textlines;
1481     int charcount;
1482
1483     /* Use the port-independent file opener to see if the file exists. */
1484     fp = dlb_fopen(str, RDTMODE);
1485
1486     if (!fp) {
1487         if (complain)
1488             pline("Cannot open %s.  Sorry.", str);
1489
1490         return; /* it doesn't exist, ignore */
1491     }
1492
1493     /*
1494      * Count the number of lines and characters in the file.
1495      */
1496     num_lines = 0;
1497     charcount = 1;
1498     while (dlb_fgets(line, LLEN, fp)) {
1499         num_lines++;
1500         charcount += strlen(line);
1501     }
1502
1503     (void) dlb_fclose(fp);
1504
1505     /* Ignore empty files */
1506     if (num_lines == 0)
1507         return;
1508
1509     /* If over the max window size, truncate the window size to the max */
1510     if (num_lines >= DISPLAY_FILE_SIZE)
1511         num_lines = DISPLAY_FILE_SIZE;
1512
1513     /*
1514      * Re-open the file and read the data into a buffer.  Cannot use
1515      * the XawAsciiFile type of widget, because that is not DLB-aware.
1516      */
1517     textlines = (char *) alloc((unsigned int) charcount);
1518     textlines[0] = '\0';
1519
1520     fp = dlb_fopen(str, RDTMODE);
1521
1522     while (dlb_fgets(line, LLEN, fp)) {
1523         (void) strcat(textlines, line);
1524     }
1525
1526     (void) dlb_fclose(fp);
1527
1528     num_args = 0;
1529     XtSetArg(args[num_args], nhStr(XtNtitle), str);
1530     num_args++;
1531
1532     popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
1533                                toplevel, args, num_args);
1534     XtOverrideTranslations(
1535         popup,
1536         XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
1537
1538     num_args = 0;
1539     XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
1540              XawtextScrollWhenNeeded);
1541     num_args++;
1542     XtSetArg(args[num_args], nhStr(XtNscrollVertical), XawtextScrollAlways);
1543     num_args++;
1544     XtSetArg(args[num_args], nhStr(XtNtype), XawAsciiString);
1545     num_args++;
1546     XtSetArg(args[num_args], nhStr(XtNstring), textlines);
1547     num_args++;
1548     XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
1549     num_args++;
1550     XtSetArg(args[num_args], nhStr(XtNtranslations),
1551              XtParseTranslationTable(display_translations));
1552     num_args++;
1553
1554     dispfile =
1555         XtCreateManagedWidget("text",                      /* name */
1556                               asciiTextWidgetClass, popup, /* parent widget */
1557                               args,      /* set some values */
1558                               num_args); /* number of values to set */
1559
1560     /* Get font and border information. */
1561     num_args = 0;
1562     XtSetArg(args[num_args], nhStr(XtNfont), &fs);
1563     num_args++;
1564     XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
1565     num_args++;
1566     XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
1567     num_args++;
1568     XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
1569     num_args++;
1570     XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
1571     num_args++;
1572     XtGetValues(dispfile, args, num_args);
1573
1574     /*
1575      * The data files are currently set up assuming an 80 char wide window
1576      * and a fixed width font.  Soo..
1577      */
1578     new_height =
1579         num_lines * nhFontHeight(dispfile) + top_margin + bottom_margin;
1580     new_width = 80 * fs->max_bounds.width + left_margin + right_margin;
1581
1582     /* Set the new width and height. */
1583     num_args = 0;
1584     XtSetArg(args[num_args], XtNwidth, new_width);
1585     num_args++;
1586     XtSetArg(args[num_args], XtNheight, new_height);
1587     num_args++;
1588     XtSetValues(dispfile, args, num_args);
1589
1590     nh_XtPopup(popup, (int) XtGrabNone, (Widget) 0);
1591     free(textlines);
1592 }
1593
1594 /* yn_function -------------------------------------------------------------
1595  */
1596 /* (not threaded) */
1597
1598 static const char *yn_quitchars = " \n\r";
1599 static const char *yn_choices; /* string of acceptable input */
1600 static char yn_def;
1601 static char yn_return;           /* return value */
1602 static char yn_esc_map;          /* ESC maps to this char. */
1603 static Widget yn_popup;          /* popup for the yn fuction (created once) */
1604 static Widget yn_label;          /* label for yn function (created once) */
1605 static boolean yn_getting_num;   /* TRUE if accepting digits */
1606 static boolean yn_preserve_case; /* default is to force yn to lower case */
1607 static int yn_ndigits;           /* digit count */
1608 static long yn_val;              /* accumulated value */
1609
1610 static const char yn_translations[] = "#override\n\
1611      <Key>: yn_key()";
1612
1613 /*
1614  * Convert the given key event into a character.  If the key maps to
1615  * more than one character only the first is returned.  If there is
1616  * no conversion (i.e. just the CTRL key hit) a NUL is returned.
1617  */
1618 char
1619 key_event_to_char(key)
1620 XKeyEvent *key;
1621 {
1622     char keystring[MAX_KEY_STRING];
1623     int nbytes;
1624     boolean meta = !!(key->state & Mod1Mask);
1625
1626     nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0,
1627                            (XComposeStatus *) 0);
1628
1629     /* Modifier keys return a zero lengh string when pressed. */
1630     if (nbytes == 0)
1631         return '\0';
1632
1633     return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
1634 }
1635
1636 /*
1637  * Called when we get a WM_DELETE_WINDOW event on a yn window.
1638  */
1639 /* ARGSUSED */
1640 static void
1641 yn_delete(w, event, params, num_params)
1642 Widget w;
1643 XEvent *event;
1644 String *params;
1645 Cardinal *num_params;
1646 {
1647     nhUse(w);
1648     nhUse(event);
1649     nhUse(params);
1650     nhUse(num_params);
1651
1652     yn_getting_num = FALSE;
1653     /* Only use yn_esc_map if we have choices.  Otherwise, return ESC. */
1654     yn_return = yn_choices ? yn_esc_map : '\033';
1655     exit_x_event = TRUE; /* exit our event handler */
1656 }
1657
1658 /*
1659  * Called when we get a key press event on a yn window.
1660  */
1661 /* ARGSUSED */
1662 static void
1663 yn_key(w, event, params, num_params)
1664 Widget w;
1665 XEvent *event;
1666 String *params;
1667 Cardinal *num_params;
1668 {
1669     char ch;
1670
1671     if (appResources.slow && !input_func)
1672         map_input(w, event, params, num_params);
1673
1674     ch = key_event_to_char((XKeyEvent *) event);
1675
1676     if (ch == '\0') { /* don't accept nul char or modifier event */
1677         /* no bell */
1678         return;
1679     }
1680
1681     if (!yn_choices) { /* accept any input */
1682         yn_return = ch;
1683     } else {
1684         if (!yn_preserve_case)
1685             ch = lowc(ch); /* move to lower case */
1686
1687         if (ch == '\033') {
1688             yn_getting_num = FALSE;
1689             yn_return = yn_esc_map;
1690         } else if (index(yn_quitchars, ch)) {
1691             yn_return = yn_def;
1692         } else if (index(yn_choices, ch)) {
1693             if (ch == '#') {
1694                 if (yn_getting_num) { /* don't select again */
1695                     X11_nhbell();
1696                     return;
1697                 }
1698                 yn_getting_num = TRUE;
1699                 yn_ndigits = 0;
1700                 yn_val = 0;
1701                 return; /* wait for more input */
1702             }
1703             yn_return = ch;
1704             if (ch != 'y')
1705                 yn_getting_num = FALSE;
1706         } else {
1707             if (yn_getting_num) {
1708                 if (digit(ch)) {
1709                     yn_ndigits++;
1710                     yn_val = (yn_val * 10) + (long) (ch - '0');
1711                     return; /* wait for more input */
1712                 }
1713                 if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) {
1714                     yn_ndigits--;
1715                     yn_val = yn_val / 10;
1716                     return; /* wait for more input */
1717                 }
1718             }
1719             X11_nhbell(); /* no match */
1720             return;
1721         }
1722
1723         if (yn_getting_num) {
1724             yn_return = '#';
1725             if (yn_val < 0)
1726                 yn_val = 0;
1727             yn_number = yn_val; /* assign global */
1728         }
1729     }
1730     exit_x_event = TRUE; /* exit our event handler */
1731 }
1732
1733 char
1734 X11_yn_function(ques, choices, def)
1735 const char *ques;
1736 const char *choices;
1737 char def;
1738 {
1739     static Boolean need_to_init = True;
1740     char buf[BUFSZ];
1741     Arg args[4];
1742     Cardinal num_args;
1743
1744     yn_choices = choices; /* set up globals for callback to use */
1745     yn_def = def;
1746     yn_preserve_case =
1747         !choices; /* preserve when arbitrary response allowed */
1748
1749     /*
1750      * This is sort of a kludge.  There are quite a few places in the main
1751      * nethack code where a pline containing information is followed by a
1752      * call to yn_function().  There is no flush of the message window
1753      * (it is implicit in the tty window port), so the line never shows
1754      * up for us!  Solution: do our own flush.
1755      */
1756     if (WIN_MESSAGE != WIN_ERR)
1757         display_message_window(&window_list[WIN_MESSAGE]);
1758
1759     if (choices) {
1760         char *cb, choicebuf[QBUFSZ];
1761
1762         Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
1763         /* default when choices are present is to force yn answer to
1764            lowercase unless one or more choices are explicitly uppercase;
1765            check this before stripping the hidden choices */
1766         for (cb = choicebuf; *cb; ++cb)
1767             if ('A' <= *cb && *cb <= 'Z') {
1768                 yn_preserve_case = TRUE;
1769                 break;
1770             }
1771         if ((cb = index(choicebuf, '\033')) != 0)
1772             *cb = '\0';
1773         /* ques [choices] (def) */
1774         if ((int) (1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ)
1775             panic("X11_yn_function:  question too long");
1776         (void) strncpy(buf, ques, QBUFSZ - 1);
1777         buf[QBUFSZ - 1] = '\0';
1778         Sprintf(eos(buf), " [%s]", choicebuf);
1779         if (def)
1780             Sprintf(eos(buf), " (%c)", def);
1781         Strcat(buf, " ");
1782
1783         /* escape maps to 'q' or 'n' or default, in that order */
1784         yn_esc_map =
1785             (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1786     } else {
1787         if ((int) (1 + strlen(ques) + 1) >= BUFSZ)
1788             panic("X11_yn_function:  question too long");
1789         Strcpy(buf, ques);
1790         Strcat(buf, " ");
1791     }
1792
1793     if (!appResources.slow && need_to_init) {
1794         need_to_init = False;
1795
1796         XtSetArg(args[0], XtNallowShellResize, True);
1797         yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
1798                                       toplevel, args, ONE);
1799         XtOverrideTranslations(
1800             yn_popup,
1801             XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
1802
1803         num_args = 0;
1804         XtSetArg(args[num_args], XtNtranslations,
1805                  XtParseTranslationTable(yn_translations));
1806         num_args++;
1807         yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass,
1808                                          yn_popup, args, num_args);
1809
1810         XtRealizeWidget(yn_popup);
1811         XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
1812                         &wm_delete_window, 1);
1813     }
1814
1815     if (appResources.slow)
1816         input_func = yn_key;
1817
1818     num_args = 0;
1819     XtSetArg(args[num_args], XtNlabel, buf);
1820     num_args++;
1821     XtSetValues(yn_label, args, num_args);
1822
1823     if (!appResources.slow) {
1824         /*
1825          * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
1826          * need to set the label twice to get the size to change.
1827          */
1828         num_args = 0;
1829         XtSetArg(args[num_args], XtNlabel, buf);
1830         num_args++;
1831         XtSetValues(yn_label, args, num_args);
1832
1833         positionpopup(yn_popup, TRUE);
1834         nh_XtPopup(yn_popup, (int) XtGrabExclusive, yn_label);
1835     }
1836
1837     yn_getting_num = FALSE;
1838     (void) x_event(EXIT_ON_EXIT);
1839
1840     if (appResources.slow) {
1841         input_func = 0;
1842         num_args = 0;
1843         XtSetArg(args[num_args], XtNlabel, " ");
1844         num_args++;
1845         XtSetValues(yn_label, args, num_args);
1846     } else {
1847         nh_XtPopdown(yn_popup); /* this removes the event grab */
1848     }
1849
1850     return yn_return;
1851 }
1852
1853 /*ARGSUSED*/
1854 void
1855 X11_preference_update(pref)
1856 const char *pref;
1857 {
1858     if (!strcmp(pref, "tiled_map")) {
1859         if (WIN_MAP != WIN_ERR)
1860             display_map_window(&window_list[WIN_MAP]);
1861     }
1862 }
1863
1864 /* End global functions ====================================================
1865  */
1866
1867 /*
1868  * Before we wait for input via nhgetch() and nh_poskey(), we need to
1869  * do some pre-processing.
1870  */
1871 static int
1872 input_event(exit_condition)
1873 int exit_condition;
1874 {
1875     if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
1876         check_turn_events();
1877     if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
1878         check_cursor_visibility(&window_list[WIN_MAP]);
1879     if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
1880         set_last_pause(&window_list[WIN_MESSAGE]);
1881
1882     return x_event(exit_condition);
1883 }
1884
1885 /*ARGSUSED*/
1886 void
1887 msgkey(w, data, event)
1888 Widget w;
1889 XtPointer data;
1890 XEvent *event;
1891 {
1892     Cardinal num = 0;
1893
1894     nhUse(w);
1895     nhUse(data);
1896
1897     map_input(window_list[WIN_MAP].w, event, (String *) 0, &num);
1898 }
1899
1900 /*ARGSUSED*/
1901 static void win_visible(w, data, event, flag) /* only called for autofocus */
1902 Widget w;
1903 XtPointer data; /* client_data not used */
1904 XEvent *event;
1905 Boolean *flag; /* continue_to_dispatch flag not used */
1906 {
1907     XVisibilityEvent *vis_event = (XVisibilityEvent *) event;
1908
1909     nhUse(data);
1910     nhUse(flag);
1911
1912     if (vis_event->state != VisibilityFullyObscured) {
1913         /* one-time operation; cancel ourself */
1914         XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
1915                              win_visible, (XtPointer) 0);
1916         /* grab initial input focus */
1917         XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
1918     }
1919 }
1920
1921 /*
1922  * Set up the playing console.  This has three major parts:  the
1923  * message window, the map, and the status window.
1924  */
1925 static void
1926 init_standard_windows()
1927 {
1928     Widget form, message_viewport, map_viewport, status;
1929     Arg args[8];
1930     Cardinal num_args;
1931     Dimension message_vp_width, map_vp_width, status_width, max_width;
1932     int map_vp_hd, status_hd;
1933     struct xwindow *wp;
1934
1935     num_args = 0;
1936     XtSetArg(args[num_args], XtNallowShellResize, True);
1937     num_args++;
1938     form = XtCreateManagedWidget("nethack", panedWidgetClass, toplevel, args,
1939                                  num_args);
1940
1941     XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey,
1942                       (XtPointer) 0);
1943
1944     if (appResources.autofocus)
1945         XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible,
1946                           (XtPointer) 0);
1947
1948     /*
1949      * Create message window.
1950      */
1951     WIN_MESSAGE = message_win = find_free_window();
1952     wp = &window_list[message_win];
1953     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1954     wp->popup = (Widget) 0;
1955     create_message_window(wp, FALSE, form);
1956     message_viewport = XtParent(wp->w);
1957
1958     /* Tell the form that contains it that resizes are OK. */
1959     num_args = 0;
1960     XtSetArg(args[num_args], nhStr(XtNresizable), True);
1961     num_args++;
1962     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
1963     num_args++;
1964     XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
1965     num_args++;
1966     XtSetValues(message_viewport, args, num_args);
1967
1968     if (appResources.slow) {
1969         num_args = 0;
1970         XtSetArg(args[num_args], XtNtranslations,
1971                  XtParseTranslationTable(yn_translations));
1972         num_args++;
1973         yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form,
1974                                          args, num_args);
1975         num_args = 0;
1976         XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
1977         num_args++;
1978         XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft);
1979         num_args++;
1980         XtSetArg(args[num_args], nhStr(XtNresizable), True);
1981         num_args++;
1982         XtSetArg(args[num_args], nhStr(XtNlabel), " ");
1983         num_args++;
1984         XtSetValues(yn_label, args, num_args);
1985     }
1986
1987     /*
1988      * Create the map window & viewport and chain the viewport beneath the
1989      * message_viewport.
1990      */
1991     map_win = find_free_window();
1992     wp = &window_list[map_win];
1993     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1994     wp->popup = (Widget) 0;
1995     create_map_window(wp, FALSE, form);
1996     map_viewport = XtParent(wp->w);
1997
1998     /* Chain beneath message_viewport or yn window. */
1999     num_args = 0;
2000     if (appResources.slow) {
2001         XtSetArg(args[num_args], nhStr(XtNfromVert), yn_label);
2002         num_args++;
2003     } else {
2004         XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2005         num_args++;
2006     }
2007     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
2008     num_args++;
2009     XtSetValues(map_viewport, args, num_args);
2010
2011     /* Create the status window, with the form as it's parent. */
2012     status_win = find_free_window();
2013     wp = &window_list[status_win];
2014     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2015     wp->popup = (Widget) 0;
2016     create_status_window(wp, FALSE, form);
2017     status = wp->w;
2018
2019     /*
2020      * Chain the status window beneath the viewport.  Mark the left and right
2021      * edges so that they stay a fixed distance from the left edge of the
2022      * parent, as well as the top and bottom edges so that they stay a fixed
2023      * distance from the bottom of the parent.  We do this so that the status
2024      * will never expand or contract.
2025      */
2026     num_args = 0;
2027     XtSetArg(args[num_args], nhStr(XtNfromVert), map_viewport);
2028     num_args++;
2029     XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
2030     num_args++;
2031     XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
2032     num_args++;
2033     XtSetArg(args[num_args], nhStr(XtNtop), XtChainBottom);
2034     num_args++;
2035     XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
2036     num_args++;
2037     XtSetValues(status, args, num_args);
2038
2039     /*
2040      * Realize the popup so that the status widget knows it's size.
2041      *
2042      * If we unset MappedWhenManaged then the DECwindow driver doesn't
2043      * attach the nethack toplevel to the highest virtual root window.
2044      * So don't do it.
2045      */
2046     /* XtSetMappedWhenManaged(toplevel, False); */
2047     XtRealizeWidget(toplevel);
2048     wm_delete_window =
2049         XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW", False);
2050     XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
2051                     &wm_delete_window, 1);
2052
2053     /*
2054      * Resize to at most full-screen.
2055      */
2056     {
2057 #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
2058
2059         int screen_width = WidthOfScreen(XtScreen(wp->w));
2060         int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
2061         Dimension form_width, form_height;
2062
2063         XtSetArg(args[0], XtNwidth, &form_width);
2064         XtSetArg(args[1], XtNheight, &form_height);
2065         XtGetValues(toplevel, args, TWO);
2066
2067         if (form_width > screen_width || form_height > screen_height) {
2068             XtSetArg(args[0], XtNwidth, min(form_width, screen_width));
2069             XtSetArg(args[1], XtNheight, min(form_height, screen_height));
2070             XtSetValues(toplevel, args, TWO);
2071             XMoveWindow(XtDisplay(toplevel), XtWindow(toplevel), 0,
2072                         TITLEBAR_SPACE);
2073         }
2074 #undef TITLEBAR_SPACE
2075     }
2076
2077     post_process_tiles(); /* after toplevel is realized */
2078
2079     /*
2080      * Now get the default widths of the windows.
2081      */
2082     XtSetArg(args[0], nhStr(XtNwidth), &message_vp_width);
2083     XtGetValues(message_viewport, args, ONE);
2084     XtSetArg(args[0], nhStr(XtNwidth), &map_vp_width);
2085     XtSetArg(args[1], nhStr(XtNhorizDistance), &map_vp_hd);
2086     XtGetValues(map_viewport, args, TWO);
2087     XtSetArg(args[0], nhStr(XtNwidth), &status_width);
2088     XtSetArg(args[1], nhStr(XtNhorizDistance), &status_hd);
2089     XtGetValues(status, args, TWO);
2090
2091     /*
2092      * Adjust positions and sizes.  The message viewport widens out to the
2093      * widest width.  Both the map and status are centered by adjusting
2094      * their horizDistance.
2095      */
2096     if (map_vp_width < status_width || map_vp_width < message_vp_width) {
2097         if (status_width > message_vp_width) {
2098             XtSetArg(args[0], nhStr(XtNwidth), status_width);
2099             XtSetValues(message_viewport, args, ONE);
2100             max_width = status_width;
2101         } else {
2102             max_width = message_vp_width;
2103         }
2104         XtSetArg(args[0], nhStr(XtNhorizDistance),
2105                  map_vp_hd + ((int) (max_width - map_vp_width) / 2));
2106         XtSetValues(map_viewport, args, ONE);
2107
2108     } else { /* map is widest */
2109         XtSetArg(args[0], nhStr(XtNwidth), map_vp_width);
2110         XtSetValues(message_viewport, args, ONE);
2111     }
2112     /*
2113      * Clear all data values on the fancy status widget so that the values
2114      * used for spacing don't appear.  This needs to be called some time
2115      * after the fancy status widget is realized (above, with the game popup),
2116      * but before it is popped up.
2117      */
2118     null_out_status();
2119     /*
2120      * Set the map size to its standard size.  As with the message window
2121      * above, the map window needs to be set to its constrained size until
2122      * its parent (the viewport widget) was realized.
2123      *
2124      * Move the message window's slider to the bottom.
2125      */
2126     set_map_size(&window_list[map_win], COLNO, ROWNO);
2127     set_message_slider(&window_list[message_win]);
2128
2129     /* attempt to catch fatal X11 errors before the program quits */
2130     (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
2131
2132     /* We can now print to the message window. */
2133     iflags.window_inited = 1;
2134 }
2135
2136 void
2137 nh_XtPopup(w, g, childwid)
2138 Widget w;        /* widget */
2139 int g;           /* type of grab */
2140 Widget childwid; /* child to recieve focus (can be None) */
2141 {
2142     XtPopup(w, (XtGrabKind) g);
2143     XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
2144     if (appResources.autofocus)
2145         XtSetKeyboardFocus(toplevel, childwid);
2146 }
2147
2148 void
2149 nh_XtPopdown(w)
2150 Widget w;
2151 {
2152     XtPopdown(w);
2153     if (appResources.autofocus)
2154         XtSetKeyboardFocus(toplevel, None);
2155 }
2156
2157 void
2158 win_X11_init(dir)
2159 int dir;
2160 {
2161     if (dir != WININIT)
2162         return;
2163
2164 #ifdef OPENWINBUG
2165     /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
2166      * two routines will not be found when linking.  An apparently correct
2167      * executable is produced, along with nasty messages and a failure code
2168      * returned to make.  The routines are in the static libXmu.a and
2169      * libXmu.sa.4.0, but not in libXmu.so.4.0.  Rather than fiddle with
2170      * static linking, we do this.
2171      */
2172     if (rn2(2) > 2) {
2173         /* i.e., FALSE that an optimizer probably can't find */
2174         get_wmShellWidgetClass();
2175         get_applicationShellWidgetClass();
2176     }
2177 #endif
2178     return;
2179 }
2180
2181 /* Callback
2182  * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2183  */
2184 /*ARGSUSED*/
2185 void
2186 nh_keyscroll(viewport, event, params, num_params)
2187 Widget viewport;
2188 XEvent *event;
2189 String *params;
2190 Cardinal *num_params;
2191 {
2192     Arg arg[2];
2193     Widget horiz_sb, vert_sb;
2194     float top, shown;
2195     Boolean do_call;
2196     int direction;
2197     Cardinal in_nparams = (num_params ? *num_params : 0);
2198
2199     nhUse(event);
2200
2201     if (in_nparams != 1)
2202         return; /* bad translation */
2203
2204     direction = atoi(params[0]);
2205
2206     horiz_sb = XtNameToWidget(viewport, "*horizontal");
2207     vert_sb = XtNameToWidget(viewport, "*vertical");
2208
2209     if (!horiz_sb && !vert_sb) {
2210         /* Perhaps the widget enclosing this has scrollbars (could use while)
2211          */
2212         Widget parent = XtParent(viewport);
2213         if (parent) {
2214             horiz_sb = XtNameToWidget(parent, "horizontal");
2215             vert_sb = XtNameToWidget(parent, "vertical");
2216         }
2217     }
2218
2219 #define H_DELTA 0.25 /* distance of horiz shift */
2220     /* vert shift is half of curr distance */
2221     /* The V_DELTA is 1/2 the value of shown. */
2222
2223     if (horiz_sb) {
2224         XtSetArg(arg[0], nhStr(XtNshown), &shown);
2225         XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2226         XtGetValues(horiz_sb, arg, TWO);
2227
2228         do_call = True;
2229
2230         switch (direction) {
2231         case 1:
2232         case 4:
2233         case 7:
2234             top -= H_DELTA;
2235             if (top < 0.0)
2236                 top = 0.0;
2237             break;
2238         case 3:
2239         case 6:
2240         case 9:
2241             top += H_DELTA;
2242             if (top + shown > 1.0)
2243                 top = 1.0 - shown;
2244             break;
2245         default:
2246             do_call = False;
2247         }
2248
2249         if (do_call) {
2250             XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2251         }
2252     }
2253
2254     if (vert_sb) {
2255         XtSetArg(arg[0], nhStr(XtNshown), &shown);
2256         XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2257         XtGetValues(vert_sb, arg, TWO);
2258
2259         do_call = True;
2260
2261         switch (direction) {
2262         case 7:
2263         case 8:
2264         case 9:
2265             top -= shown / 2.0;
2266             if (top < 0.0)
2267                 top = 0;
2268             break;
2269         case 1:
2270         case 2:
2271         case 3:
2272             top += shown / 2.0;
2273             if (top + shown > 1.0)
2274                 top = 1.0 - shown;
2275             break;
2276         default:
2277             do_call = False;
2278         }
2279
2280         if (do_call) {
2281             XtCallCallbacks(vert_sb, XtNjumpProc, &top);
2282         }
2283     }
2284 }
2285
2286 /*winX.c*/