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