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 *
find_widget(w)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
find_free_window()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
nhApproxColor(screen,colormap,str,color)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
nhCvtStringToPixel(dpy,args,num_args,fromVal,toVal,closure_ret)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
nhFreePixel(app,toVal,closure,args,num_args)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
X11_raw_print(str)455 X11_raw_print(str)
456     const char *str;
457 {
458     (void) puts(str);
459 }
460 
461 void
X11_raw_print_bold(str)462 X11_raw_print_bold(str)
463     const char *str;
464 {
465     (void) puts(str);
466 }
467 
468 void
X11_curs(window,x,y)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
X11_putstr(window,attr,str)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. */
X11_get_nh_event()538 void X11_get_nh_event() { return; }
539 
540 int
X11_nhgetch()541 X11_nhgetch()
542 {
543     return input_event(EXIT_ON_KEY_PRESS);
544 }
545 
546 
547 int
X11_nh_poskey(x,y,mod)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
X11_create_nhwindow(type)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
X11_clear_nhwindow(window)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
X11_display_nhwindow(window,blocking)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
X11_destroy_nhwindow(window)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
X11_update_inventory()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. */
X11_doprev_message()775 int X11_doprev_message() { return 0; }
776 
777 void
X11_nhbell()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 
X11_mark_synch()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 
X11_wait_synch()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. */
X11_resume_nhwindows()806 void X11_resume_nhwindows() { return; }
807 
808 /* ARGSUSED */
X11_suspend_nhwindows(str)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 */
X11_number_pad(state)813 void X11_number_pad(state) int state; { return; } /* called from options.c */
814 
815 
X11_start_screen()816 void X11_start_screen() { return; } /* called from setftty() in unixtty.c */
X11_end_screen()817 void X11_end_screen() { return; }   /* called from settty() in unixtty.c */
818 
819 #ifdef GRAPHIC_TOMBSTONE
X11_outrip(window,how)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
X11_init_nhwindows(argcp,argv)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 */
X11_exit_nhwindows(dummy)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
d_timeout(client_data,id)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
X11_delay_output()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
X11_hangup(w,event,params,num_params)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
askname_delete(w,event,params,num_params)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
askname_done(w,client_data,call_data)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
X11_askname()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
done_button(w,client_data,call_data)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
getline_delete(w,event,params,num_params)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
abort_button(w,client_data,call_data)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
X11_getlin(question,input)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
delete_file(w,event,params,num_params)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
dismiss_file(w,event,params,num_params)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
X11_display_file(str,complain)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
key_event_to_char(key)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
yn_delete(w,event,params,num_params)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
yn_key(w,event,params,num_params)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
X11_yn_function(ques,choices,def)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
input_event(exit_condition)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
msgkey(w,data,event)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
win_visible(w,data,event,flag)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
init_standard_windows()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
nh_XtPopup(w,g,childwid)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
nh_XtPopdown(w)1923 nh_XtPopdown(w)
1924     Widget w;
1925 {
1926     XtPopdown(w);
1927     if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None);
1928 }
1929 
1930 void
win_X11_init()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
nh_keyscroll(viewport,event,params,num_params)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*/
2036