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