NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / X11 / winX.c
blobba214c5d9fe6883ba79d973b9698307660538166
1 /* aNetHack 0.0.1 winX.c $ANH-Date: 1457079197 2016/03/04 08:13:17 $ $ANH-Branch: aNetHack-3.6.0 $:$ANH-Revision: 1.41 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* aNetHack may be freely redistributed. See license for details. */
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 */
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
15 #ifdef MSDOS /* from compiler */
16 #define SHORT_FILENAMES
17 #endif
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>
31 /* for color support */
32 #ifdef SHORT_FILENAMES
33 #include <X11/IntrinsP.h>
34 #else
35 #include <X11/IntrinsicP.h>
36 #endif
38 #ifdef PRESERVE_NO_SYSV
39 #ifdef SYSV
40 #undef SYSV
41 #endif
42 #undef PRESERVE_NO_SYSV
43 #endif
45 #ifdef SHORT_FILENAMES
46 #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
47 #endif
49 #include "hack.h"
50 #include "winX.h"
51 #include "dlb.h"
53 #ifndef NO_SIGNAL
54 #include <signal.h>
55 #endif
57 /* Should be defined in <X11/Intrinsic.h> but you never know */
58 #ifndef XtSpecificationRelease
59 #define XtSpecificationRelease 0
60 #endif
63 * Icons.
65 #include "../win/X11/nh72icon"
66 #include "../win/X11/nh56icon"
67 #include "../win/X11/nh32icon"
69 static struct icon_info {
70 const char *name;
71 unsigned char *bits;
72 unsigned width, height;
73 } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
74 { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
75 { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
76 { (const char *) 0, (unsigned char *) 0, 0, 0 } };
79 * Private global variables (shared among the window port files).
81 struct xwindow window_list[MAX_WINDOWS];
82 AppResources appResources;
83 void FDECL((*input_func), (Widget, XEvent *, String *, Cardinal *));
84 int click_x, click_y, click_button; /* Click position on a map window */
85 /* (filled by set_button_values()). */
86 int updated_inventory;
88 #if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
89 #if XtSpecificationRelease >= 6
90 #define X11_HANGUP_SIGNAL
91 static XtSignalId X11_sig_id;
92 #endif
93 #endif
95 /* this is only needed until X11_status_* routines are written */
96 extern NEARDATA winid WIN_STATUS;
98 /* Interface definition, for windows.c */
99 struct window_procs X11_procs = {
100 "X11", WC_COLOR | WC_HILITE_PET | WC_TILED_MAP, 0L, X11_init_nhwindows,
101 X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows,
102 X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow,
103 X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs,
104 X11_putstr, genl_putmixed, X11_display_file, X11_start_menu, X11_add_menu,
105 X11_end_menu, X11_select_menu,
106 genl_message_menu, /* no need for X-specific handling */
107 X11_update_inventory, X11_mark_synch, X11_wait_synch,
108 #ifdef CLIPPING
109 X11_cliparound,
110 #endif
111 #ifdef POSITIONBAR
112 donull,
113 #endif
114 X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch,
115 X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function,
116 X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output,
117 #ifdef CHANGE_COLOR /* only a Mac option currently */
118 donull, donull,
119 #endif
120 /* other defs that really should go away (they're tty specific) */
121 X11_start_screen, X11_end_screen,
122 #ifdef GRAPHIC_TOMBSTONE
123 X11_outrip,
124 #else
125 genl_outrip,
126 #endif
127 X11_preference_update, genl_getmsghistory, genl_putmsghistory,
128 #ifdef STATUS_VIA_WINDOWPORT
129 genl_status_init, genl_status_finish, genl_status_enablefield,
130 genl_status_update,
131 #ifdef STATUS_HILITES
132 genl_status_threshold,
133 #endif
134 #endif
135 genl_can_suspend_no, /* XXX may not always be correct */
139 * Local functions.
141 static winid NDECL(find_free_window);
142 static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer,
143 XrmValuePtr, Cardinal *));
144 static void NDECL(load_default_resources);
145 static void NDECL(release_default_resources);
146 #ifdef X11_HANGUP_SIGNAL
147 static void FDECL(X11_sig, (int));
148 static void FDECL(X11_sig_cb, (XtPointer, XtSignalId *));
149 #endif
150 static void FDECL(d_timeout, (XtPointer, XtIntervalId *));
151 static void FDECL(X11_hangup, (Widget, XEvent *, String *, Cardinal *));
152 static void FDECL(askname_delete, (Widget, XEvent *, String *, Cardinal *));
153 static void FDECL(askname_done, (Widget, XtPointer, XtPointer));
154 static void FDECL(done_button, (Widget, XtPointer, XtPointer));
155 static void FDECL(getline_delete, (Widget, XEvent *, String *, Cardinal *));
156 static void FDECL(abort_button, (Widget, XtPointer, XtPointer));
157 static void NDECL(release_getline_widgets);
158 static void FDECL(delete_file, (Widget, XEvent *, String *, Cardinal *));
159 static void FDECL(dismiss_file, (Widget, XEvent *, String *, Cardinal *));
160 static void FDECL(yn_delete, (Widget, XEvent *, String *, Cardinal *));
161 static void FDECL(yn_key, (Widget, XEvent *, String *, Cardinal *));
162 static void NDECL(release_yn_widgets);
163 static int FDECL(input_event, (int));
164 static void FDECL(win_visible, (Widget, XtPointer, XEvent *, Boolean *));
165 static void NDECL(init_standard_windows);
168 * Local variables.
170 static boolean x_inited = FALSE; /* TRUE if window system is set up. */
171 static winid message_win = WIN_ERR, /* These are the winids of the message, */
172 map_win = WIN_ERR, /* map, and status windows, when they */
173 status_win = WIN_ERR; /* are created in init_windows(). */
174 static Pixmap icon_pixmap = None; /* Pixmap for icon. */
177 * Find the window structure that corresponds to the given widget. Note
178 * that this is not the popup widget, nor the viewport, but the child.
180 struct xwindow *
181 find_widget(w)
182 Widget w;
184 int windex;
185 struct xwindow *wp;
188 * Search to find the corresponding window. Look at the main widget,
189 * popup, the parent of the main widget, then parent of the widget.
191 for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
192 if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w
193 || (wp->w && (XtParent(wp->w)) == w)
194 || (wp->popup == XtParent(w))))
195 break;
197 if (windex == MAX_WINDOWS)
198 panic("find_widget: can't match widget");
199 return wp;
203 * Find a free window slot for use.
205 static winid
206 find_free_window()
208 int windex;
209 struct xwindow *wp;
211 for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS;
212 windex++, wp++)
213 if (wp->type == NHW_NONE)
214 break;
216 if (windex == MAX_WINDOWS)
217 panic("find_free_window: no free windows!");
218 return (winid) windex;
222 * Color conversion. The default X11 color converters don't try very
223 * hard to find matching colors in PseudoColor visuals. If they can't
224 * allocate the exact color, they puke and give you something stupid.
225 * This is an attempt to find some close readonly cell and use it.
227 XtConvertArgRec const nhcolorConvertArgs[] = {
228 { XtWidgetBaseOffset,
229 (XtPointer) (ptrdiff_t) XtOffset(Widget, core.screen),
230 sizeof (Screen *) },
231 { XtWidgetBaseOffset,
232 (XtPointer) (ptrdiff_t) XtOffset(Widget, core.colormap),
233 sizeof (Colormap) }
236 #define done(type, value) \
238 if (toVal->addr != 0) { \
239 if (toVal->size < sizeof(type)) { \
240 toVal->size = sizeof(type); \
241 return False; \
243 *(type *)(toVal->addr) = (value); \
244 } else { \
245 static type static_val; \
246 static_val = (value); \
247 toVal->addr = (genericptr_t) &static_val; \
249 toVal->size = sizeof(type); \
250 return True; \
254 * Find a color that approximates the color named in "str".
255 * The "str" color may be a color name ("red") or number ("#7f0000").
256 * If str is Null, then "color" is assumed to contain the RGB color wanted.
257 * The approximate color found is returned in color as well.
258 * Return True if something close was found.
260 Boolean
261 nhApproxColor(screen, colormap, str, color)
262 Screen *screen; /* screen to use */
263 Colormap colormap; /* the colormap to use */
264 char *str; /* color name */
265 XColor *color; /* the X color structure; changed only if successful */
267 int ncells;
268 long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
269 XColor tmp;
270 static XColor *table = 0;
271 register int i, j;
272 register long tdiff;
274 /* if the screen doesn't have a big colormap, don't waste our time
275 or if it's huge, and _some_ match should have been possible */
276 if ((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
277 return False;
279 if (str != (char *) 0) {
280 if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
281 return False;
282 } else {
283 tmp = *color;
284 tmp.flags = 7; /* force to use all 3 of RGB */
287 if (!table) {
288 table = (XColor *) XtCalloc(ncells, sizeof(XColor));
289 for (i = 0; i < ncells; i++)
290 table[i].pixel = i;
291 XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
294 /* go thru cells and look for the one with smallest diff */
295 /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
296 /* a more knowledgeable color person might improve this -dlc */
297 try_again:
298 for (i = 0; i < ncells; i++) {
299 if (table[i].flags == tmp.flags) {
300 j = (int) table[i].red - (int) tmp.red;
301 if (j < 0)
302 j = -j;
303 tdiff = j;
304 j = (int) table[i].green - (int) tmp.green;
305 if (j < 0)
306 j = -j;
307 tdiff += j;
308 j = (int) table[i].blue - (int) tmp.blue;
309 if (j < 0)
310 j = -j;
311 tdiff += j;
312 if (tdiff < cdiff) {
313 cdiff = tdiff;
314 tmp.pixel = i; /* table[i].pixel == i */
319 if (cdiff == 16777216)
320 return False; /* nothing found?! */
323 * Found something. Return it and mark this color as used to avoid
324 * reuse. Reuse causes major contrast problems :-)
326 *color = table[tmp.pixel];
327 table[tmp.pixel].flags = 0;
328 /* try to alloc the color, so no one else can change it */
329 if (!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
330 cdiff = 16777216;
331 goto try_again;
333 return True;
336 Boolean
337 nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
338 Display *dpy;
339 XrmValuePtr args;
340 Cardinal *num_args;
341 XrmValuePtr fromVal;
342 XrmValuePtr toVal;
343 XtPointer *closure_ret;
345 String str = (String) fromVal->addr;
346 XColor screenColor;
347 XColor exactColor;
348 Screen *screen;
349 XtAppContext app = XtDisplayToApplicationContext(dpy);
350 Colormap colormap;
351 Status status;
352 String params[1];
353 Cardinal num_params = 1;
355 if (*num_args != 2) {
356 XtAppWarningMsg(app, "wrongParameters",
357 "cvtStringToPixel", "XtToolkitError",
358 "String to pixel conversion needs screen and colormap arguments",
359 (String *) 0, (Cardinal *) 0);
360 return False;
363 screen = *((Screen **) args[0].addr);
364 colormap = *((Colormap *) args[1].addr);
366 /* If Xt colors, use the Xt routine and hope for the best */
367 #if (XtSpecificationRelease >= 5)
368 if ((strcmpi(str, XtDefaultBackground) == 0)
369 || (strcmpi(str, XtDefaultForeground) == 0)) {
370 return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal,
371 closure_ret);
373 #else
374 if (strcmpi(str, XtDefaultBackground) == 0) {
375 *closure_ret = (char *) False;
376 done(Pixel, WhitePixelOfScreen(screen));
378 if (strcmpi(str, XtDefaultForeground) == 0) {
379 *closure_ret = (char *) False;
380 done(Pixel, BlackPixelOfScreen(screen));
382 #endif
384 status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char *) str,
385 &screenColor, &exactColor);
386 if (status == 0) {
387 String msg, type;
389 /* some versions of XAllocNamedColor don't allow #xxyyzz names */
390 if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap,
391 str, &exactColor)
392 && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
393 *closure_ret = (char *) True;
394 done(Pixel, exactColor.pixel);
397 params[0] = str;
398 /* Server returns a specific error code but Xlib discards it. Ugh */
399 if (XLookupColor(DisplayOfScreen(screen), colormap, (char *) str,
400 &exactColor, &screenColor)) {
401 /* try to find another color that will do */
402 if (nhApproxColor(screen, colormap, (char *) str, &screenColor)) {
403 *closure_ret = (char *) True;
404 done(Pixel, screenColor.pixel);
406 type = nhStr("noColormap");
407 msg = nhStr("Cannot allocate colormap entry for \"%s\"");
408 } else {
409 /* some versions of XLookupColor also don't allow #xxyyzz names */
410 if (str[0] == '#'
411 && (nhApproxColor(screen, colormap, (char *) str,
412 &screenColor))) {
413 *closure_ret = (char *) True;
414 done(Pixel, screenColor.pixel);
416 type = nhStr("badValue");
417 msg = nhStr("Color name \"%s\" is not defined");
420 XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg,
421 params, &num_params);
422 *closure_ret = False;
423 return False;
424 } else {
425 *closure_ret = (char *) True;
426 done(Pixel, screenColor.pixel);
430 /* ARGSUSED */
431 static void
432 nhFreePixel(app, toVal, closure, args, num_args)
433 XtAppContext app;
434 XrmValuePtr toVal;
435 XtPointer closure;
436 XrmValuePtr args;
437 Cardinal *num_args;
439 Screen *screen;
440 Colormap colormap;
442 if (*num_args != 2) {
443 XtAppWarningMsg(app, "wrongParameters", "freePixel", "XtToolkitError",
444 "Freeing a pixel requires screen and colormap arguments",
445 (String *) 0, (Cardinal *) 0);
446 return;
449 screen = *((Screen **) args[0].addr);
450 colormap = *((Colormap *) args[1].addr);
452 if (closure) {
453 XFreeColors(DisplayOfScreen(screen), colormap,
454 (unsigned long *) toVal->addr, 1, (unsigned long) 0);
458 /* [ALI] Utility function to ask Xaw for font height, since the previous
459 * assumption of ascent + descent is not always valid.
461 Dimension
462 nhFontHeight(w)
463 Widget w;
465 #ifdef _XawTextSink_h
466 Widget sink;
467 XawTextPosition pos = 0;
468 int resWidth, resHeight;
469 Arg args[1];
471 XtSetArg(args[0], XtNtextSink, &sink);
472 XtGetValues(w, args, 1);
474 XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
475 return resHeight;
476 #else
477 XFontStruct *fs;
478 Arg args[1];
480 XtSetArg(args[0], XtNfont, &fs);
481 XtGetValues(w, args, 1);
483 /* Assume font height is ascent + descent. */
484 return = fs->ascent + fs->descent;
485 #endif
488 static String *default_resource_data = 0; /* NULL-terminated array */
490 /* read the template aNetHack.ad into default_resource_data[] to supply
491 fallback resources to XtAppInitialize() */
492 static void
493 load_default_resources()
495 FILE *fp;
496 String inbuf;
497 unsigned insiz, linelen, longlen, numlines;
498 boolean comment = FALSE; /* lint suppression */
501 * Running anethack via the shell script adds $HACKDIR to the path used
502 * by X to find resources, but running it directly doesn't. So, if we
503 * can find the template file for aNetHack.ad in the current directory,
504 * load its contents into memory so that the application startup call
505 * in X11_init_nhwindows() can use them as fallback resources.
507 * No attempt to support the 'include' directive has been made.
509 fp = fopen("./aNetHack.ad", "r");
510 if (!fp)
511 return;
513 /* measure the file without retaining its contents */
514 insiz = BUFSIZ; /* stdio BUFSIZ, not anethack BUFSZ */
515 inbuf = (String) alloc(insiz);
516 linelen = longlen = 0;
517 numlines = 0;
518 while (fgets(inbuf, insiz, fp)) {
519 if (!linelen) /* inbuf has start of record; treat empty as comment */
520 comment = (*inbuf == '!' || *inbuf == '\n');
521 linelen += strlen(inbuf);
522 if (!index(inbuf, '\n'))
523 continue;
524 if (linelen > longlen)
525 longlen = linelen;
526 linelen = 0;
527 if (!comment)
528 ++numlines;
530 free((genericptr_t) inbuf);
531 insiz = longlen + 1;
532 inbuf = (String) alloc(insiz);
533 ++numlines; /* room for terminator */
534 default_resource_data = (String *) alloc(numlines * sizeof (String));
536 /* now read the file into the array */
537 (void) rewind(fp);
538 numlines = 0;
539 while (fgets(inbuf, insiz, fp)) {
540 if (*inbuf != '!' && *inbuf != '\n')
541 default_resource_data[numlines++] = dupstr(inbuf);
543 default_resource_data[numlines] = (String) 0;
544 (void) fclose(fp);
545 free((genericptr_t) inbuf);
548 static void
549 release_default_resources()
551 if (default_resource_data) {
552 unsigned idx;
554 for (idx = 0; default_resource_data[idx]; idx++)
555 free((genericptr_t) default_resource_data[idx]);
556 free((genericptr_t) default_resource_data), default_resource_data = 0;
560 /* Global Functions ======================================================= */
561 void
562 X11_raw_print(str)
563 const char *str;
565 (void) puts(str);
568 void
569 X11_raw_print_bold(str)
570 const char *str;
572 (void) puts(str);
575 void
576 X11_curs(window, x, y)
577 winid window;
578 int x, y;
580 check_winid(window);
582 if (x < 0 || x >= COLNO) {
583 impossible("curs: bad x value [%d]", x);
584 x = 0;
586 if (y < 0 || y >= ROWNO) {
587 impossible("curs: bad y value [%d]", y);
588 y = 0;
591 window_list[window].cursx = x;
592 window_list[window].cursy = y;
595 void
596 X11_putstr(window, attr, str)
597 winid window;
598 int attr;
599 const char *str;
601 winid new_win;
602 struct xwindow *wp;
604 check_winid(window);
605 wp = &window_list[window];
607 switch (wp->type) {
608 case NHW_MESSAGE:
609 (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
610 toplines[TBUFSZ - 1] = 0;
611 append_message(wp, str);
612 break;
613 case NHW_STATUS:
614 adjust_status(wp, str);
615 break;
616 case NHW_MAP:
617 impossible("putstr: called on map window \"%s\"", str);
618 break;
619 case NHW_MENU:
620 if (wp->menu_information->is_menu) {
621 impossible("putstr: called on a menu window, \"%s\" discarded",
622 str);
623 break;
626 * Change this menu window into a text window by creating a
627 * new text window, then copying it to this winid.
629 new_win = X11_create_nhwindow(NHW_TEXT);
630 X11_destroy_nhwindow(window);
631 *wp = window_list[new_win];
632 window_list[new_win].type = NHW_NONE; /* allow re-use */
633 /* fall though to add text */
634 case NHW_TEXT:
635 add_to_text_window(wp, attr, str);
636 break;
637 default:
638 impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str);
642 /* We do event processing as a callback, so this is a null routine. */
643 void
644 X11_get_nh_event()
646 return;
650 X11_nhgetch()
652 return input_event(EXIT_ON_KEY_PRESS);
656 X11_nh_poskey(x, y, mod)
657 int *x, *y, *mod;
659 int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
661 if (val == 0) { /* user clicked on a map window */
662 *x = click_x;
663 *y = click_y;
664 *mod = click_button;
666 return val;
669 winid
670 X11_create_nhwindow(type)
671 int type;
673 winid window;
674 struct xwindow *wp;
676 if (!x_inited)
677 panic("create_nhwindow: windows not initialized");
679 #ifdef X11_HANGUP_SIGNAL
680 /* set up our own signal handlers on the first call. Can't do this in
681 * X11_init_nhwindows because unixmain sets its handler after calling
682 * all the init routines. */
683 if (X11_sig_id == 0) {
684 X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer) 0);
685 #ifdef SA_RESTART
687 struct sigaction sact;
689 (void) memset((char *) &sact, 0, sizeof(struct sigaction));
690 sact.sa_handler = (SIG_RET_TYPE) X11_sig;
691 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
692 #ifdef SIGXCPU
693 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
694 #endif
696 #else
697 (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
698 #ifdef SIGXCPU
699 (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
700 #endif
701 #endif
703 #endif
706 * We have already created the standard message, map, and status
707 * windows in the window init routine. The first window of that
708 * type to be created becomes the standard.
710 * A better way to do this would be to say that init_nhwindows()
711 * has already defined these three windows.
713 if (type == NHW_MAP && map_win != WIN_ERR) {
714 window = map_win;
715 map_win = WIN_ERR;
716 return window;
718 if (type == NHW_MESSAGE && message_win != WIN_ERR) {
719 window = message_win;
720 message_win = WIN_ERR;
721 return window;
723 if (type == NHW_STATUS && status_win != WIN_ERR) {
724 window = status_win;
725 status_win = WIN_ERR;
726 return window;
729 window = find_free_window();
730 wp = &window_list[window];
732 /* The create routines will set type, popup, w, and Win_info. */
733 wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width =
734 wp->pixel_height = 0;
735 wp->keep_window = FALSE;
737 switch (type) {
738 case NHW_MAP:
739 create_map_window(wp, TRUE, (Widget) 0);
740 break;
741 case NHW_MESSAGE:
742 create_message_window(wp, TRUE, (Widget) 0);
743 break;
744 case NHW_STATUS:
745 create_status_window(wp, TRUE, (Widget) 0);
746 break;
747 case NHW_MENU:
748 create_menu_window(wp);
749 break;
750 case NHW_TEXT:
751 create_text_window(wp);
752 break;
753 default:
754 panic("create_nhwindow: unknown type [%d]", type);
755 break;
757 return window;
760 void
761 X11_clear_nhwindow(window)
762 winid window;
764 struct xwindow *wp;
766 check_winid(window);
767 wp = &window_list[window];
769 switch (wp->type) {
770 case NHW_MAP:
771 clear_map_window(wp);
772 break;
773 case NHW_TEXT:
774 clear_text_window(wp);
775 break;
776 case NHW_STATUS:
777 case NHW_MENU:
778 case NHW_MESSAGE:
779 /* do nothing for these window types */
780 break;
781 default:
782 panic("clear_nhwindow: unknown window type [%d]", wp->type);
783 break;
787 void
788 X11_display_nhwindow(window, blocking)
789 winid window;
790 boolean blocking;
792 struct xwindow *wp;
794 check_winid(window);
795 wp = &window_list[window];
797 switch (wp->type) {
798 case NHW_MAP:
799 if (wp->popup)
800 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
801 display_map_window(wp); /* flush map */
804 * We need to flush the message window here due to the way the tty
805 * port is set up. To flush a window, you need to call this
806 * routine. However, the tty port _pauses_ with a --more-- if we
807 * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call
808 * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
809 * get a --more-- after every line.
811 * Perhaps the window document should mention that when the map
812 * is flushed, everything on the three main windows should be
813 * flushed. Note: we don't need to flush the status window
814 * because we don't buffer changes.
816 if (WIN_MESSAGE != WIN_ERR)
817 display_message_window(&window_list[WIN_MESSAGE]);
818 if (blocking)
819 (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
820 break;
821 case NHW_MESSAGE:
822 if (wp->popup)
823 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
824 display_message_window(wp); /* flush messages */
825 break;
826 case NHW_STATUS:
827 if (wp->popup)
828 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
829 break; /* no flushing necessary */
830 case NHW_MENU: {
831 int n;
832 menu_item *selected;
834 /* pop up menu */
835 n = X11_select_menu(window, PICK_NONE, &selected);
836 if (n) {
837 impossible("perminvent: %d selected??", n);
838 free((genericptr_t) selected);
840 break;
842 case NHW_TEXT:
843 display_text_window(wp, blocking); /* pop up text window */
844 break;
845 default:
846 panic("display_nhwindow: unknown window type [%d]", wp->type);
847 break;
851 void
852 X11_destroy_nhwindow(window)
853 winid window;
855 struct xwindow *wp;
857 check_winid(window);
858 wp = &window_list[window];
860 * "Zap" known windows, but don't destroy them. We need to keep the
861 * toplevel widget popped up so that later windows (e.g. tombstone)
862 * are visible on DECWindow systems. This is due to the virtual
863 * roots that the DECWindow wm creates.
865 if (window == WIN_MESSAGE) {
866 wp->keep_window = TRUE;
867 WIN_MESSAGE = WIN_ERR;
868 iflags.window_inited = 0;
869 } else if (window == WIN_MAP) {
870 wp->keep_window = TRUE;
871 WIN_MAP = WIN_ERR;
872 } else if (window == WIN_STATUS) {
873 wp->keep_window = TRUE;
874 WIN_STATUS = WIN_ERR;
875 } else if (window == WIN_INVEN) {
876 /* don't need to keep this one */
877 WIN_INVEN = WIN_ERR;
880 switch (wp->type) {
881 case NHW_MAP:
882 destroy_map_window(wp);
883 break;
884 case NHW_MENU:
885 destroy_menu_window(wp);
886 break;
887 case NHW_TEXT:
888 destroy_text_window(wp);
889 break;
890 case NHW_STATUS:
891 destroy_status_window(wp);
892 break;
893 case NHW_MESSAGE:
894 destroy_message_window(wp);
895 break;
896 default:
897 panic("destroy_nhwindow: unknown window type [%d]", wp->type);
898 break;
902 void
903 X11_update_inventory()
905 if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
906 updated_inventory = 1; /* hack to avoid mapping&raising window */
907 (void) display_inventory((char *) 0, FALSE);
908 updated_inventory = 0;
912 /* The current implementation has all of the saved lines on the screen. */
914 X11_doprev_message()
916 return 0;
919 void
920 X11_nhbell()
922 /* We can't use XBell until toplevel has been initialized. */
923 if (x_inited)
924 XBell(XtDisplay(toplevel), 0);
925 /* else print ^G ?? */
928 void
929 X11_mark_synch()
931 if (x_inited) {
933 * The window document is unclear about the status of text
934 * that has been pline()d but not displayed w/display_nhwindow().
935 * Both the main and tty code assume that a pline() followed
936 * by mark_synch() results in the text being seen, even if
937 * display_nhwindow() wasn't called. Duplicate this behavior.
939 if (WIN_MESSAGE != WIN_ERR)
940 display_message_window(&window_list[WIN_MESSAGE]);
941 XSync(XtDisplay(toplevel), False);
945 void
946 X11_wait_synch()
948 if (x_inited)
949 XFlush(XtDisplay(toplevel));
952 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
953 void
954 X11_resume_nhwindows()
956 return;
958 /* ARGSUSED */
959 void
960 X11_suspend_nhwindows(str)
961 const char *str;
963 nhUse(str);
965 return;
968 /* Under X, we don't need to initialize the number pad. */
969 /* ARGSUSED */
970 void
971 X11_number_pad(state) /* called from options.c */
972 int state;
974 nhUse(state);
976 return;
979 /* called from setftty() in unixtty.c */
980 void
981 X11_start_screen()
983 return;
986 /* called from settty() in unixtty.c */
987 void
988 X11_end_screen()
990 return;
993 #ifdef GRAPHIC_TOMBSTONE
994 void
995 X11_outrip(window, how, when)
996 winid window;
997 int how;
998 time_t when;
1000 struct xwindow *wp;
1002 check_winid(window);
1003 wp = &window_list[window];
1005 if (wp->type == NHW_TEXT) {
1006 wp->text_information->is_rip = TRUE;
1007 } else {
1008 panic("ripout on non-text window (window type [%d])", wp->type);
1011 calculate_rip_text(how, when);
1013 #endif
1015 /* init and exit nhwindows ------------------------------------------------ */
1017 XtAppContext app_context; /* context of application */
1018 Widget toplevel = (Widget) 0; /* toplevel widget */
1019 Atom wm_delete_window; /* pop down windows */
1021 static XtActionsRec actions[] = {
1022 { nhStr("dismiss_file"), dismiss_file }, /* file viewing widget */
1023 { nhStr("delete_file"), delete_file }, /* file delete-window */
1024 { nhStr("dismiss_text"), dismiss_text }, /* text widget button action */
1025 { nhStr("delete_text"), delete_text }, /* text widget delete action */
1026 { nhStr("key_dismiss_text"), key_dismiss_text }, /* text key action */
1027 #ifdef GRAPHIC_TOMBSTONE
1028 { nhStr("rip_dismiss_text"), rip_dismiss_text }, /* rip in text widget */
1029 #endif
1030 { nhStr("menu_key"), menu_key }, /* menu accelerators */
1031 { nhStr("yn_key"), yn_key }, /* yn accelerators */
1032 { nhStr("yn_delete"), yn_delete }, /* yn delete-window */
1033 { nhStr("askname_delete"), askname_delete }, /* askname delete-window */
1034 { nhStr("getline_delete"), getline_delete }, /* getline delete-window */
1035 { nhStr("menu_delete"), menu_delete }, /* menu delete-window */
1036 { nhStr("ec_key"), ec_key }, /* extended commands */
1037 { nhStr("ec_delete"), ec_delete }, /* ext-com menu delete */
1038 { nhStr("ps_key"), ps_key }, /* player selection */
1039 { nhStr("race_key"), race_key }, /* race selection */
1040 { nhStr("gend_key"), gend_key }, /* gender selection */
1041 { nhStr("algn_key"), algn_key }, /* alignment selection */
1042 { nhStr("X11_hangup"), X11_hangup }, /* delete of top-level */
1043 { nhStr("input"), map_input }, /* key input */
1044 { nhStr("scroll"), nh_keyscroll }, /* scrolling by keys */
1047 static XtResource resources[] = {
1048 { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean),
1049 XtOffset(AppResources *, slow), XtRString, nhStr("True") },
1050 { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean),
1051 XtOffset(AppResources *, autofocus), XtRString, nhStr("False") },
1052 { nhStr("message_line"), nhStr("Message_line"), XtRBoolean,
1053 sizeof(Boolean), XtOffset(AppResources *, message_line), XtRString,
1054 nhStr("False") },
1055 { nhStr("highlight_prompt"), nhStr("Highlight_prompt"), XtRBoolean,
1056 sizeof(Boolean), XtOffset(AppResources *, highlight_prompt), XtRString,
1057 nhStr("True") },
1058 { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean,
1059 sizeof(Boolean), XtOffset(AppResources *, double_tile_size), XtRString,
1060 nhStr("False") },
1061 { nhStr("tile_file"), nhStr("Tile_file"), XtRString, sizeof(String),
1062 XtOffset(AppResources *, tile_file), XtRString, nhStr("x11tiles") },
1063 { nhStr("icon"), nhStr("Icon"), XtRString, sizeof(String),
1064 XtOffset(AppResources *, icon), XtRString, nhStr("nh72") },
1065 { nhStr("message_lines"), nhStr("Message_lines"), XtRInt, sizeof(int),
1066 XtOffset(AppResources *, message_lines), XtRString, nhStr("12") },
1067 { nhStr("extcmd_height_delta"), nhStr("Extcmd_height_delta"),
1068 XtRInt, sizeof (int),
1069 XtOffset(AppResources *, extcmd_height_delta), XtRString, nhStr("0") },
1070 { nhStr("pet_mark_bitmap"), nhStr("Pet_mark_bitmap"), XtRString,
1071 sizeof(String), XtOffset(AppResources *, pet_mark_bitmap), XtRString,
1072 nhStr("pet_mark.xbm") },
1073 { nhStr("pet_mark_color"), nhStr("Pet_mark_color"), XtRPixel,
1074 sizeof(XtRPixel), XtOffset(AppResources *, pet_mark_color), XtRString,
1075 nhStr("Red") },
1076 { nhStr("pilemark_bitmap"), nhStr("Pilemark_bitmap"), XtRString,
1077 sizeof(String), XtOffset(AppResources *, pilemark_bitmap), XtRString,
1078 nhStr("pilemark.xbm") },
1079 { nhStr("pilemark_color"), nhStr("Pilemark_color"), XtRPixel,
1080 sizeof(XtRPixel), XtOffset(AppResources *, pilemark_color), XtRString,
1081 nhStr("Green") },
1082 #ifdef GRAPHIC_TOMBSTONE
1083 { nhStr("tombstone"), "Tombstone", XtRString, sizeof(String),
1084 XtOffset(AppResources *, tombstone), XtRString, "rip.xpm" },
1085 { nhStr("tombtext_x"), "Tombtext_x", XtRInt, sizeof(int),
1086 XtOffset(AppResources *, tombtext_x), XtRString, "155" },
1087 { nhStr("tombtext_y"), "Tombtext_y", XtRInt, sizeof(int),
1088 XtOffset(AppResources *, tombtext_y), XtRString, "78" },
1089 { nhStr("tombtext_dx"), "Tombtext_dx", XtRInt, sizeof(int),
1090 XtOffset(AppResources *, tombtext_dx), XtRString, "0" },
1091 { nhStr("tombtext_dy"), "Tombtext_dy", XtRInt, sizeof(int),
1092 XtOffset(AppResources *, tombtext_dy), XtRString, "13" },
1093 #endif
1096 void
1097 X11_init_nhwindows(argcp, argv)
1098 int *argcp;
1099 char **argv;
1101 int i;
1102 Cardinal num_args;
1103 Arg args[4];
1104 uid_t savuid;
1106 /* Init windows to nothing. */
1107 for (i = 0; i < MAX_WINDOWS; i++)
1108 window_list[i].type = NHW_NONE;
1110 /* add another option that can be set */
1111 set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
1113 load_default_resources(); /* create default_resource_data[] */
1116 * setuid hack: make sure that if anethack is setuid, to use real uid
1117 * when opening X11 connections, in case the user is using xauth, since
1118 * the "games" or whatever user probably doesn't have permission to open
1119 * a window on the user's display. This code is harmless if the binary
1120 * is not installed setuid. See include/system.h on compilation failures.
1122 savuid = geteuid();
1123 (void) seteuid(getuid());
1125 XSetIOErrorHandler((XIOErrorHandler) hangup);
1127 num_args = 0;
1128 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
1130 toplevel = XtAppInitialize(&app_context, "aNetHack", /* application */
1131 (XrmOptionDescList) 0, 0, /* options list */
1132 argcp, (String *) argv, /* command line */
1133 default_resource_data, /* fallback resources */
1134 (ArgList) args, num_args);
1135 XtOverrideTranslations(toplevel,
1136 XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
1138 /* We don't need to realize the top level widget. */
1140 #ifdef TEXTCOLOR
1141 /* add new color converter to deal with overused colormaps */
1142 XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
1143 (XtConvertArgList) nhcolorConvertArgs,
1144 XtNumber(nhcolorConvertArgs), XtCacheByDisplay,
1145 nhFreePixel);
1146 #endif /* TEXTCOLOR */
1148 /* Register the actions mentioned in "actions". */
1149 XtAppAddActions(app_context, actions, XtNumber(actions));
1151 /* Get application-wide resources */
1152 XtGetApplicationResources(toplevel, (XtPointer) &appResources, resources,
1153 XtNumber(resources), (ArgList) 0, ZERO);
1155 /* Initialize other things. */
1156 init_standard_windows();
1158 /* Give the window manager an icon to use; toplevel must be realized. */
1159 if (appResources.icon && *appResources.icon) {
1160 struct icon_info *ip;
1162 for (ip = icon_data; ip->name; ip++)
1163 if (!strcmp(appResources.icon, ip->name)) {
1164 icon_pixmap = XCreateBitmapFromData(
1165 XtDisplay(toplevel), XtWindow(toplevel),
1166 (genericptr_t) ip->bits, ip->width, ip->height);
1167 if (icon_pixmap != None) {
1168 XWMHints hints;
1170 (void) memset((genericptr_t) &hints, 0, sizeof(XWMHints));
1171 hints.flags = IconPixmapHint;
1172 hints.icon_pixmap = icon_pixmap;
1173 XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel),
1174 &hints);
1176 break;
1180 /* end of setuid hack: reset uid back to the "games" uid */
1181 (void) seteuid(savuid);
1183 x_inited = TRUE; /* X is now initialized */
1185 release_default_resources();
1187 /* Display the startup banner in the message window. */
1188 for (i = 1; i <= 4 + 2; ++i) /* (values beyond 4 yield blank lines) */
1189 X11_putstr(WIN_MESSAGE, 0, copyright_banner_line(i));
1193 * All done.
1195 /* ARGSUSED */
1196 void
1197 X11_exit_nhwindows(dummy)
1198 const char *dummy;
1200 extern Pixmap tile_pixmap; /* from winmap.c */
1202 nhUse(dummy);
1204 /* explicitly free the icon and tile pixmaps */
1205 if (icon_pixmap != None) {
1206 XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1207 icon_pixmap = None;
1209 if (tile_pixmap != None) {
1210 XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1211 tile_pixmap = None;
1213 if (WIN_INVEN != WIN_ERR)
1214 X11_destroy_nhwindow(WIN_INVEN);
1215 if (WIN_STATUS != WIN_ERR)
1216 X11_destroy_nhwindow(WIN_STATUS);
1217 if (WIN_MAP != WIN_ERR)
1218 X11_destroy_nhwindow(WIN_MAP);
1219 if (WIN_MESSAGE != WIN_ERR)
1220 X11_destroy_nhwindow(WIN_MESSAGE);
1222 release_getline_widgets();
1223 release_yn_widgets();
1226 #ifdef X11_HANGUP_SIGNAL
1227 static void
1228 X11_sig(sig) /* Unix signal handler */
1229 int sig;
1231 XtNoticeSignal(X11_sig_id);
1232 hangup(sig);
1235 static void
1236 X11_sig_cb(not_used, id)
1237 XtPointer not_used;
1238 XtSignalId *id;
1240 XEvent event;
1241 XClientMessageEvent *mesg;
1243 nhUse(not_used);
1244 nhUse(id);
1246 /* Set up a fake message to the event handler. */
1247 mesg = (XClientMessageEvent *) &event;
1248 mesg->type = ClientMessage;
1249 mesg->message_type = XA_STRING;
1250 mesg->format = 8;
1252 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1253 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1254 (XEvent *) mesg);
1256 #endif
1258 /* delay_output ----------------------------------------------------------- */
1261 * Timeout callback for delay_output(). Send a fake message to the map
1262 * window.
1264 /* ARGSUSED */
1265 static void
1266 d_timeout(client_data, id)
1267 XtPointer client_data;
1268 XtIntervalId *id;
1270 XEvent event;
1271 XClientMessageEvent *mesg;
1273 nhUse(client_data);
1274 nhUse(id);
1276 /* Set up a fake message to the event handler. */
1277 mesg = (XClientMessageEvent *) &event;
1278 mesg->type = ClientMessage;
1279 mesg->message_type = XA_STRING;
1280 mesg->format = 8;
1281 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1282 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1283 (XEvent *) mesg);
1287 * Delay for 50ms. This is not implemented asynch. Maybe later.
1288 * Start the timeout, then wait in the event loop. The timeout
1289 * function will send an event to the map window which will be waiting
1290 * for a sent event.
1292 void
1293 X11_delay_output()
1295 if (!x_inited)
1296 return;
1298 (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1300 /* The timeout function will enable the event loop exit. */
1301 (void) x_event(EXIT_ON_SENT_EVENT);
1304 /* X11_hangup ------------------------------------------------------------- */
1305 /* ARGSUSED */
1306 static void
1307 X11_hangup(w, event, params, num_params)
1308 Widget w;
1309 XEvent *event;
1310 String *params;
1311 Cardinal *num_params;
1313 nhUse(w);
1314 nhUse(event);
1315 nhUse(params);
1316 nhUse(num_params);
1318 hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1319 exit_x_event = TRUE;
1322 /* askname ---------------------------------------------------------------- */
1323 /* ARGSUSED */
1324 static void
1325 askname_delete(w, event, params, num_params)
1326 Widget w;
1327 XEvent *event;
1328 String *params;
1329 Cardinal *num_params;
1331 nhUse(event);
1332 nhUse(params);
1333 nhUse(num_params);
1335 nh_XtPopdown(w);
1336 (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1337 exit_x_event = TRUE;
1340 /* Callback for askname dialog widget. */
1341 /* ARGSUSED */
1342 static void
1343 askname_done(w, client_data, call_data)
1344 Widget w;
1345 XtPointer client_data;
1346 XtPointer call_data;
1348 unsigned len;
1349 char *s;
1350 Widget dialog = (Widget) client_data;
1352 nhUse(w);
1353 nhUse(call_data);
1355 s = (char *) GetDialogResponse(dialog);
1357 len = strlen(s);
1358 if (len == 0) {
1359 X11_nhbell();
1360 return;
1363 /* Truncate name if necessary */
1364 if (len >= sizeof plname - 1)
1365 len = sizeof plname - 1;
1367 (void) strncpy(plname, s, len);
1368 plname[len] = '\0';
1369 XtFree(s);
1371 nh_XtPopdown(XtParent(dialog));
1372 exit_x_event = TRUE;
1375 /* ask player for character's name to replace generic name "player" (or other
1376 values; see config.h) after 'anethack -u player' or OPTIONS=name:player */
1377 void
1378 X11_askname()
1380 Widget popup, dialog;
1381 Arg args[1];
1383 XtSetArg(args[0], XtNallowShellResize, True);
1385 popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel,
1386 args, ONE);
1387 XtOverrideTranslations(popup,
1388 XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1390 dialog = CreateDialog(popup, nhStr("dialog"), askname_done,
1391 (XtCallbackProc) 0);
1393 SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */
1394 SetDialogResponse(dialog, nhStr(""), PL_NSIZ); /* set default answer */
1396 XtRealizeWidget(popup);
1397 positionpopup(popup, TRUE); /* center,bottom */
1399 nh_XtPopup(popup, (int) XtGrabExclusive, dialog);
1401 /* The callback will enable the event loop exit. */
1402 (void) x_event(EXIT_ON_EXIT);
1404 XtDestroyWidget(dialog);
1405 XtDestroyWidget(popup);
1408 /* getline ---------------------------------------------------------------- */
1409 /* This uses Tim Theisen's dialog widget set (from GhostView). */
1411 static Widget getline_popup, getline_dialog;
1413 #define CANCEL_STR "\033"
1414 static char *getline_input;
1416 /* Callback for getline dialog widget. */
1417 /* ARGSUSED */
1418 static void
1419 done_button(w, client_data, call_data)
1420 Widget w;
1421 XtPointer client_data;
1422 XtPointer call_data;
1424 int len;
1425 char *s;
1426 Widget dialog = (Widget) client_data;
1428 nhUse(w);
1429 nhUse(call_data);
1431 s = (char *) GetDialogResponse(dialog);
1432 len = strlen(s);
1434 /* Truncate input if necessary */
1435 if (len >= BUFSZ)
1436 len = BUFSZ - 1;
1438 (void) strncpy(getline_input, s, len);
1439 getline_input[len] = '\0';
1440 XtFree(s);
1442 nh_XtPopdown(XtParent(dialog));
1443 exit_x_event = TRUE;
1446 /* ARGSUSED */
1447 static void
1448 getline_delete(w, event, params, num_params)
1449 Widget w;
1450 XEvent *event;
1451 String *params;
1452 Cardinal *num_params;
1454 nhUse(event);
1455 nhUse(params);
1456 nhUse(num_params);
1458 Strcpy(getline_input, CANCEL_STR);
1459 nh_XtPopdown(w);
1460 exit_x_event = TRUE;
1463 /* Callback for getline dialog widget. */
1464 /* ARGSUSED */
1465 static void
1466 abort_button(w, client_data, call_data)
1467 Widget w;
1468 XtPointer client_data;
1469 XtPointer call_data;
1471 Widget dialog = (Widget) client_data;
1473 nhUse(w);
1474 nhUse(call_data);
1476 Strcpy(getline_input, CANCEL_STR);
1477 nh_XtPopdown(XtParent(dialog));
1478 exit_x_event = TRUE;
1481 static void
1482 release_getline_widgets()
1484 if (getline_dialog)
1485 XtDestroyWidget(getline_dialog), getline_dialog = (Widget) 0;
1486 if (getline_popup)
1487 XtDestroyWidget(getline_popup), getline_popup = (Widget) 0;
1490 void
1491 X11_getlin(question, input)
1492 const char *question;
1493 char *input;
1495 getline_input = input;
1497 flush_screen(1);
1498 if (!getline_popup) {
1499 Arg args[1];
1501 XtSetArg(args[0], XtNallowShellResize, True);
1503 getline_popup = XtCreatePopupShell("getline",
1504 transientShellWidgetClass,
1505 toplevel, args, ONE);
1506 XtOverrideTranslations(getline_popup,
1507 XtParseTranslationTable(
1508 "<Message>WM_PROTOCOLS: getline_delete()"));
1510 getline_dialog = CreateDialog(getline_popup, nhStr("dialog"),
1511 done_button, abort_button);
1513 XtRealizeWidget(getline_popup);
1514 XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1515 &wm_delete_window, 1);
1517 SetDialogPrompt(getline_dialog, (String) question); /* set prompt */
1518 /* 60: make the answer widget be wide enough to hold 60 characters,
1519 or the length of the prompt string, whichever is bigger. User's
1520 response can be longer--when limit is reached, value-so-far will
1521 slide left hiding some chars at the beginning of the response but
1522 making room to type more. [Prior to 3.6.1, width wasn't specifiable
1523 and answer box always got sized to match the width of the prompt.] */
1524 SetDialogResponse(getline_dialog, nhStr(""), 60); /* set default answer */
1525 positionpopup(getline_popup, TRUE); /* center,bottom */
1527 nh_XtPopup(getline_popup, (int) XtGrabExclusive, getline_dialog);
1529 /* The callback will enable the event loop exit. */
1530 (void) x_event(EXIT_ON_EXIT);
1533 /* Display file ----------------------------------------------------------- */
1534 static const char display_translations[] = "#override\n\
1535 <Key>q: dismiss_file()\n\
1536 <Key>Escape: dismiss_file()\n\
1537 <BtnDown>: dismiss_file()";
1539 /* WM_DELETE_WINDOW callback for file dismissal. */
1540 /*ARGSUSED*/
1541 static void
1542 delete_file(w, event, params, num_params)
1543 Widget w;
1544 XEvent *event;
1545 String *params;
1546 Cardinal *num_params;
1548 nhUse(event);
1549 nhUse(params);
1550 nhUse(num_params);
1552 nh_XtPopdown(w);
1553 XtDestroyWidget(w);
1556 /* Callback for file dismissal. */
1557 /*ARGSUSED*/
1558 static void
1559 dismiss_file(w, event, params, num_params)
1560 Widget w;
1561 XEvent *event;
1562 String *params;
1563 Cardinal *num_params;
1565 Widget popup = XtParent(w);
1567 nhUse(event);
1568 nhUse(params);
1569 nhUse(num_params);
1571 nh_XtPopdown(popup);
1572 XtDestroyWidget(popup);
1575 void
1576 X11_display_file(str, complain)
1577 const char *str;
1578 boolean complain;
1580 dlb *fp;
1581 Arg args[12];
1582 Cardinal num_args;
1583 Widget popup, dispfile;
1584 Position top_margin, bottom_margin, left_margin, right_margin;
1585 XFontStruct *fs;
1586 int new_width, new_height;
1587 #define LLEN 128
1588 char line[LLEN];
1589 int num_lines;
1590 char *textlines, *bp;
1591 int charcount;
1593 /* Use the port-independent file opener to see if the file exists. */
1594 fp = dlb_fopen(str, RDTMODE);
1596 if (!fp) {
1597 if (complain)
1598 pline("Cannot open %s. Sorry.", str);
1599 return; /* it doesn't exist, ignore */
1603 * Count the number of lines and characters in the file.
1605 num_lines = 0;
1606 charcount = 1;
1607 while (dlb_fgets(line, LLEN, fp)) {
1608 num_lines++;
1609 charcount += strlen(line);
1612 (void) dlb_fclose(fp);
1614 /* Ignore empty files */
1615 if (num_lines == 0)
1616 return;
1618 /* If over the max window size, truncate the window size to the max */
1619 if (num_lines >= DISPLAY_FILE_SIZE)
1620 num_lines = DISPLAY_FILE_SIZE;
1623 * Re-open the file and read the data into a buffer. Cannot use
1624 * the XawAsciiFile type of widget, because that is not DLB-aware.
1626 textlines = (char *) alloc((unsigned int) charcount);
1627 textlines[0] = '\0';
1629 fp = dlb_fopen(str, RDTMODE);
1631 bp = textlines;
1632 while (dlb_fgets(line, LLEN, fp)) {
1633 Strcpy((bp = eos(bp)), line);
1636 (void) dlb_fclose(fp);
1638 num_args = 0;
1639 XtSetArg(args[num_args], nhStr(XtNtitle), str); num_args++;
1641 popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
1642 toplevel, args, num_args);
1643 XtOverrideTranslations(popup,
1644 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
1646 num_args = 0;
1647 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
1648 XawtextScrollWhenNeeded); num_args++;
1649 XtSetArg(args[num_args], nhStr(XtNscrollVertical), XawtextScrollAlways);
1650 num_args++;
1651 XtSetArg(args[num_args], nhStr(XtNtype), XawAsciiString); num_args++;
1652 XtSetArg(args[num_args], nhStr(XtNstring), textlines); num_args++;
1653 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False); num_args++;
1654 XtSetArg(args[num_args], nhStr(XtNtranslations),
1655 XtParseTranslationTable(display_translations)); num_args++;
1657 dispfile = XtCreateManagedWidget("text", /* name */
1658 asciiTextWidgetClass, popup, /* parent */
1659 args, num_args);
1661 /* Get font and border information. */
1662 num_args = 0;
1663 XtSetArg(args[num_args], nhStr(XtNfont), &fs); num_args++;
1664 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin); num_args++;
1665 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
1666 num_args++;
1667 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin); num_args++;
1668 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
1669 num_args++;
1670 XtGetValues(dispfile, args, num_args);
1673 * The data files are currently set up assuming an 80 char wide window
1674 * and a fixed width font. Soo..
1676 new_height =
1677 num_lines * nhFontHeight(dispfile) + top_margin + bottom_margin;
1678 new_width = 80 * fs->max_bounds.width + left_margin + right_margin;
1680 /* Set the new width and height. */
1681 num_args = 0;
1682 XtSetArg(args[num_args], XtNwidth, new_width); num_args++;
1683 XtSetArg(args[num_args], XtNheight, new_height); num_args++;
1684 XtSetValues(dispfile, args, num_args);
1686 nh_XtPopup(popup, (int) XtGrabNone, (Widget) 0);
1687 free(textlines);
1690 /* yn_function ------------------------------------------------------------ */
1691 /* (not threaded) */
1693 static const char *yn_quitchars = " \n\r";
1694 static const char *yn_choices; /* string of acceptable input */
1695 static char yn_def;
1696 static char yn_return; /* return value */
1697 static char yn_esc_map; /* ESC maps to this char. */
1698 static Widget yn_popup; /* popup for the yn fuction (created once) */
1699 static Widget yn_label; /* label for yn function (created once) */
1700 static boolean yn_getting_num; /* TRUE if accepting digits */
1701 static boolean yn_preserve_case; /* default is to force yn to lower case */
1702 static int yn_ndigits; /* digit count */
1703 static long yn_val; /* accumulated value */
1705 static const char yn_translations[] = "#override\n\
1706 <Key>: yn_key()";
1709 * Convert the given key event into a character. If the key maps to
1710 * more than one character only the first is returned. If there is
1711 * no conversion (i.e. just the CTRL key hit) a NUL is returned.
1713 char
1714 key_event_to_char(key)
1715 XKeyEvent *key;
1717 char keystring[MAX_KEY_STRING];
1718 int nbytes;
1719 boolean meta = !!(key->state & Mod1Mask);
1721 nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0,
1722 (XComposeStatus *) 0);
1724 /* Modifier keys return a zero lengh string when pressed. */
1725 if (nbytes == 0)
1726 return '\0';
1728 return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
1732 * Called when we get a WM_DELETE_WINDOW event on a yn window.
1734 /* ARGSUSED */
1735 static void
1736 yn_delete(w, event, params, num_params)
1737 Widget w;
1738 XEvent *event;
1739 String *params;
1740 Cardinal *num_params;
1742 nhUse(w);
1743 nhUse(event);
1744 nhUse(params);
1745 nhUse(num_params);
1747 yn_getting_num = FALSE;
1748 /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */
1749 yn_return = yn_choices ? yn_esc_map : '\033';
1750 exit_x_event = TRUE; /* exit our event handler */
1754 * Called when we get a key press event on a yn window.
1756 /* ARGSUSED */
1757 static void
1758 yn_key(w, event, params, num_params)
1759 Widget w;
1760 XEvent *event;
1761 String *params;
1762 Cardinal *num_params;
1764 char ch;
1766 if (appResources.slow && !input_func)
1767 map_input(w, event, params, num_params);
1769 ch = key_event_to_char((XKeyEvent *) event);
1771 if (ch == '\0') { /* don't accept nul char or modifier event */
1772 /* no bell */
1773 return;
1776 if (!yn_choices) { /* accept any input */
1777 yn_return = ch;
1778 } else {
1779 if (!yn_preserve_case)
1780 ch = lowc(ch); /* move to lower case */
1782 if (ch == '\033') {
1783 yn_getting_num = FALSE;
1784 yn_return = yn_esc_map;
1785 } else if (index(yn_quitchars, ch)) {
1786 yn_return = yn_def;
1787 } else if (index(yn_choices, ch)) {
1788 if (ch == '#') {
1789 if (yn_getting_num) { /* don't select again */
1790 X11_nhbell();
1791 return;
1793 yn_getting_num = TRUE;
1794 yn_ndigits = 0;
1795 yn_val = 0;
1796 return; /* wait for more input */
1798 yn_return = ch;
1799 if (ch != 'y')
1800 yn_getting_num = FALSE;
1801 } else {
1802 if (yn_getting_num) {
1803 if (digit(ch)) {
1804 yn_ndigits++;
1805 yn_val = (yn_val * 10) + (long) (ch - '0');
1806 return; /* wait for more input */
1808 if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) {
1809 yn_ndigits--;
1810 yn_val = yn_val / 10;
1811 return; /* wait for more input */
1814 X11_nhbell(); /* no match */
1815 return;
1818 if (yn_getting_num) {
1819 yn_return = '#';
1820 if (yn_val < 0)
1821 yn_val = 0;
1822 yn_number = yn_val; /* assign global */
1825 exit_x_event = TRUE; /* exit our event handler */
1828 /* called at exit time */
1829 static void
1830 release_yn_widgets()
1832 if (yn_label)
1833 XtDestroyWidget(yn_label), yn_label = (Widget) 0;
1834 if (yn_popup)
1835 XtDestroyWidget(yn_popup), yn_popup = (Widget) 0;
1838 /* X11-specific edition of yn_function(), the routine called by the core
1839 to show a prompt and get a single keystroke answer, often 'y' vs 'n' */
1840 char
1841 X11_yn_function(ques, choices, def)
1842 const char *ques;
1843 const char *choices; /* string of possible response chars; any char if Null */
1844 char def; /* default response if user hits <space> or <return> */
1846 char buf[BUFSZ];
1847 Arg args[4];
1848 Cardinal num_args;
1850 yn_choices = choices; /* set up globals for callback to use */
1851 yn_def = def;
1852 yn_preserve_case = !choices; /* preserve case when an arbitrary
1853 response is allowed */
1856 * This is sort of a kludge. There are quite a few places in the main
1857 * anethack code where a pline containing information is followed by a
1858 * call to yn_function(). There is no flush of the message window
1859 * (it is implicit in the tty window port), so the line never shows
1860 * up for us! Solution: do our own flush.
1862 if (WIN_MESSAGE != WIN_ERR)
1863 display_message_window(&window_list[WIN_MESSAGE]);
1865 if (choices) {
1866 char *cb, choicebuf[QBUFSZ];
1868 Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
1869 /* default when choices are present is to force yn answer to
1870 lowercase unless one or more choices are explicitly uppercase;
1871 check this before stripping the hidden choices */
1872 for (cb = choicebuf; *cb; ++cb)
1873 if ('A' <= *cb && *cb <= 'Z') {
1874 yn_preserve_case = TRUE;
1875 break;
1877 if ((cb = index(choicebuf, '\033')) != 0)
1878 *cb = '\0';
1879 /* ques [choices] (def) */
1880 if ((int) (1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ)
1881 panic("X11_yn_function: question too long");
1882 (void) strncpy(buf, ques, QBUFSZ - 1);
1883 buf[QBUFSZ - 1] = '\0';
1884 Sprintf(eos(buf), " [%s]", choicebuf);
1885 if (def)
1886 Sprintf(eos(buf), " (%c)", def);
1887 Strcat(buf, " ");
1889 /* escape maps to 'q' or 'n' or default, in that order */
1890 yn_esc_map = (index(choices, 'q') ? 'q'
1891 : index(choices, 'n') ? 'n'
1892 : def);
1893 } else {
1894 if ((int) (1 + strlen(ques) + 1) >= BUFSZ)
1895 panic("X11_yn_function: question too long");
1896 Strcpy(buf, ques);
1897 Strcat(buf, " ");
1901 * The 'slow' resource is misleadingly named. When it is True, we
1902 * re-use the same one-line widget above the map for all yn prompt
1903 * responses instead of re-using a popup widget for each one. It's
1904 * crucial for client/server configs where the server is slow putting
1905 * up a popup or requires click-to-focus to respond to the popup, but
1906 * is also useful for players who just want to be able to look at the
1907 * same location to read questions they're being asked to answer.
1910 if (appResources.slow) {
1912 * 'slow': the yn_label widget was created when the map and
1913 * status widgets were, and is positioned between them. It
1914 * will persist until end of game. All we need to do for
1915 * yn_function is direct keystroke input to the yn response
1916 * handler and reset its label to be the prompt text (below).
1918 input_func = yn_key;
1919 highlight_yn(FALSE); /* expose yn_label as separate from map */
1920 } else if (!yn_label) {
1922 * Not 'slow'; create a persistent widget that will be popped up
1923 * as needed, then down again, and last until end of game. The
1924 * associated yn_label widget is used to track whether it exists.
1926 XtSetArg(args[0], XtNallowShellResize, True);
1927 yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
1928 toplevel, args, ONE);
1929 XtOverrideTranslations(yn_popup,
1930 XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
1932 num_args = 0;
1933 XtSetArg(args[num_args], XtNtranslations,
1934 XtParseTranslationTable(yn_translations)); num_args++;
1935 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass,
1936 yn_popup, args, num_args);
1938 XtRealizeWidget(yn_popup);
1939 XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
1940 &wm_delete_window, 1);
1943 /* set the label of the yn widget to be the prompt text */
1944 num_args = 0;
1945 XtSetArg(args[num_args], XtNlabel, buf); num_args++;
1946 XtSetValues(yn_label, args, num_args);
1948 if (!appResources.slow) {
1950 * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
1951 * need to set the label twice to get the size to change.
1953 num_args = 0;
1954 XtSetArg(args[num_args], XtNlabel, buf); num_args++;
1955 XtSetValues(yn_label, args, num_args);
1957 positionpopup(yn_popup, TRUE);
1958 nh_XtPopup(yn_popup, (int) XtGrabExclusive, yn_label);
1961 yn_getting_num = FALSE;
1962 (void) x_event(EXIT_ON_EXIT); /* get keystroke(s) */
1964 if (appResources.slow) {
1965 /* keystrokes now belong to the map */
1966 input_func = 0;
1967 /* erase the prompt */
1968 num_args = 0;
1969 XtSetArg(args[num_args], XtNlabel, " "); num_args++;
1970 XtSetValues(yn_label, args, num_args);
1971 highlight_yn(FALSE); /* disguise yn_label as part of map */
1972 } else {
1973 nh_XtPopdown(yn_popup); /* this removes the event grab */
1976 return yn_return;
1979 /* used when processing window-capability-specific run-time options;
1980 we support toggling tiles on and off via iflags.wc_tiled_map */
1981 void
1982 X11_preference_update(pref)
1983 const char *pref;
1985 if (!strcmp(pref, "tiled_map")) {
1986 if (WIN_MAP != WIN_ERR)
1987 display_map_window(&window_list[WIN_MAP]);
1991 /* End global functions =================================================== */
1994 * Before we wait for input via nhgetch() and nh_poskey(), we need to
1995 * do some pre-processing.
1997 static int
1998 input_event(exit_condition)
1999 int exit_condition;
2001 if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
2002 check_turn_events();
2003 if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
2004 check_cursor_visibility(&window_list[WIN_MAP]);
2005 if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
2006 set_last_pause(&window_list[WIN_MESSAGE]);
2008 return x_event(exit_condition);
2011 /*ARGSUSED*/
2012 void
2013 msgkey(w, data, event)
2014 Widget w;
2015 XtPointer data;
2016 XEvent *event;
2018 Cardinal num = 0;
2020 nhUse(w);
2021 nhUse(data);
2023 map_input(window_list[WIN_MAP].w, event, (String *) 0, &num);
2026 /* only called for autofocus */
2027 /*ARGSUSED*/
2028 static void
2029 win_visible(w, data, event, flag)
2030 Widget w;
2031 XtPointer data; /* client_data not used */
2032 XEvent *event;
2033 Boolean *flag; /* continue_to_dispatch flag not used */
2035 XVisibilityEvent *vis_event = (XVisibilityEvent *) event;
2037 nhUse(data);
2038 nhUse(flag);
2040 if (vis_event->state != VisibilityFullyObscured) {
2041 /* one-time operation; cancel ourself */
2042 XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
2043 win_visible, (XtPointer) 0);
2044 /* grab initial input focus */
2045 XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
2049 /* if 'slow' and 'highlight_prompt', set the yn_label widget to look like
2050 part of the map when idle or to invert background and foreground when
2051 a prompt is active */
2052 void
2053 highlight_yn(init)
2054 boolean init;
2056 struct xwindow *xmap;
2058 if (!appResources.slow || !appResources.highlight_prompt)
2059 return;
2061 /* first time through, WIN_MAP isn't fully initiialized yet */
2062 xmap = ((map_win != WIN_ERR) ? &window_list[map_win]
2063 : (WIN_MAP != WIN_ERR) ? &window_list[WIN_MAP] : 0);
2065 if (init && xmap) {
2066 Arg args[2];
2067 XGCValues vals;
2068 unsigned long fg_bg = (GCForeground | GCBackground);
2069 GC gc = (xmap->map_information->is_tile
2070 ? xmap->map_information->tile_map.white_gc
2071 : xmap->map_information->text_map.copy_gc);
2073 (void) memset((genericptr_t) &vals, 0, sizeof vals);
2074 if (XGetGCValues(XtDisplay(xmap->w), gc, fg_bg, &vals)) {
2075 XtSetArg(args[0], XtNforeground, vals.foreground);
2076 XtSetArg(args[1], XtNbackground, vals.background);
2077 XtSetValues(yn_label, args, TWO);
2079 } else
2080 swap_fg_bg(yn_label);
2084 * Set up the playing console. This has three major parts: the
2085 * message window, the map, and the status window.
2087 * For configs specifying the 'slow' resource, the yn_label widget
2088 * is placed above the map and below the message window. Prompts
2089 * requiring a single character response are displayed there rather
2090 * than using a popup.
2092 static void
2093 init_standard_windows()
2095 Widget form, message_viewport, map_viewport, status;
2096 Arg args[8];
2097 Cardinal num_args;
2098 Dimension message_vp_width, map_vp_width, status_width, max_width;
2099 int map_vp_hd, status_hd;
2100 struct xwindow *wp;
2102 num_args = 0;
2103 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
2104 form = XtCreateManagedWidget("anethack", panedWidgetClass, toplevel, args,
2105 num_args);
2107 XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey,
2108 (XtPointer) 0);
2110 if (appResources.autofocus)
2111 XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible,
2112 (XtPointer) 0);
2115 * Create message window.
2117 WIN_MESSAGE = message_win = find_free_window();
2118 wp = &window_list[message_win];
2119 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2120 wp->popup = (Widget) 0;
2121 create_message_window(wp, FALSE, form);
2122 message_viewport = XtParent(wp->w);
2124 /* Tell the form that contains it that resizes are OK. */
2125 num_args = 0;
2126 XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2127 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2128 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
2129 XtSetValues(message_viewport, args, num_args);
2131 if (appResources.slow) {
2132 num_args = 0;
2133 XtSetArg(args[num_args], XtNtranslations,
2134 XtParseTranslationTable(yn_translations)); num_args++;
2135 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form,
2136 args, num_args);
2137 num_args = 0;
2138 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2139 num_args++;
2140 XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft);
2141 num_args++;
2142 XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2143 XtSetArg(args[num_args], nhStr(XtNlabel), " "); num_args++;
2144 XtSetValues(yn_label, args, num_args);
2148 * Create the map window & viewport and chain the viewport beneath the
2149 * message_viewport.
2151 map_win = find_free_window();
2152 wp = &window_list[map_win];
2153 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2154 wp->popup = (Widget) 0;
2155 create_map_window(wp, FALSE, form);
2156 map_viewport = XtParent(wp->w);
2158 /* Chain beneath message_viewport or yn window. */
2159 num_args = 0;
2160 if (appResources.slow) {
2161 XtSetArg(args[num_args], nhStr(XtNfromVert), yn_label); num_args++;
2162 } else {
2163 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2164 num_args++;
2166 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2167 XtSetValues(map_viewport, args, num_args);
2169 /* Create the status window, with the form as it's parent. */
2170 status_win = find_free_window();
2171 wp = &window_list[status_win];
2172 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2173 wp->popup = (Widget) 0;
2174 create_status_window(wp, FALSE, form);
2175 status = wp->w;
2178 * Chain the status window beneath the viewport. Mark the left and right
2179 * edges so that they stay a fixed distance from the left edge of the
2180 * parent, as well as the top and bottom edges so that they stay a fixed
2181 * distance from the bottom of the parent. We do this so that the status
2182 * will never expand or contract.
2184 num_args = 0;
2185 XtSetArg(args[num_args], nhStr(XtNfromVert), map_viewport); num_args++;
2186 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2187 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
2188 XtSetArg(args[num_args], nhStr(XtNtop), XtChainBottom); num_args++;
2189 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2190 XtSetValues(status, args, num_args);
2193 * Realize the popup so that the status widget knows it's size.
2195 * If we unset MappedWhenManaged then the DECwindow driver doesn't
2196 * attach the anethack toplevel to the highest virtual root window.
2197 * So don't do it.
2199 /* XtSetMappedWhenManaged(toplevel, False); */
2200 XtRealizeWidget(toplevel);
2201 wm_delete_window = XInternAtom(XtDisplay(toplevel),
2202 "WM_DELETE_WINDOW", False);
2203 XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
2204 &wm_delete_window, 1);
2207 * Resize to at most full-screen.
2210 #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
2212 int screen_width = WidthOfScreen(XtScreen(wp->w));
2213 int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
2214 Dimension form_width, form_height;
2216 XtSetArg(args[0], XtNwidth, &form_width);
2217 XtSetArg(args[1], XtNheight, &form_height);
2218 XtGetValues(toplevel, args, TWO);
2220 if (form_width > screen_width || form_height > screen_height) {
2221 XtSetArg(args[0], XtNwidth, min(form_width, screen_width));
2222 XtSetArg(args[1], XtNheight, min(form_height, screen_height));
2223 XtSetValues(toplevel, args, TWO);
2224 XMoveWindow(XtDisplay(toplevel), XtWindow(toplevel), 0,
2225 TITLEBAR_SPACE);
2227 #undef TITLEBAR_SPACE
2230 post_process_tiles(); /* after toplevel is realized */
2233 * Now get the default widths of the windows.
2235 XtSetArg(args[0], nhStr(XtNwidth), &message_vp_width);
2236 XtGetValues(message_viewport, args, ONE);
2237 XtSetArg(args[0], nhStr(XtNwidth), &map_vp_width);
2238 XtSetArg(args[1], nhStr(XtNhorizDistance), &map_vp_hd);
2239 XtGetValues(map_viewport, args, TWO);
2240 XtSetArg(args[0], nhStr(XtNwidth), &status_width);
2241 XtSetArg(args[1], nhStr(XtNhorizDistance), &status_hd);
2242 XtGetValues(status, args, TWO);
2245 * Adjust positions and sizes. The message viewport widens out to the
2246 * widest width. Both the map and status are centered by adjusting
2247 * their horizDistance.
2249 if (map_vp_width < status_width || map_vp_width < message_vp_width) {
2250 if (status_width > message_vp_width) {
2251 XtSetArg(args[0], nhStr(XtNwidth), status_width);
2252 XtSetValues(message_viewport, args, ONE);
2253 max_width = status_width;
2254 } else {
2255 max_width = message_vp_width;
2257 XtSetArg(args[0], nhStr(XtNhorizDistance),
2258 map_vp_hd + ((int) (max_width - map_vp_width) / 2));
2259 XtSetValues(map_viewport, args, ONE);
2261 } else { /* map is widest */
2262 XtSetArg(args[0], nhStr(XtNwidth), map_vp_width);
2263 XtSetValues(message_viewport, args, ONE);
2266 * Clear all data values on the fancy status widget so that the values
2267 * used for spacing don't appear. This needs to be called some time
2268 * after the fancy status widget is realized (above, with the game popup),
2269 * but before it is popped up.
2271 null_out_status();
2273 * Set the map size to its standard size. As with the message window
2274 * above, the map window needs to be set to its constrained size until
2275 * its parent (the viewport widget) was realized.
2277 * Move the message window's slider to the bottom.
2279 set_map_size(&window_list[map_win], COLNO, ROWNO);
2280 set_message_slider(&window_list[message_win]);
2282 /* attempt to catch fatal X11 errors before the program quits */
2283 (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
2285 highlight_yn(TRUE); /* switch foreground and background */
2287 /* We can now print to the message window. */
2288 iflags.window_inited = 1;
2291 void
2292 nh_XtPopup(w, g, childwid)
2293 Widget w; /* widget */
2294 int g; /* type of grab */
2295 Widget childwid; /* child to recieve focus (can be None) */
2297 XtPopup(w, (XtGrabKind) g);
2298 XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
2299 if (appResources.autofocus)
2300 XtSetKeyboardFocus(toplevel, childwid);
2303 void
2304 nh_XtPopdown(w)
2305 Widget w;
2307 XtPopdown(w);
2308 if (appResources.autofocus)
2309 XtSetKeyboardFocus(toplevel, None);
2312 void
2313 win_X11_init(dir)
2314 int dir;
2316 if (dir != WININIT)
2317 return;
2319 #ifdef OPENWINBUG
2320 /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
2321 * two routines will not be found when linking. An apparently correct
2322 * executable is produced, along with nasty messages and a failure code
2323 * returned to make. The routines are in the static libXmu.a and
2324 * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with
2325 * static linking, we do this.
2327 if (rn2(2) > 2) { /* i.e., FALSE that an optimizer probably can't find */
2328 get_wmShellWidgetClass();
2329 get_applicationShellWidgetClass();
2331 #endif
2332 return;
2335 /* Callback
2336 * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2338 /*ARGSUSED*/
2339 void
2340 nh_keyscroll(viewport, event, params, num_params)
2341 Widget viewport;
2342 XEvent *event;
2343 String *params;
2344 Cardinal *num_params;
2346 Arg arg[2];
2347 Widget horiz_sb, vert_sb;
2348 float top, shown;
2349 Boolean do_call;
2350 int direction;
2351 Cardinal in_nparams = (num_params ? *num_params : 0);
2353 nhUse(event);
2355 if (in_nparams != 1)
2356 return; /* bad translation */
2358 direction = atoi(params[0]);
2360 horiz_sb = XtNameToWidget(viewport, "*horizontal");
2361 vert_sb = XtNameToWidget(viewport, "*vertical");
2363 if (!horiz_sb && !vert_sb) {
2364 /* Perhaps the widget enclosing this has scrollbars (could use while)
2366 Widget parent = XtParent(viewport);
2368 if (parent) {
2369 horiz_sb = XtNameToWidget(parent, "horizontal");
2370 vert_sb = XtNameToWidget(parent, "vertical");
2374 #define H_DELTA 0.25 /* distance of horiz shift */
2375 /* vert shift is half of curr distance */
2376 /* The V_DELTA is 1/2 the value of shown. */
2378 if (horiz_sb) {
2379 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2380 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2381 XtGetValues(horiz_sb, arg, TWO);
2383 do_call = True;
2385 switch (direction) {
2386 case 1:
2387 case 4:
2388 case 7:
2389 top -= H_DELTA;
2390 if (top < 0.0)
2391 top = 0.0;
2392 break;
2393 case 3:
2394 case 6:
2395 case 9:
2396 top += H_DELTA;
2397 if (top + shown > 1.0)
2398 top = 1.0 - shown;
2399 break;
2400 default:
2401 do_call = False;
2404 if (do_call) {
2405 XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2409 if (vert_sb) {
2410 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2411 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2412 XtGetValues(vert_sb, arg, TWO);
2414 do_call = True;
2416 switch (direction) {
2417 case 7:
2418 case 8:
2419 case 9:
2420 top -= shown / 2.0;
2421 if (top < 0.0)
2422 top = 0;
2423 break;
2424 case 1:
2425 case 2:
2426 case 3:
2427 top += shown / 2.0;
2428 if (top + shown > 1.0)
2429 top = 1.0 - shown;
2430 break;
2431 default:
2432 do_call = False;
2435 if (do_call) {
2436 XtCallCallbacks(vert_sb, XtNjumpProc, &top);
2441 /*winX.c*/