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