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. */
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
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
15 #ifdef MSDOS /* from compiler */
16 #define SHORT_FILENAMES
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>
31 /* for color support */
32 #ifdef SHORT_FILENAMES
33 #include <X11/IntrinsP.h>
35 #include <X11/IntrinsicP.h>
38 #ifdef PRESERVE_NO_SYSV
42 #undef PRESERVE_NO_SYSV
45 #ifdef SHORT_FILENAMES
46 #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
57 /* Should be defined in <X11/Intrinsic.h> but you never know */
58 #ifndef XtSpecificationRelease
59 #define XtSpecificationRelease 0
65 #include "../win/X11/nh72icon"
66 #include "../win/X11/nh56icon"
67 #include "../win/X11/nh32icon"
69 static struct icon_info
{
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
;
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
,
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 */
120 /* other defs that really should go away (they're tty specific) */
121 X11_start_screen
, X11_end_screen
,
122 #ifdef GRAPHIC_TOMBSTONE
127 X11_preference_update
, genl_getmsghistory
, genl_putmsghistory
,
128 #ifdef STATUS_VIA_WINDOWPORT
129 genl_status_init
, genl_status_finish
, genl_status_enablefield
,
131 #ifdef STATUS_HILITES
132 genl_status_threshold
,
135 genl_can_suspend_no
, /* XXX may not always be correct */
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
*));
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
);
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.
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
))))
197 if (windex
== MAX_WINDOWS
)
198 panic("find_widget: can't match widget");
203 * Find a free window slot for use.
211 for (windex
= 0, wp
= &window_list
[0]; windex
< MAX_WINDOWS
;
213 if (wp
->type
== NHW_NONE
)
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
),
231 { XtWidgetBaseOffset
,
232 (XtPointer
) (ptrdiff_t) XtOffset(Widget
, core
.colormap
),
236 #define done(type, value) \
238 if (toVal->addr != 0) { \
239 if (toVal->size < sizeof(type)) { \
240 toVal->size = sizeof(type); \
243 *(type *)(toVal->addr) = (value); \
245 static type static_val; \
246 static_val = (value); \
247 toVal->addr = (genericptr_t) &static_val; \
249 toVal->size = sizeof(type); \
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.
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 */
268 long cdiff
= 16777216; /* 2^24; hopefully our map is smaller */
270 static XColor
*table
= 0;
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)
279 if (str
!= (char *) 0) {
280 if (!XParseColor(DisplayOfScreen(screen
), colormap
, str
, &tmp
))
284 tmp
.flags
= 7; /* force to use all 3 of RGB */
288 table
= (XColor
*) XtCalloc(ncells
, sizeof(XColor
));
289 for (i
= 0; i
< ncells
; 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 */
298 for (i
= 0; i
< ncells
; i
++) {
299 if (table
[i
].flags
== tmp
.flags
) {
300 j
= (int) table
[i
].red
- (int) tmp
.red
;
304 j
= (int) table
[i
].green
- (int) tmp
.green
;
308 j
= (int) table
[i
].blue
- (int) tmp
.blue
;
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
)) {
337 nhCvtStringToPixel(dpy
, args
, num_args
, fromVal
, toVal
, closure_ret
)
343 XtPointer
*closure_ret
;
345 String str
= (String
) fromVal
->addr
;
349 XtAppContext app
= XtDisplayToApplicationContext(dpy
);
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);
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
,
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
));
384 status
= XAllocNamedColor(DisplayOfScreen(screen
), colormap
, (char *) str
,
385 &screenColor
, &exactColor
);
389 /* some versions of XAllocNamedColor don't allow #xxyyzz names */
390 if (str
[0] == '#' && XParseColor(DisplayOfScreen(screen
), colormap
,
392 && XAllocColor(DisplayOfScreen(screen
), colormap
, &exactColor
)) {
393 *closure_ret
= (char *) True
;
394 done(Pixel
, exactColor
.pixel
);
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\"");
409 /* some versions of XLookupColor also don't allow #xxyyzz names */
411 && (nhApproxColor(screen
, colormap
, (char *) str
,
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
;
425 *closure_ret
= (char *) True
;
426 done(Pixel
, screenColor
.pixel
);
432 nhFreePixel(app
, toVal
, closure
, args
, num_args
)
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);
449 screen
= *((Screen
**) args
[0].addr
);
450 colormap
= *((Colormap
*) args
[1].addr
);
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.
465 #ifdef _XawTextSink_h
467 XawTextPosition pos
= 0;
468 int resWidth
, resHeight
;
471 XtSetArg(args
[0], XtNtextSink
, &sink
);
472 XtGetValues(w
, args
, 1);
474 XawTextSinkFindPosition(sink
, pos
, 0, 0, 0, &pos
, &resWidth
, &resHeight
);
480 XtSetArg(args
[0], XtNfont
, &fs
);
481 XtGetValues(w
, args
, 1);
483 /* Assume font height is ascent + descent. */
484 return = fs
->ascent
+ fs
->descent
;
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() */
493 load_default_resources()
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");
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;
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'))
524 if (linelen
> longlen
)
530 free((genericptr_t
) inbuf
);
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 */
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;
545 free((genericptr_t
) inbuf
);
549 release_default_resources()
551 if (default_resource_data
) {
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 ======================================================= */
569 X11_raw_print_bold(str
)
576 X11_curs(window
, x
, y
)
582 if (x
< 0 || x
>= COLNO
) {
583 impossible("curs: bad x value [%d]", x
);
586 if (y
< 0 || y
>= ROWNO
) {
587 impossible("curs: bad y value [%d]", y
);
591 window_list
[window
].cursx
= x
;
592 window_list
[window
].cursy
= y
;
596 X11_putstr(window
, attr
, str
)
605 wp
= &window_list
[window
];
609 (void) strncpy(toplines
, str
, TBUFSZ
); /* for Norep(). */
610 toplines
[TBUFSZ
- 1] = 0;
611 append_message(wp
, str
);
614 adjust_status(wp
, str
);
617 impossible("putstr: called on map window \"%s\"", str
);
620 if (wp
->menu_information
->is_menu
) {
621 impossible("putstr: called on a menu window, \"%s\" discarded",
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 */
635 add_to_text_window(wp
, attr
, str
);
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. */
652 return input_event(EXIT_ON_KEY_PRESS
);
656 X11_nh_poskey(x
, y
, mod
)
659 int val
= input_event(EXIT_ON_KEY_OR_BUTTON_PRESS
);
661 if (val
== 0) { /* user clicked on a map window */
670 X11_create_nhwindow(type
)
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);
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);
693 (void) sigaction(SIGXCPU
, &sact
, (struct sigaction
*) 0);
697 (void) signal(SIGHUP
, (SIG_RET_TYPE
) X11_sig
);
699 (void) signal(SIGXCPU
, (SIG_RET_TYPE
) X11_sig
);
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
) {
718 if (type
== NHW_MESSAGE
&& message_win
!= WIN_ERR
) {
719 window
= message_win
;
720 message_win
= WIN_ERR
;
723 if (type
== NHW_STATUS
&& status_win
!= WIN_ERR
) {
725 status_win
= WIN_ERR
;
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
;
739 create_map_window(wp
, TRUE
, (Widget
) 0);
742 create_message_window(wp
, TRUE
, (Widget
) 0);
745 create_status_window(wp
, TRUE
, (Widget
) 0);
748 create_menu_window(wp
);
751 create_text_window(wp
);
754 panic("create_nhwindow: unknown type [%d]", type
);
761 X11_clear_nhwindow(window
)
767 wp
= &window_list
[window
];
771 clear_map_window(wp
);
774 clear_text_window(wp
);
779 /* do nothing for these window types */
782 panic("clear_nhwindow: unknown window type [%d]", wp
->type
);
788 X11_display_nhwindow(window
, blocking
)
795 wp
= &window_list
[window
];
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
]);
819 (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS
);
823 nh_XtPopup(wp
->popup
, (int) XtGrabNone
, wp
->w
);
824 display_message_window(wp
); /* flush messages */
828 nh_XtPopup(wp
->popup
, (int) XtGrabNone
, wp
->w
);
829 break; /* no flushing necessary */
835 n
= X11_select_menu(window
, PICK_NONE
, &selected
);
837 impossible("perminvent: %d selected??", n
);
838 free((genericptr_t
) selected
);
843 display_text_window(wp
, blocking
); /* pop up text window */
846 panic("display_nhwindow: unknown window type [%d]", wp
->type
);
852 X11_destroy_nhwindow(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
;
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 */
882 destroy_map_window(wp
);
885 destroy_menu_window(wp
);
888 destroy_text_window(wp
);
891 destroy_status_window(wp
);
894 destroy_message_window(wp
);
897 panic("destroy_nhwindow: unknown window type [%d]", wp
->type
);
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. */
922 /* We can't use XBell until toplevel has been initialized. */
924 XBell(XtDisplay(toplevel
), 0);
925 /* else print ^G ?? */
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
);
949 XFlush(XtDisplay(toplevel
));
952 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
954 X11_resume_nhwindows()
960 X11_suspend_nhwindows(str
)
968 /* Under X, we don't need to initialize the number pad. */
971 X11_number_pad(state
) /* called from options.c */
979 /* called from setftty() in unixtty.c */
986 /* called from settty() in unixtty.c */
993 #ifdef GRAPHIC_TOMBSTONE
995 X11_outrip(window
, how
, when
)
1002 check_winid(window
);
1003 wp
= &window_list
[window
];
1005 if (wp
->type
== NHW_TEXT
) {
1006 wp
->text_information
->is_rip
= TRUE
;
1008 panic("ripout on non-text window (window type [%d])", wp
->type
);
1011 calculate_rip_text(how
, when
);
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 */
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
,
1055 { nhStr("highlight_prompt"), nhStr("Highlight_prompt"), XtRBoolean
,
1056 sizeof(Boolean
), XtOffset(AppResources
*, highlight_prompt
), XtRString
,
1058 { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean
,
1059 sizeof(Boolean
), XtOffset(AppResources
*, double_tile_size
), XtRString
,
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
,
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
,
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" },
1097 X11_init_nhwindows(argcp
, argv
)
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.
1123 (void) seteuid(getuid());
1125 XSetIOErrorHandler((XIOErrorHandler
) hangup
);
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. */
1141 /* add new color converter to deal with overused colormaps */
1142 XtSetTypeConverter(XtRString
, XtRPixel
, nhCvtStringToPixel
,
1143 (XtConvertArgList
) nhcolorConvertArgs
,
1144 XtNumber(nhcolorConvertArgs
), XtCacheByDisplay
,
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
) {
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
),
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
));
1197 X11_exit_nhwindows(dummy
)
1200 extern Pixmap tile_pixmap
; /* from winmap.c */
1204 /* explicitly free the icon and tile pixmaps */
1205 if (icon_pixmap
!= None
) {
1206 XFreePixmap(XtDisplay(toplevel
), icon_pixmap
);
1209 if (tile_pixmap
!= None
) {
1210 XFreePixmap(XtDisplay(toplevel
), tile_pixmap
);
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
1228 X11_sig(sig
) /* Unix signal handler */
1231 XtNoticeSignal(X11_sig_id
);
1236 X11_sig_cb(not_used
, id
)
1241 XClientMessageEvent
*mesg
;
1246 /* Set up a fake message to the event handler. */
1247 mesg
= (XClientMessageEvent
*) &event
;
1248 mesg
->type
= ClientMessage
;
1249 mesg
->message_type
= XA_STRING
;
1252 XSendEvent(XtDisplay(window_list
[WIN_MAP
].w
),
1253 XtWindow(window_list
[WIN_MAP
].w
), False
, NoEventMask
,
1258 /* delay_output ----------------------------------------------------------- */
1261 * Timeout callback for delay_output(). Send a fake message to the map
1266 d_timeout(client_data
, id
)
1267 XtPointer client_data
;
1271 XClientMessageEvent
*mesg
;
1276 /* Set up a fake message to the event handler. */
1277 mesg
= (XClientMessageEvent
*) &event
;
1278 mesg
->type
= ClientMessage
;
1279 mesg
->message_type
= XA_STRING
;
1281 XSendEvent(XtDisplay(window_list
[WIN_MAP
].w
),
1282 XtWindow(window_list
[WIN_MAP
].w
), False
, NoEventMask
,
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
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 ------------------------------------------------------------- */
1307 X11_hangup(w
, event
, params
, num_params
)
1311 Cardinal
*num_params
;
1318 hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1319 exit_x_event
= TRUE
;
1322 /* askname ---------------------------------------------------------------- */
1325 askname_delete(w
, event
, params
, num_params
)
1329 Cardinal
*num_params
;
1336 (void) strcpy(plname
, "Mumbles"); /* give them a name... ;-) */
1337 exit_x_event
= TRUE
;
1340 /* Callback for askname dialog widget. */
1343 askname_done(w
, client_data
, call_data
)
1345 XtPointer client_data
;
1346 XtPointer call_data
;
1350 Widget dialog
= (Widget
) client_data
;
1355 s
= (char *) GetDialogResponse(dialog
);
1363 /* Truncate name if necessary */
1364 if (len
>= sizeof plname
- 1)
1365 len
= sizeof plname
- 1;
1367 (void) strncpy(plname
, s
, len
);
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 */
1380 Widget popup
, dialog
;
1383 XtSetArg(args
[0], XtNallowShellResize
, True
);
1385 popup
= XtCreatePopupShell("askname", transientShellWidgetClass
, toplevel
,
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. */
1419 done_button(w
, client_data
, call_data
)
1421 XtPointer client_data
;
1422 XtPointer call_data
;
1426 Widget dialog
= (Widget
) client_data
;
1431 s
= (char *) GetDialogResponse(dialog
);
1434 /* Truncate input if necessary */
1438 (void) strncpy(getline_input
, s
, len
);
1439 getline_input
[len
] = '\0';
1442 nh_XtPopdown(XtParent(dialog
));
1443 exit_x_event
= TRUE
;
1448 getline_delete(w
, event
, params
, num_params
)
1452 Cardinal
*num_params
;
1458 Strcpy(getline_input
, CANCEL_STR
);
1460 exit_x_event
= TRUE
;
1463 /* Callback for getline dialog widget. */
1466 abort_button(w
, client_data
, call_data
)
1468 XtPointer client_data
;
1469 XtPointer call_data
;
1471 Widget dialog
= (Widget
) client_data
;
1476 Strcpy(getline_input
, CANCEL_STR
);
1477 nh_XtPopdown(XtParent(dialog
));
1478 exit_x_event
= TRUE
;
1482 release_getline_widgets()
1485 XtDestroyWidget(getline_dialog
), getline_dialog
= (Widget
) 0;
1487 XtDestroyWidget(getline_popup
), getline_popup
= (Widget
) 0;
1491 X11_getlin(question
, input
)
1492 const char *question
;
1495 getline_input
= input
;
1498 if (!getline_popup
) {
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. */
1542 delete_file(w
, event
, params
, num_params
)
1546 Cardinal
*num_params
;
1556 /* Callback for file dismissal. */
1559 dismiss_file(w
, event
, params
, num_params
)
1563 Cardinal
*num_params
;
1565 Widget popup
= XtParent(w
);
1571 nh_XtPopdown(popup
);
1572 XtDestroyWidget(popup
);
1576 X11_display_file(str
, complain
)
1583 Widget popup
, dispfile
;
1584 Position top_margin
, bottom_margin
, left_margin
, right_margin
;
1586 int new_width
, new_height
;
1590 char *textlines
, *bp
;
1593 /* Use the port-independent file opener to see if the file exists. */
1594 fp
= dlb_fopen(str
, RDTMODE
);
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.
1607 while (dlb_fgets(line
, LLEN
, fp
)) {
1609 charcount
+= strlen(line
);
1612 (void) dlb_fclose(fp
);
1614 /* Ignore empty files */
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
);
1632 while (dlb_fgets(line
, LLEN
, fp
)) {
1633 Strcpy((bp
= eos(bp
)), line
);
1636 (void) dlb_fclose(fp
);
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()"));
1647 XtSetArg(args
[num_args
], nhStr(XtNscrollHorizontal
),
1648 XawtextScrollWhenNeeded
); num_args
++;
1649 XtSetArg(args
[num_args
], nhStr(XtNscrollVertical
), XawtextScrollAlways
);
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 */
1661 /* Get font and border information. */
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
);
1667 XtSetArg(args
[num_args
], nhStr(XtNleftMargin
), &left_margin
); num_args
++;
1668 XtSetArg(args
[num_args
], nhStr(XtNrightMargin
), &right_margin
);
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..
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. */
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);
1690 /* yn_function ------------------------------------------------------------ */
1691 /* (not threaded) */
1693 static const char *yn_quitchars
= " \n\r";
1694 static const char *yn_choices
; /* string of acceptable input */
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\
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.
1714 key_event_to_char(key
)
1717 char keystring
[MAX_KEY_STRING
];
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. */
1728 return (char) (((int) keystring
[0]) + (meta
? 0x80 : 0));
1732 * Called when we get a WM_DELETE_WINDOW event on a yn window.
1736 yn_delete(w
, event
, params
, num_params
)
1740 Cardinal
*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.
1758 yn_key(w
, event
, params
, num_params
)
1762 Cardinal
*num_params
;
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 */
1776 if (!yn_choices
) { /* accept any input */
1779 if (!yn_preserve_case
)
1780 ch
= lowc(ch
); /* move to lower case */
1783 yn_getting_num
= FALSE
;
1784 yn_return
= yn_esc_map
;
1785 } else if (index(yn_quitchars
, ch
)) {
1787 } else if (index(yn_choices
, ch
)) {
1789 if (yn_getting_num
) { /* don't select again */
1793 yn_getting_num
= TRUE
;
1796 return; /* wait for more input */
1800 yn_getting_num
= FALSE
;
1802 if (yn_getting_num
) {
1805 yn_val
= (yn_val
* 10) + (long) (ch
- '0');
1806 return; /* wait for more input */
1808 if (yn_ndigits
&& (ch
== '\b' || ch
== 127 /*DEL*/)) {
1810 yn_val
= yn_val
/ 10;
1811 return; /* wait for more input */
1814 X11_nhbell(); /* no match */
1818 if (yn_getting_num
) {
1822 yn_number
= yn_val
; /* assign global */
1825 exit_x_event
= TRUE
; /* exit our event handler */
1828 /* called at exit time */
1830 release_yn_widgets()
1833 XtDestroyWidget(yn_label
), yn_label
= (Widget
) 0;
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' */
1841 X11_yn_function(ques
, choices
, def
)
1843 const char *choices
; /* string of possible response chars; any char if Null */
1844 char def
; /* default response if user hits <space> or <return> */
1850 yn_choices
= choices
; /* set up globals for callback to use */
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
]);
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
;
1877 if ((cb
= index(choicebuf
, '\033')) != 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
);
1886 Sprintf(eos(buf
), " (%c)", def
);
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'
1894 if ((int) (1 + strlen(ques
) + 1) >= BUFSZ
)
1895 panic("X11_yn_function: question too long");
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()"));
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 */
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.
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 */
1967 /* erase the prompt */
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 */
1973 nh_XtPopdown(yn_popup
); /* this removes the event grab */
1979 /* used when processing window-capability-specific run-time options;
1980 we support toggling tiles on and off via iflags.wc_tiled_map */
1982 X11_preference_update(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.
1998 input_event(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
);
2013 msgkey(w
, data
, event
)
2023 map_input(window_list
[WIN_MAP
].w
, event
, (String
*) 0, &num
);
2026 /* only called for autofocus */
2029 win_visible(w
, data
, event
, flag
)
2031 XtPointer data
; /* client_data not used */
2033 Boolean
*flag
; /* continue_to_dispatch flag not used */
2035 XVisibilityEvent
*vis_event
= (XVisibilityEvent
*) event
;
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 */
2056 struct xwindow
*xmap
;
2058 if (!appResources
.slow
|| !appResources
.highlight_prompt
)
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);
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
);
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.
2093 init_standard_windows()
2095 Widget form
, message_viewport
, map_viewport
, status
;
2098 Dimension message_vp_width
, map_vp_width
, status_width
, max_width
;
2099 int map_vp_hd
, status_hd
;
2103 XtSetArg(args
[num_args
], XtNallowShellResize
, True
); num_args
++;
2104 form
= XtCreateManagedWidget("anethack", panedWidgetClass
, toplevel
, args
,
2107 XtAddEventHandler(form
, KeyPressMask
, False
, (XtEventHandler
) msgkey
,
2110 if (appResources
.autofocus
)
2111 XtAddEventHandler(toplevel
, VisibilityChangeMask
, False
, win_visible
,
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. */
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
) {
2133 XtSetArg(args
[num_args
], XtNtranslations
,
2134 XtParseTranslationTable(yn_translations
)); num_args
++;
2135 yn_label
= XtCreateManagedWidget("yn_label", labelWidgetClass
, form
,
2138 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), message_viewport
);
2140 XtSetArg(args
[num_args
], nhStr(XtNjustify
), XtJustifyLeft
);
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
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. */
2160 if (appResources
.slow
) {
2161 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), yn_label
); num_args
++;
2163 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), message_viewport
);
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
);
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.
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.
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,
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
;
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.
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;
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
);
2308 if (appResources
.autofocus
)
2309 XtSetKeyboardFocus(toplevel
, None
);
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();
2336 * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2340 nh_keyscroll(viewport
, event
, params
, num_params
)
2344 Cardinal
*num_params
;
2347 Widget horiz_sb
, vert_sb
;
2351 Cardinal in_nparams
= (num_params
? *num_params
: 0);
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
);
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. */
2379 XtSetArg(arg
[0], nhStr(XtNshown
), &shown
);
2380 XtSetArg(arg
[1], nhStr(XtNtopOfThumb
), &top
);
2381 XtGetValues(horiz_sb
, arg
, TWO
);
2385 switch (direction
) {
2397 if (top
+ shown
> 1.0)
2405 XtCallCallbacks(horiz_sb
, XtNjumpProc
, &top
);
2410 XtSetArg(arg
[0], nhStr(XtNshown
), &shown
);
2411 XtSetArg(arg
[1], nhStr(XtNtopOfThumb
), &top
);
2412 XtGetValues(vert_sb
, arg
, TWO
);
2416 switch (direction
) {
2428 if (top
+ shown
> 1.0)
2436 XtCallCallbacks(vert_sb
, XtNjumpProc
, &top
);