1 /* NetHack 3.6 winX.c $NHDT-Date: 1552441031 2019/03/13 01:37:11 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.73 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /*
6 * "Main" file for the X window-port. This contains most of the interface
7 * routines. Please see doc/window.doc for an description of the window
8 * interface.
9 */
10
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
14
15 #ifdef MSDOS /* from compiler */
16 #define SHORT_FILENAMES
17 #endif
18
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Shell.h>
22 #include <X11/Xaw/AsciiText.h>
23 #include <X11/Xaw/Label.h>
24 #include <X11/Xaw/Form.h>
25 #include <X11/Xaw/Scrollbar.h>
26 #include <X11/Xaw/Paned.h>
27 #include <X11/Xaw/Cardinals.h>
28 #include <X11/Xatom.h>
29 #include <X11/Xos.h>
30
31 /* for color support */
32 #ifdef SHORT_FILENAMES
33 #include <X11/IntrinsP.h>
34 #else
35 #include <X11/IntrinsicP.h>
36 #endif
37
38 #ifdef PRESERVE_NO_SYSV
39 #ifdef SYSV
40 #undef SYSV
41 #endif
42 #undef PRESERVE_NO_SYSV
43 #endif
44
45 #ifdef SHORT_FILENAMES
46 #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
47 #endif
48
49 #include "hack.h"
50 #include "winX.h"
51 #include "dlb.h"
52 #include "xwindow.h"
53
54 #ifndef NO_SIGNAL
55 #include <signal.h>
56 #endif
57
58 /* Should be defined in <X11/Intrinsic.h> but you never know */
59 #ifndef XtSpecificationRelease
60 #define XtSpecificationRelease 0
61 #endif
62
63 /*
64 * Icons.
65 */
66 #include "../win/X11/nh72icon"
67 #include "../win/X11/nh56icon"
68 #include "../win/X11/nh32icon"
69
70 static struct icon_info {
71 const char *name;
72 unsigned char *bits;
73 unsigned width, height;
74 } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
75 { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
76 { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
77 { (const char *) 0, (unsigned char *) 0, 0, 0 } };
78
79 /*
80 * Private global variables (shared among the window port files).
81 */
82 struct xwindow window_list[MAX_WINDOWS];
83 AppResources appResources;
84 void FDECL((*input_func), (Widget, XEvent *, String *, Cardinal *));
85 int click_x, click_y, click_button; /* Click position on a map window */
86 /* (filled by set_button_values()). */
87 int updated_inventory;
88
89 static int (*old_error_handler) (Display *, XErrorEvent *);
90
91 #if !defined(NO_SIGNAL) && defined(SAFERHANGUP)
92 #if XtSpecificationRelease >= 6
93 #define X11_HANGUP_SIGNAL
94 static XtSignalId X11_sig_id;
95 #endif
96 #endif
97
98 /* Interface definition, for windows.c */
99 struct window_procs X11_procs = {
100 "X11",
101 (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP
102 | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT),
103 #if defined(STATUS_HILITES)
104 WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS |
105 #endif
106 0L,
107 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
108 X11_init_nhwindows,
109 X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows,
110 X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow,
111 X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs,
112 X11_putstr, genl_putmixed, X11_display_file, X11_start_menu, X11_add_menu,
113 X11_end_menu, X11_select_menu,
114 genl_message_menu, /* no need for X-specific handling */
115 X11_update_inventory, X11_mark_synch, X11_wait_synch,
116 #ifdef CLIPPING
117 X11_cliparound,
118 #endif
119 #ifdef POSITIONBAR
120 donull,
121 #endif
122 X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch,
123 X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function,
124 X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output,
125 #ifdef CHANGE_COLOR /* only a Mac option currently */
126 donull, donull,
127 #endif
128 /* other defs that really should go away (they're tty specific) */
129 X11_start_screen, X11_end_screen,
130 #ifdef GRAPHIC_TOMBSTONE
131 X11_outrip,
132 #else
133 genl_outrip,
134 #endif
135 X11_preference_update, X11_getmsghistory, X11_putmsghistory,
136 X11_status_init, X11_status_finish, X11_status_enablefield,
137 X11_status_update,
138 genl_can_suspend_no, /* XXX may not always be correct */
139 };
140
141 /*
142 * Local functions.
143 */
144 static winid NDECL(find_free_window);
145 #ifdef TEXTCOLOR
146 static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer,
147 XrmValuePtr, Cardinal *));
148 #endif
149 static boolean FDECL(new_resource_macro, (String, unsigned));
150 static void NDECL(load_default_resources);
151 static void NDECL(release_default_resources);
152 static int FDECL(panic_on_error, (Display *, XErrorEvent *));
153 #ifdef X11_HANGUP_SIGNAL
154 static void FDECL(X11_sig, (int));
155 static void FDECL(X11_sig_cb, (XtPointer, XtSignalId *));
156 #endif
157 static void FDECL(d_timeout, (XtPointer, XtIntervalId *));
158 static void FDECL(X11_hangup, (Widget, XEvent *, String *, Cardinal *));
159 static void FDECL(askname_delete, (Widget, XEvent *, String *, Cardinal *));
160 static void FDECL(askname_done, (Widget, XtPointer, XtPointer));
161 static void FDECL(done_button, (Widget, XtPointer, XtPointer));
162 static void FDECL(getline_delete, (Widget, XEvent *, String *, Cardinal *));
163 static void FDECL(abort_button, (Widget, XtPointer, XtPointer));
164 static void NDECL(release_getline_widgets);
165 static void FDECL(yn_delete, (Widget, XEvent *, String *, Cardinal *));
166 static void FDECL(yn_key, (Widget, XEvent *, String *, Cardinal *));
167 static void NDECL(release_yn_widgets);
168 static int FDECL(input_event, (int));
169 static void FDECL(win_visible, (Widget, XtPointer, XEvent *, Boolean *));
170 static void NDECL(init_standard_windows);
171
172 /*
173 * Local variables.
174 */
175 static boolean x_inited = FALSE; /* TRUE if window system is set up. */
176 static winid message_win = WIN_ERR, /* These are the winids of the message, */
177 map_win = WIN_ERR, /* map, and status windows, when they */
178 status_win = WIN_ERR; /* are created in init_windows(). */
179 static Pixmap icon_pixmap = None; /* Pixmap for icon. */
180
181 void
X11_putmsghistory(msg,is_restoring)182 X11_putmsghistory(msg, is_restoring)
183 const char *msg;
184 boolean is_restoring;
185 {
186 if (WIN_MESSAGE != WIN_ERR) {
187 struct xwindow *wp = &window_list[WIN_MESSAGE];
188 debugpline2("X11_putmsghistory('%s',%i)", msg, is_restoring);
189 if (msg)
190 append_message(wp, msg);
191 }
192 }
193
194 char *
X11_getmsghistory(init)195 X11_getmsghistory(init)
196 boolean init;
197 {
198 if (WIN_MESSAGE != WIN_ERR) {
199 static struct line_element *curr = (struct line_element *) 0;
200 static int numlines = 0;
201 struct xwindow *wp = &window_list[WIN_MESSAGE];
202
203 if (init)
204 curr = (struct line_element *) 0;
205
206 if (!curr) {
207 curr = wp->mesg_information->head;
208 numlines = 0;
209 }
210
211 if (numlines < wp->mesg_information->num_lines) {
212 curr = curr->next;
213 numlines++;
214 debugpline2("X11_getmsghistory(%i)='%s'", init, curr->line);
215 return curr->line;
216 }
217 }
218 return (char *) 0;
219 }
220
221 /*
222 * Find the window structure that corresponds to the given widget. Note
223 * that this is not the popup widget, nor the viewport, but the child.
224 */
225 struct xwindow *
find_widget(w)226 find_widget(w)
227 Widget w;
228 {
229 int windex;
230 struct xwindow *wp;
231
232 /*
233 * Search to find the corresponding window. Look at the main widget,
234 * popup, the parent of the main widget, then parent of the widget.
235 */
236 for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
237 if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w
238 || (wp->w && (XtParent(wp->w)) == w)
239 || (wp->popup == XtParent(w))))
240 break;
241
242 if (windex == MAX_WINDOWS)
243 panic("find_widget: can't match widget");
244 return wp;
245 }
246
247 /*
248 * Find a free window slot for use.
249 */
250 static winid
find_free_window()251 find_free_window()
252 {
253 int windex;
254 struct xwindow *wp;
255
256 for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS;
257 windex++, wp++)
258 if (wp->type == NHW_NONE)
259 break;
260
261 if (windex == MAX_WINDOWS)
262 panic("find_free_window: no free windows!");
263 return (winid) windex;
264 }
265
266
267 XColor
get_nhcolor(wp,clr)268 get_nhcolor(wp, clr)
269 struct xwindow *wp;
270 int clr;
271 {
272 init_menu_nhcolors(wp);
273 /* FIXME: init_menu_nhcolors may fail */
274
275 if (clr >= 0 && clr < CLR_MAX)
276 return wp->nh_colors[clr];
277
278 return wp->nh_colors[0];
279 }
280
281 void
init_menu_nhcolors(wp)282 init_menu_nhcolors(wp)
283 struct xwindow *wp;
284 {
285 static const char *mapCLR_to_res[CLR_MAX] = {
286 XtNblack,
287 XtNred,
288 XtNgreen,
289 XtNbrown,
290 XtNblue,
291 XtNmagenta,
292 XtNcyan,
293 XtNgray,
294 XtNforeground,
295 XtNorange,
296 XtNbright_green,
297 XtNyellow,
298 XtNbright_blue,
299 XtNbright_magenta,
300 XtNbright_cyan,
301 XtNwhite,
302 };
303 Display *dpy;
304 Colormap screen_colormap;
305 XrmDatabase rDB;
306 XrmValue value;
307 Status rc;
308 int color;
309 char *ret_type[32];
310 char clr_name[BUFSZ];
311 char clrclass[BUFSZ];
312 const char *wintypenames[NHW_TEXT] = {
313 "message", "status", "map", "menu", "text"
314 };
315 const char *wtn;
316 char wtn_up[BUFSZ];
317
318 if (wp->nh_colors_inited || !wp->type)
319 return;
320
321 wtn = wintypenames[wp->type - 1];
322 Strcpy(wtn_up, wtn);
323 (void) upstart(wtn_up);
324
325 dpy = XtDisplay(wp->w);
326 screen_colormap = DefaultColormap(dpy, DefaultScreen(dpy));
327 rDB = XrmGetDatabase(dpy);
328
329 for (color = 0; color < CLR_MAX; color++) {
330 Sprintf(clr_name, "nethack.%s.%s", wtn, mapCLR_to_res[color]);
331 Sprintf(clrclass, "NetHack.%s.%s", wtn_up, mapCLR_to_res[color]);
332
333 if (!XrmGetResource(rDB, clr_name, clrclass, ret_type, &value)) {
334 Sprintf(clr_name, "nethack.map.%s", mapCLR_to_res[color]);
335 Sprintf(clrclass, "NetHack.Map.%s", mapCLR_to_res[color]);
336 }
337
338 if (!XrmGetResource(rDB, clr_name, clrclass, ret_type, &value)) {
339 impossible("XrmGetResource error (%s)", clr_name);
340 } else if (!strcmp(ret_type[0], "String")) {
341 char tmpbuf[256];
342
343 if (value.size >= sizeof tmpbuf)
344 value.size = sizeof tmpbuf - 1;
345 (void) strncpy(tmpbuf, (char *) value.addr, (int) value.size);
346 tmpbuf[value.size] = '\0';
347 /* tmpbuf now contains the color name from the named resource */
348
349 rc = XAllocNamedColor(dpy, screen_colormap, tmpbuf,
350 &wp->nh_colors[color],
351 &wp->nh_colors[color]);
352 if (rc == 0) {
353 impossible("XAllocNamedColor failed for color %i (%s)",
354 color, clr_name);
355 }
356 }
357 }
358
359 wp->nh_colors_inited = TRUE;
360 }
361
362 /*
363 * Color conversion. The default X11 color converters don't try very
364 * hard to find matching colors in PseudoColor visuals. If they can't
365 * allocate the exact color, they puke and give you something stupid.
366 * This is an attempt to find some close readonly cell and use it.
367 */
368 XtConvertArgRec const nhcolorConvertArgs[] = {
369 { XtWidgetBaseOffset,
370 (XtPointer) (ptrdiff_t) XtOffset(Widget, core.screen),
371 sizeof (Screen *) },
372 { XtWidgetBaseOffset,
373 (XtPointer) (ptrdiff_t) XtOffset(Widget, core.colormap),
374 sizeof (Colormap) }
375 };
376
377 #define done(type, value) \
378 { \
379 if (toVal->addr != 0) { \
380 if (toVal->size < sizeof(type)) { \
381 toVal->size = sizeof(type); \
382 return False; \
383 } \
384 *(type *)(toVal->addr) = (value); \
385 } else { \
386 static type static_val; \
387 static_val = (value); \
388 toVal->addr = (genericptr_t) &static_val; \
389 } \
390 toVal->size = sizeof(type); \
391 return True; \
392 }
393
394 /*
395 * Find a color that approximates the color named in "str".
396 * The "str" color may be a color name ("red") or number ("#7f0000").
397 * If str is Null, then "color" is assumed to contain the RGB color wanted.
398 * The approximate color found is returned in color as well.
399 * Return True if something close was found.
400 */
401 Boolean
nhApproxColor(screen,colormap,str,color)402 nhApproxColor(screen, colormap, str, color)
403 Screen *screen; /* screen to use */
404 Colormap colormap; /* the colormap to use */
405 char *str; /* color name */
406 XColor *color; /* the X color structure; changed only if successful */
407 {
408 int ncells;
409 long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
410 XColor tmp;
411 static XColor *table = 0;
412 register int i, j;
413 register long tdiff;
414
415 /* if the screen doesn't have a big colormap, don't waste our time
416 or if it's huge, and _some_ match should have been possible */
417 if ((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
418 return False;
419
420 if (str != (char *) 0) {
421 if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
422 return False;
423 } else {
424 tmp = *color;
425 tmp.flags = 7; /* force to use all 3 of RGB */
426 }
427
428 if (!table) {
429 table = (XColor *) XtCalloc(ncells, sizeof(XColor));
430 for (i = 0; i < ncells; i++)
431 table[i].pixel = i;
432 XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
433 }
434
435 /* go thru cells and look for the one with smallest diff */
436 /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
437 /* a more knowledgeable color person might improve this -dlc */
438 try_again:
439 for (i = 0; i < ncells; i++) {
440 if (table[i].flags == tmp.flags) {
441 j = (int) table[i].red - (int) tmp.red;
442 if (j < 0)
443 j = -j;
444 tdiff = j;
445 j = (int) table[i].green - (int) tmp.green;
446 if (j < 0)
447 j = -j;
448 tdiff += j;
449 j = (int) table[i].blue - (int) tmp.blue;
450 if (j < 0)
451 j = -j;
452 tdiff += j;
453 if (tdiff < cdiff) {
454 cdiff = tdiff;
455 tmp.pixel = i; /* table[i].pixel == i */
456 }
457 }
458 }
459
460 if (cdiff == 16777216)
461 return False; /* nothing found?! */
462
463 /*
464 * Found something. Return it and mark this color as used to avoid
465 * reuse. Reuse causes major contrast problems :-)
466 */
467 *color = table[tmp.pixel];
468 table[tmp.pixel].flags = 0;
469 /* try to alloc the color, so no one else can change it */
470 if (!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
471 cdiff = 16777216;
472 goto try_again;
473 }
474 return True;
475 }
476
477 Boolean
nhCvtStringToPixel(dpy,args,num_args,fromVal,toVal,closure_ret)478 nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
479 Display *dpy;
480 XrmValuePtr args;
481 Cardinal *num_args;
482 XrmValuePtr fromVal;
483 XrmValuePtr toVal;
484 XtPointer *closure_ret;
485 {
486 String str = (String) fromVal->addr;
487 XColor screenColor;
488 XColor exactColor;
489 Screen *screen;
490 XtAppContext app = XtDisplayToApplicationContext(dpy);
491 Colormap colormap;
492 Status status;
493 String params[1];
494 Cardinal num_params = 1;
495
496 if (*num_args != 2) {
497 XtAppWarningMsg(app, "wrongParameters",
498 "cvtStringToPixel", "XtToolkitError",
499 "String to pixel conversion needs screen and colormap arguments",
500 (String *) 0, (Cardinal *) 0);
501 return False;
502 }
503
504 screen = *((Screen **) args[0].addr);
505 colormap = *((Colormap *) args[1].addr);
506
507 /* If Xt colors, use the Xt routine and hope for the best */
508 #if (XtSpecificationRelease >= 5)
509 if ((strcmpi(str, XtDefaultBackground) == 0)
510 || (strcmpi(str, XtDefaultForeground) == 0)) {
511 return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal,
512 closure_ret);
513 }
514 #else
515 if (strcmpi(str, XtDefaultBackground) == 0) {
516 *closure_ret = (char *) False;
517 done(Pixel, WhitePixelOfScreen(screen));
518 }
519 if (strcmpi(str, XtDefaultForeground) == 0) {
520 *closure_ret = (char *) False;
521 done(Pixel, BlackPixelOfScreen(screen));
522 }
523 #endif
524
525 status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char *) str,
526 &screenColor, &exactColor);
527 if (status == 0) {
528 String msg, type;
529
530 /* some versions of XAllocNamedColor don't allow #xxyyzz names */
531 if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap,
532 str, &exactColor)
533 && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
534 *closure_ret = (char *) True;
535 done(Pixel, exactColor.pixel);
536 }
537
538 params[0] = str;
539 /* Server returns a specific error code but Xlib discards it. Ugh */
540 if (XLookupColor(DisplayOfScreen(screen), colormap, (char *) str,
541 &exactColor, &screenColor)) {
542 /* try to find another color that will do */
543 if (nhApproxColor(screen, colormap, (char *) str, &screenColor)) {
544 *closure_ret = (char *) True;
545 done(Pixel, screenColor.pixel);
546 }
547 type = nhStr("noColormap");
548 msg = nhStr("Cannot allocate colormap entry for \"%s\"");
549 } else {
550 /* some versions of XLookupColor also don't allow #xxyyzz names */
551 if (str[0] == '#'
552 && (nhApproxColor(screen, colormap, (char *) str,
553 &screenColor))) {
554 *closure_ret = (char *) True;
555 done(Pixel, screenColor.pixel);
556 }
557 type = nhStr("badValue");
558 msg = nhStr("Color name \"%s\" is not defined");
559 }
560
561 XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg,
562 params, &num_params);
563 *closure_ret = False;
564 return False;
565 } else {
566 *closure_ret = (char *) True;
567 done(Pixel, screenColor.pixel);
568 }
569 }
570
571 /* Ask the WM for window frame size */
572 void
get_window_frame_extents(w,top,bottom,left,right)573 get_window_frame_extents(w, top, bottom, left, right)
574 Widget w;
575 long *top, *bottom, *left, *right;
576 {
577 XEvent event;
578 Display *dpy = XtDisplay(w);
579 Window win = XtWindow(w);
580 Atom prop, retprop;
581 int retfmt;
582 unsigned long nitems;
583 unsigned long nbytes;
584 unsigned char *data = 0;
585 long *extents;
586
587 prop = XInternAtom(dpy, "_NET_FRAME_EXTENTS", True);
588
589 while (XGetWindowProperty(dpy, win, prop,
590 0, 4, False, AnyPropertyType,
591 &retprop, &retfmt,
592 &nitems, &nbytes, &data) != Success
593 || nitems != 4 || nbytes != 0)
594 {
595 XNextEvent(dpy, &event);
596 }
597
598 extents = (long *) data;
599
600 *left = extents[0];
601 *right = extents[1];
602 *top = extents[2];
603 *bottom = extents[3];
604 }
605
606 void
get_widget_window_geometry(w,x,y,width,height)607 get_widget_window_geometry(w, x,y, width, height)
608 Widget w;
609 int *x, *y, *width, *height;
610 {
611 long top, bottom, left, right;
612 Arg args[5];
613 XtSetArg(args[0], nhStr(XtNx), x);
614 XtSetArg(args[1], nhStr(XtNy), y);
615 XtSetArg(args[2], nhStr(XtNwidth), width);
616 XtSetArg(args[3], nhStr(XtNheight), height);
617 XtGetValues(w, args, 4);
618 get_window_frame_extents(w, &top, &bottom, &left, &right);
619 *x -= left;
620 *y -= top;
621 }
622
623 /* Change the full font name string so the weight is "bold" */
624 char *
fontname_boldify(fontname)625 fontname_boldify(fontname)
626 const char *fontname;
627 {
628 static char buf[BUFSZ];
629 char *bufp = buf;
630 int idx = 0;
631
632 while (*fontname) {
633 if (*fontname == '-')
634 idx++;
635 *bufp = *fontname;
636 if (idx == 3) {
637 strcat(buf, "bold");
638 bufp += 5;
639 do {
640 fontname++;
641 } while (*fontname && *fontname != '-');
642 } else {
643 bufp++;
644 fontname++;
645 }
646 }
647 *bufp = '\0';
648 return buf;
649 }
650
651 void
load_boldfont(wp,w)652 load_boldfont(wp, w)
653 struct xwindow *wp;
654 Widget w;
655 {
656 Arg args[1];
657 XFontStruct *fs;
658 unsigned long ret;
659 char *fontname;
660 Display *dpy;
661
662 if (wp->boldfs)
663 return;
664
665 XtSetArg(args[0], nhStr(XtNfont), &fs);
666 XtGetValues(w, args, 1);
667
668 if (!XGetFontProperty(fs, XA_FONT, &ret))
669 return;
670
671 wp->boldfs_dpy = dpy = XtDisplay(w);
672 fontname = fontname_boldify(XGetAtomName(dpy, (Atom)ret));
673 wp->boldfs = XLoadQueryFont(dpy, fontname);
674 }
675
676 #ifdef TEXTCOLOR
677 /* ARGSUSED */
678 static void
nhFreePixel(app,toVal,closure,args,num_args)679 nhFreePixel(app, toVal, closure, args, num_args)
680 XtAppContext app;
681 XrmValuePtr toVal;
682 XtPointer closure;
683 XrmValuePtr args;
684 Cardinal *num_args;
685 {
686 Screen *screen;
687 Colormap colormap;
688
689 if (*num_args != 2) {
690 XtAppWarningMsg(app, "wrongParameters", "freePixel", "XtToolkitError",
691 "Freeing a pixel requires screen and colormap arguments",
692 (String *) 0, (Cardinal *) 0);
693 return;
694 }
695
696 screen = *((Screen **) args[0].addr);
697 colormap = *((Colormap *) args[1].addr);
698
699 if (closure) {
700 XFreeColors(DisplayOfScreen(screen), colormap,
701 (unsigned long *) toVal->addr, 1, (unsigned long) 0);
702 }
703 }
704 #endif /*TEXTCOLOR*/
705
706 /* [ALI] Utility function to ask Xaw for font height, since the previous
707 * assumption of ascent + descent is not always valid.
708 */
709 Dimension
nhFontHeight(w)710 nhFontHeight(w)
711 Widget w;
712 {
713 #ifdef _XawTextSink_h
714 Widget sink;
715 XawTextPosition pos = 0;
716 int resWidth, resHeight;
717 Arg args[1];
718
719 XtSetArg(args[0], XtNtextSink, &sink);
720 XtGetValues(w, args, 1);
721
722 XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight);
723 return resHeight;
724 #else
725 XFontStruct *fs;
726 Arg args[1];
727
728 XtSetArg(args[0], XtNfont, &fs);
729 XtGetValues(w, args, 1);
730
731 /* Assume font height is ascent + descent. */
732 return = fs->ascent + fs->descent;
733 #endif
734 }
735
736 static String *default_resource_data = 0, /* NULL-terminated arrays */
737 *def_rsrc_macr = 0, /* macro names */
738 *def_rsrc_valu = 0; /* macro values */
739
740 /* caller found "#define"; parse into macro name and its expansion value */
741 static boolean
new_resource_macro(inbuf,numdefs)742 new_resource_macro(inbuf, numdefs)
743 String inbuf; /* points past '#define' rather than to start of buffer */
744 unsigned numdefs; /* array slot to fill */
745 {
746 String p, q;
747
748 /* we expect inbuf to be terminated by newline; get rid of it */
749 q = eos(inbuf);
750 if (q > inbuf && q[-1] == '\n')
751 q[-1] = '\0';
752
753 /* figure out macro's name */
754 for (p = inbuf; *p == ' ' || *p == '\t'; ++p)
755 continue; /* skip whitespace */
756 for (q = p; *q && *q != ' ' && *q != '\t'; ++q)
757 continue; /* token consists of non-whitespace */
758 Strcat(q, " "); /* guarantee something beyond '#define FOO' */
759 *q++ = '\0'; /* p..(q-1) contains macro name */
760 if (!*p) /* invalid definition: '#define' followed by nothing */
761 return FALSE;
762 def_rsrc_macr[numdefs] = dupstr(p);
763
764 /* figure out macro's value; empty value is supported but not expected */
765 while (*q == ' ' || *q == '\t')
766 ++q; /* skip whitespace between name and value */
767 for (p = eos(q); --p > q && (*p == ' ' || *p == '\t'); )
768 continue; /* discard trailing whitespace */
769 *++p = '\0'; /* q..p containes macro value */
770 def_rsrc_valu[numdefs] = dupstr(q);
771 return TRUE;
772 }
773
774 /* read the template NetHack.ad into default_resource_data[] to supply
775 fallback resources to XtAppInitialize() */
776 static void
load_default_resources()777 load_default_resources()
778 {
779 FILE *fp;
780 String inbuf;
781 unsigned insiz, linelen, longlen, numlines, numdefs, midx;
782 boolean comment, isdef;
783
784 /*
785 * Running nethack via the shell script adds $HACKDIR to the path used
786 * by X to find resources, but running it directly doesn't. So, if we
787 * can find the template file for NetHack.ad in the current directory,
788 * load its contents into memory so that the application startup call
789 * in X11_init_nhwindows() can use them as fallback resources.
790 *
791 * No attempt to support the 'include' directive has been made, nor
792 * backslash+newline continuation lines. Macro expansion (at most
793 * one substitution per line) is supported. '#define' to introduce
794 * a macro must be at start of line (no whitespace before or after
795 * the '#' character).
796 */
797 fp = fopen("./NetHack.ad", "r");
798 if (!fp)
799 return;
800
801 /* measure the file without retaining its contents */
802 insiz = BUFSIZ; /* stdio BUFSIZ, not nethack BUFSZ */
803 inbuf = (String) alloc(insiz);
804 linelen = longlen = 0;
805 numlines = numdefs = 0;
806 comment = isdef = FALSE; /* lint suppression */
807 while (fgets(inbuf, insiz, fp)) {
808 if (!linelen) {
809 /* !linelen: inbuf has start of record; treat empty as comment */
810 comment = (*inbuf == '!' || *inbuf == '\n');
811 isdef = !strncmp(inbuf, "#define", 7);
812 ++numdefs;
813 }
814 linelen += strlen(inbuf);
815 if (!index(inbuf, '\n'))
816 continue;
817 if (linelen > longlen)
818 longlen = linelen;
819 linelen = 0;
820 if (!comment && !isdef)
821 ++numlines;
822 }
823 insiz = longlen + 1;
824 if (numdefs) { /* don't alloc if 0; no need for any terminator */
825 def_rsrc_macr = (String *) alloc(numdefs * sizeof (String));
826 def_rsrc_valu = (String *) alloc(numdefs * sizeof (String));
827 insiz += BUFSIZ; /* include room for macro expansion within buffer */
828 }
829 if (insiz > BUFSIZ) {
830 free((genericptr_t) inbuf);
831 inbuf = (String) alloc(insiz);
832 }
833 ++numlines; /* room for terminator */
834 default_resource_data = (String *) alloc(numlines * sizeof (String));
835
836 /* now re-read the file, storing its contents into the allocated array
837 after performing macro substitutions */
838 (void) rewind(fp);
839 numlines = numdefs = 0;
840 while (fgets(inbuf, insiz, fp)) {
841 if (!strncmp(inbuf, "#define", 7)) {
842 if (new_resource_macro(&inbuf[7], numdefs))
843 ++numdefs;
844 } else if (*inbuf != '!' && *inbuf != '\n') {
845 if (numdefs) {
846 /*
847 * Macro expansion: we assume at most one substitution
848 * per line. That's all that our sample NetHack.ad uses.
849 *
850 * If we ever need more, this will have to become a lot
851 * more sophisticated. It will need to find the first
852 * instance within inbuf[] rather than first macro which
853 * appears, and to avoid finding names within substituted
854 * expansion values.
855 *
856 * Any substitution which would exceed the buffer size is
857 * skipped. A sophisticated implementation would need to
858 * be prepared to allocate a bigger buffer when needed.
859 */
860 linelen = strlen(inbuf);
861 for (midx = 0; midx < numdefs; ++midx) {
862 if ((linelen + strlen(def_rsrc_valu[midx])
863 < insiz - strlen(def_rsrc_macr[midx]))
864 && strNsubst(inbuf, def_rsrc_macr[midx],
865 def_rsrc_valu[midx], 1))
866 break;
867 }
868 }
869 default_resource_data[numlines++] = dupstr(inbuf);
870 }
871 }
872 default_resource_data[numlines] = (String) 0;
873 (void) fclose(fp);
874 free((genericptr_t) inbuf);
875 if (def_rsrc_macr) { /* implies def_rsrc_valu is non-Null too */
876 for (midx = 0; midx < numdefs; ++midx) {
877 free((genericptr_t) def_rsrc_macr[midx]);
878 free((genericptr_t) def_rsrc_valu[midx]);
879 }
880 free((genericptr_t) def_rsrc_macr), def_rsrc_macr = 0;
881 free((genericptr_t) def_rsrc_valu), def_rsrc_valu = 0;
882 }
883 }
884
885 static void
release_default_resources()886 release_default_resources()
887 {
888 if (default_resource_data) {
889 unsigned idx;
890
891 for (idx = 0; default_resource_data[idx]; idx++)
892 free((genericptr_t) default_resource_data[idx]);
893 free((genericptr_t) default_resource_data), default_resource_data = 0;
894 }
895 /* def_rsrc_macr[] and def_rsrc_valu[] have already been released */
896 }
897
898 /* Global Functions ======================================================= */
899 void
X11_raw_print(str)900 X11_raw_print(str)
901 const char *str;
902 {
903 (void) puts(str);
904 }
905
906 void
X11_raw_print_bold(str)907 X11_raw_print_bold(str)
908 const char *str;
909 {
910 (void) puts(str);
911 }
912
913 void
X11_curs(window,x,y)914 X11_curs(window, x, y)
915 winid window;
916 int x, y;
917 {
918 check_winid(window);
919
920 if (x < 0 || x >= COLNO) {
921 impossible("curs: bad x value [%d]", x);
922 x = 0;
923 }
924 if (y < 0 || y >= ROWNO) {
925 impossible("curs: bad y value [%d]", y);
926 y = 0;
927 }
928
929 window_list[window].cursx = x;
930 window_list[window].cursy = y;
931 }
932
933 void
X11_putstr(window,attr,str)934 X11_putstr(window, attr, str)
935 winid window;
936 int attr;
937 const char *str;
938 {
939 winid new_win;
940 struct xwindow *wp;
941
942 check_winid(window);
943 wp = &window_list[window];
944
945 switch (wp->type) {
946 case NHW_MESSAGE:
947 (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
948 toplines[TBUFSZ - 1] = 0;
949 append_message(wp, str);
950 break;
951 #ifndef STATUS_HILITES
952 case NHW_STATUS:
953 adjust_status(wp, str);
954 break;
955 #endif
956 case NHW_MAP:
957 impossible("putstr: called on map window \"%s\"", str);
958 break;
959 case NHW_MENU:
960 if (wp->menu_information->is_menu) {
961 impossible("putstr: called on a menu window, \"%s\" discarded",
962 str);
963 break;
964 }
965 /*
966 * Change this menu window into a text window by creating a
967 * new text window, then copying it to this winid.
968 */
969 new_win = X11_create_nhwindow(NHW_TEXT);
970 X11_destroy_nhwindow(window);
971 *wp = window_list[new_win];
972 window_list[new_win].type = NHW_NONE; /* allow re-use */
973 /* fall though to add text */
974 case NHW_TEXT:
975 add_to_text_window(wp, attr, str);
976 break;
977 default:
978 impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str);
979 }
980 }
981
982 /* We do event processing as a callback, so this is a null routine. */
983 void
X11_get_nh_event()984 X11_get_nh_event()
985 {
986 return;
987 }
988
989 int
X11_nhgetch()990 X11_nhgetch()
991 {
992 return input_event(EXIT_ON_KEY_PRESS);
993 }
994
995 int
X11_nh_poskey(x,y,mod)996 X11_nh_poskey(x, y, mod)
997 int *x, *y, *mod;
998 {
999 int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
1000
1001 if (val == 0) { /* user clicked on a map window */
1002 *x = click_x;
1003 *y = click_y;
1004 *mod = click_button;
1005 }
1006 return val;
1007 }
1008
1009 winid
X11_create_nhwindow(type)1010 X11_create_nhwindow(type)
1011 int type;
1012 {
1013 winid window;
1014 struct xwindow *wp;
1015
1016 if (!x_inited)
1017 panic("create_nhwindow: windows not initialized");
1018
1019 #ifdef X11_HANGUP_SIGNAL
1020 /* set up our own signal handlers on the first call. Can't do this in
1021 * X11_init_nhwindows because unixmain sets its handler after calling
1022 * all the init routines. */
1023 if (X11_sig_id == 0) {
1024 X11_sig_id = XtAppAddSignal(app_context, X11_sig_cb, (XtPointer) 0);
1025 #ifdef SA_RESTART
1026 {
1027 struct sigaction sact;
1028
1029 (void) memset((char *) &sact, 0, sizeof(struct sigaction));
1030 sact.sa_handler = (SIG_RET_TYPE) X11_sig;
1031 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
1032 #ifdef SIGXCPU
1033 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
1034 #endif
1035 }
1036 #else
1037 (void) signal(SIGHUP, (SIG_RET_TYPE) X11_sig);
1038 #ifdef SIGXCPU
1039 (void) signal(SIGXCPU, (SIG_RET_TYPE) X11_sig);
1040 #endif
1041 #endif
1042 }
1043 #endif
1044
1045 /*
1046 * We have already created the standard message, map, and status
1047 * windows in the window init routine. The first window of that
1048 * type to be created becomes the standard.
1049 *
1050 * A better way to do this would be to say that init_nhwindows()
1051 * has already defined these three windows.
1052 */
1053 if (type == NHW_MAP && map_win != WIN_ERR) {
1054 window = map_win;
1055 map_win = WIN_ERR;
1056 return window;
1057 }
1058 if (type == NHW_MESSAGE && message_win != WIN_ERR) {
1059 window = message_win;
1060 message_win = WIN_ERR;
1061 return window;
1062 }
1063 if (type == NHW_STATUS && status_win != WIN_ERR) {
1064 window = status_win;
1065 status_win = WIN_ERR;
1066 return window;
1067 }
1068
1069 window = find_free_window();
1070 wp = &window_list[window];
1071
1072 /* The create routines will set type, popup, w, and Win_info. */
1073 wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width =
1074 wp->pixel_height = 0;
1075 wp->keep_window = FALSE;
1076 wp->nh_colors_inited = FALSE;
1077 wp->boldfs = (XFontStruct *) 0;
1078 wp->boldfs_dpy = (Display *) 0;
1079 wp->title = (char *) 0;
1080
1081 switch (type) {
1082 case NHW_MAP:
1083 create_map_window(wp, TRUE, (Widget) 0);
1084 break;
1085 case NHW_MESSAGE:
1086 create_message_window(wp, TRUE, (Widget) 0);
1087 break;
1088 case NHW_STATUS:
1089 create_status_window(wp, TRUE, (Widget) 0);
1090 break;
1091 case NHW_MENU:
1092 create_menu_window(wp);
1093 break;
1094 case NHW_TEXT:
1095 create_text_window(wp);
1096 break;
1097 default:
1098 panic("create_nhwindow: unknown type [%d]", type);
1099 break;
1100 }
1101 return window;
1102 }
1103
1104 void
X11_clear_nhwindow(window)1105 X11_clear_nhwindow(window)
1106 winid window;
1107 {
1108 struct xwindow *wp;
1109
1110 check_winid(window);
1111 wp = &window_list[window];
1112
1113 switch (wp->type) {
1114 case NHW_MAP:
1115 clear_map_window(wp);
1116 break;
1117 case NHW_TEXT:
1118 clear_text_window(wp);
1119 break;
1120 case NHW_STATUS:
1121 case NHW_MENU:
1122 case NHW_MESSAGE:
1123 /* do nothing for these window types */
1124 break;
1125 default:
1126 panic("clear_nhwindow: unknown window type [%d]", wp->type);
1127 break;
1128 }
1129 }
1130
1131 void
X11_display_nhwindow(window,blocking)1132 X11_display_nhwindow(window, blocking)
1133 winid window;
1134 boolean blocking;
1135 {
1136 struct xwindow *wp;
1137
1138 check_winid(window);
1139 wp = &window_list[window];
1140
1141 switch (wp->type) {
1142 case NHW_MAP:
1143 if (wp->popup)
1144 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1145 display_map_window(wp); /* flush map */
1146
1147 /*
1148 * We need to flush the message window here due to the way the tty
1149 * port is set up. To flush a window, you need to call this
1150 * routine. However, the tty port _pauses_ with a --more-- if we
1151 * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call
1152 * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
1153 * get a --more-- after every line.
1154 *
1155 * Perhaps the window document should mention that when the map
1156 * is flushed, everything on the three main windows should be
1157 * flushed. Note: we don't need to flush the status window
1158 * because we don't buffer changes.
1159 */
1160 if (WIN_MESSAGE != WIN_ERR)
1161 display_message_window(&window_list[WIN_MESSAGE]);
1162 if (blocking)
1163 (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
1164 break;
1165 case NHW_MESSAGE:
1166 if (wp->popup)
1167 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1168 display_message_window(wp); /* flush messages */
1169 break;
1170 case NHW_STATUS:
1171 if (wp->popup)
1172 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
1173 break; /* no flushing necessary */
1174 case NHW_MENU: {
1175 int n;
1176 menu_item *selected;
1177
1178 /* pop up menu */
1179 n = X11_select_menu(window, PICK_NONE, &selected);
1180 if (n) {
1181 impossible("perminvent: %d selected??", n);
1182 free((genericptr_t) selected);
1183 }
1184 break;
1185 }
1186 case NHW_TEXT:
1187 display_text_window(wp, blocking); /* pop up text window */
1188 break;
1189 default:
1190 panic("display_nhwindow: unknown window type [%d]", wp->type);
1191 break;
1192 }
1193 }
1194
1195 void
X11_destroy_nhwindow(window)1196 X11_destroy_nhwindow(window)
1197 winid window;
1198 {
1199 struct xwindow *wp;
1200
1201 check_winid(window);
1202 wp = &window_list[window];
1203 /*
1204 * "Zap" known windows, but don't destroy them. We need to keep the
1205 * toplevel widget popped up so that later windows (e.g. tombstone)
1206 * are visible on DECWindow systems. This is due to the virtual
1207 * roots that the DECWindow wm creates.
1208 */
1209 if (window == WIN_MESSAGE) {
1210 wp->keep_window = TRUE;
1211 WIN_MESSAGE = WIN_ERR;
1212 iflags.window_inited = 0;
1213 } else if (window == WIN_MAP) {
1214 wp->keep_window = TRUE;
1215 WIN_MAP = WIN_ERR;
1216 } else if (window == WIN_STATUS) {
1217 wp->keep_window = TRUE;
1218 WIN_STATUS = WIN_ERR;
1219 } else if (window == WIN_INVEN) {
1220 /* don't need to keep this one */
1221 WIN_INVEN = WIN_ERR;
1222 }
1223
1224 if (wp->boldfs) {
1225 XFreeFont(wp->boldfs_dpy, wp->boldfs);
1226 wp->boldfs = (XFontStruct *) 0;
1227 wp->boldfs_dpy = (Display *) 0;
1228 }
1229
1230 if (wp->title) {
1231 free(wp->title);
1232 wp->title = (char *) 0;
1233 }
1234
1235 switch (wp->type) {
1236 case NHW_MAP:
1237 destroy_map_window(wp);
1238 break;
1239 case NHW_MENU:
1240 destroy_menu_window(wp);
1241 break;
1242 case NHW_TEXT:
1243 destroy_text_window(wp);
1244 break;
1245 case NHW_STATUS:
1246 destroy_status_window(wp);
1247 break;
1248 case NHW_MESSAGE:
1249 destroy_message_window(wp);
1250 break;
1251 default:
1252 panic("destroy_nhwindow: unknown window type [%d]", wp->type);
1253 break;
1254 }
1255 }
1256
1257 void
X11_update_inventory()1258 X11_update_inventory()
1259 {
1260 if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
1261 updated_inventory = 1; /* hack to avoid mapping&raising window */
1262 (void) display_inventory((char *) 0, FALSE);
1263 updated_inventory = 0;
1264 }
1265 }
1266
1267 /* The current implementation has all of the saved lines on the screen. */
1268 int
X11_doprev_message()1269 X11_doprev_message()
1270 {
1271 return 0;
1272 }
1273
1274 void
X11_nhbell()1275 X11_nhbell()
1276 {
1277 /* We can't use XBell until toplevel has been initialized. */
1278 if (x_inited)
1279 XBell(XtDisplay(toplevel), 0);
1280 /* else print ^G ?? */
1281 }
1282
1283 void
X11_mark_synch()1284 X11_mark_synch()
1285 {
1286 if (x_inited) {
1287 /*
1288 * The window document is unclear about the status of text
1289 * that has been pline()d but not displayed w/display_nhwindow().
1290 * Both the main and tty code assume that a pline() followed
1291 * by mark_synch() results in the text being seen, even if
1292 * display_nhwindow() wasn't called. Duplicate this behavior.
1293 */
1294 if (WIN_MESSAGE != WIN_ERR)
1295 display_message_window(&window_list[WIN_MESSAGE]);
1296 XSync(XtDisplay(toplevel), False);
1297 }
1298 }
1299
1300 void
X11_wait_synch()1301 X11_wait_synch()
1302 {
1303 if (x_inited)
1304 XFlush(XtDisplay(toplevel));
1305 }
1306
1307 /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
1308 void
X11_resume_nhwindows()1309 X11_resume_nhwindows()
1310 {
1311 return;
1312 }
1313 /* ARGSUSED */
1314 void
X11_suspend_nhwindows(str)1315 X11_suspend_nhwindows(str)
1316 const char *str;
1317 {
1318 nhUse(str);
1319
1320 return;
1321 }
1322
1323 /* Under X, we don't need to initialize the number pad. */
1324 /* ARGSUSED */
1325 void
X11_number_pad(state)1326 X11_number_pad(state) /* called from options.c */
1327 int state;
1328 {
1329 nhUse(state);
1330
1331 return;
1332 }
1333
1334 /* called from setftty() in unixtty.c */
1335 void
X11_start_screen()1336 X11_start_screen()
1337 {
1338 return;
1339 }
1340
1341 /* called from settty() in unixtty.c */
1342 void
X11_end_screen()1343 X11_end_screen()
1344 {
1345 return;
1346 }
1347
1348 #ifdef GRAPHIC_TOMBSTONE
1349 void
X11_outrip(window,how,when)1350 X11_outrip(window, how, when)
1351 winid window;
1352 int how;
1353 time_t when;
1354 {
1355 struct xwindow *wp;
1356 FILE *rip_fp = 0;
1357
1358 check_winid(window);
1359 wp = &window_list[window];
1360
1361 /* make sure the graphical tombstone is available; it's not easy to
1362 revert to the ASCII-art text tombstone once we're past this point */
1363 if (appResources.tombstone && *appResources.tombstone)
1364 rip_fp = fopen(appResources.tombstone, "r"); /* "rip.xpm" */
1365 if (!rip_fp) {
1366 genl_outrip(window, how, when);
1367 return;
1368 }
1369 (void) fclose(rip_fp);
1370
1371 if (wp->type == NHW_TEXT) {
1372 wp->text_information->is_rip = TRUE;
1373 } else {
1374 panic("ripout on non-text window (window type [%d])", wp->type);
1375 }
1376
1377 calculate_rip_text(how, when);
1378 }
1379 #endif
1380
1381 /* init and exit nhwindows ------------------------------------------------ */
1382
1383 XtAppContext app_context; /* context of application */
1384 Widget toplevel = (Widget) 0; /* toplevel widget */
1385 Atom wm_delete_window; /* pop down windows */
1386
1387 static XtActionsRec actions[] = {
1388 { nhStr("dismiss_text"), dismiss_text }, /* text widget button action */
1389 { nhStr("delete_text"), delete_text }, /* text widget delete action */
1390 { nhStr("key_dismiss_text"), key_dismiss_text }, /* text key action */
1391 #ifdef GRAPHIC_TOMBSTONE
1392 { nhStr("rip_dismiss_text"), rip_dismiss_text }, /* rip in text widget */
1393 #endif
1394 { nhStr("menu_key"), menu_key }, /* menu accelerators */
1395 { nhStr("yn_key"), yn_key }, /* yn accelerators */
1396 { nhStr("yn_delete"), yn_delete }, /* yn delete-window */
1397 { nhStr("askname_delete"), askname_delete }, /* askname delete-window */
1398 { nhStr("getline_delete"), getline_delete }, /* getline delete-window */
1399 { nhStr("menu_delete"), menu_delete }, /* menu delete-window */
1400 { nhStr("ec_key"), ec_key }, /* extended commands */
1401 { nhStr("ec_delete"), ec_delete }, /* ext-com menu delete */
1402 { nhStr("ps_key"), ps_key }, /* player selection */
1403 { nhStr("plsel_quit"), plsel_quit }, /* player selection dialog */
1404 { nhStr("plsel_play"), plsel_play }, /* player selection dialog */
1405 { nhStr("plsel_rnd"), plsel_randomize }, /* player selection dialog */
1406 { nhStr("race_key"), race_key }, /* race selection */
1407 { nhStr("gend_key"), gend_key }, /* gender selection */
1408 { nhStr("algn_key"), algn_key }, /* alignment selection */
1409 { nhStr("X11_hangup"), X11_hangup }, /* delete of top-level */
1410 { nhStr("input"), map_input }, /* key input */
1411 { nhStr("scroll"), nh_keyscroll }, /* scrolling by keys */
1412 };
1413
1414 static XtResource resources[] = {
1415 { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean),
1416 XtOffset(AppResources *, slow), XtRString, nhStr("True") },
1417 { nhStr("fancy_status"), nhStr("Fancy_status"), XtRBoolean, sizeof(Boolean),
1418 XtOffset(AppResources *, fancy_status), XtRString, nhStr("True") },
1419 { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean),
1420 XtOffset(AppResources *, autofocus), XtRString, nhStr("False") },
1421 { nhStr("message_line"), nhStr("Message_line"), XtRBoolean,
1422 sizeof(Boolean), XtOffset(AppResources *, message_line), XtRString,
1423 nhStr("False") },
1424 { nhStr("highlight_prompt"), nhStr("Highlight_prompt"), XtRBoolean,
1425 sizeof(Boolean), XtOffset(AppResources *, highlight_prompt), XtRString,
1426 nhStr("True") },
1427 { nhStr("double_tile_size"), nhStr("Double_tile_size"), XtRBoolean,
1428 sizeof(Boolean), XtOffset(AppResources *, double_tile_size), XtRString,
1429 nhStr("False") },
1430 { nhStr("tile_file"), nhStr("Tile_file"), XtRString, sizeof(String),
1431 XtOffset(AppResources *, tile_file), XtRString, nhStr("x11tiles") },
1432 { nhStr("icon"), nhStr("Icon"), XtRString, sizeof(String),
1433 XtOffset(AppResources *, icon), XtRString, nhStr("nh72") },
1434 { nhStr("message_lines"), nhStr("Message_lines"), XtRInt, sizeof(int),
1435 XtOffset(AppResources *, message_lines), XtRString, nhStr("12") },
1436 { nhStr("extcmd_height_delta"), nhStr("Extcmd_height_delta"),
1437 XtRInt, sizeof (int),
1438 XtOffset(AppResources *, extcmd_height_delta), XtRString, nhStr("0") },
1439 { nhStr("pet_mark_bitmap"), nhStr("Pet_mark_bitmap"), XtRString,
1440 sizeof(String), XtOffset(AppResources *, pet_mark_bitmap), XtRString,
1441 nhStr("pet_mark.xbm") },
1442 { nhStr("pet_mark_color"), nhStr("Pet_mark_color"), XtRPixel,
1443 sizeof(XtRPixel), XtOffset(AppResources *, pet_mark_color), XtRString,
1444 nhStr("Red") },
1445 { nhStr("pilemark_bitmap"), nhStr("Pilemark_bitmap"), XtRString,
1446 sizeof(String), XtOffset(AppResources *, pilemark_bitmap), XtRString,
1447 nhStr("pilemark.xbm") },
1448 { nhStr("pilemark_color"), nhStr("Pilemark_color"), XtRPixel,
1449 sizeof(XtRPixel), XtOffset(AppResources *, pilemark_color), XtRString,
1450 nhStr("Green") },
1451 #ifdef GRAPHIC_TOMBSTONE
1452 { nhStr("tombstone"), nhStr("Tombstone"), XtRString, sizeof(String),
1453 XtOffset(AppResources *, tombstone), XtRString, nhStr("rip.xpm") },
1454 { nhStr("tombtext_x"), nhStr("Tombtext_x"), XtRInt, sizeof(int),
1455 XtOffset(AppResources *, tombtext_x), XtRString, nhStr("155") },
1456 { nhStr("tombtext_y"), nhStr("Tombtext_y"), XtRInt, sizeof(int),
1457 XtOffset(AppResources *, tombtext_y), XtRString, nhStr("78") },
1458 { nhStr("tombtext_dx"), nhStr("Tombtext_dx"), XtRInt, sizeof(int),
1459 XtOffset(AppResources *, tombtext_dx), XtRString, nhStr("0") },
1460 { nhStr("tombtext_dy"), nhStr("Tombtext_dy"), XtRInt, sizeof(int),
1461 XtOffset(AppResources *, tombtext_dy), XtRString, nhStr("13") },
1462 #endif
1463 };
1464
1465 static int
panic_on_error(display,error)1466 panic_on_error(display, error)
1467 Display *display;
1468 XErrorEvent *error;
1469 {
1470 char buf[BUFSZ];
1471 XGetErrorText(display, error->error_code, buf, BUFSZ);
1472 fprintf(stderr, "X Error: code %i (%s), request %i, minor %i, serial %lu\n",
1473 error->error_code, buf,
1474 error->request_code, error->minor_code,
1475 error->serial);
1476 panic("X Error");
1477 return 0;
1478 }
1479
1480 void
X11_init_nhwindows(argcp,argv)1481 X11_init_nhwindows(argcp, argv)
1482 int *argcp;
1483 char **argv;
1484 {
1485 int i;
1486 Cardinal num_args;
1487 Arg args[4];
1488 uid_t savuid;
1489
1490 /* Init windows to nothing. */
1491 for (i = 0; i < MAX_WINDOWS; i++)
1492 window_list[i].type = NHW_NONE;
1493
1494 /* add another option that can be set */
1495 set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
1496 set_option_mod_status("mouse_support", SET_IN_GAME);
1497
1498 load_default_resources(); /* create default_resource_data[] */
1499
1500 /*
1501 * setuid hack: make sure that if nethack is setuid, to use real uid
1502 * when opening X11 connections, in case the user is using xauth, since
1503 * the "games" or whatever user probably doesn't have permission to open
1504 * a window on the user's display. This code is harmless if the binary
1505 * is not installed setuid. See include/system.h on compilation failures.
1506 */
1507 savuid = geteuid();
1508 (void) seteuid(getuid());
1509
1510 XSetIOErrorHandler((XIOErrorHandler) hangup);
1511
1512 num_args = 0;
1513 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
1514 XtSetArg(args[num_args], XtNtitle, "NetHack"); num_args++;
1515
1516 toplevel = XtAppInitialize(&app_context, "NetHack", /* application */
1517 (XrmOptionDescList) 0, 0, /* options list */
1518 argcp, (String *) argv, /* command line */
1519 default_resource_data, /* fallback resources */
1520 (ArgList) args, num_args);
1521 XtOverrideTranslations(toplevel,
1522 XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
1523
1524 /* We don't need to realize the top level widget. */
1525
1526 old_error_handler = XSetErrorHandler(panic_on_error);
1527
1528 #ifdef TEXTCOLOR
1529 /* add new color converter to deal with overused colormaps */
1530 XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
1531 (XtConvertArgList) nhcolorConvertArgs,
1532 XtNumber(nhcolorConvertArgs), XtCacheByDisplay,
1533 nhFreePixel);
1534 #endif /* TEXTCOLOR */
1535
1536 /* Register the actions mentioned in "actions". */
1537 XtAppAddActions(app_context, actions, XtNumber(actions));
1538
1539 /* Get application-wide resources */
1540 XtGetApplicationResources(toplevel, (XtPointer) &appResources, resources,
1541 XtNumber(resources), (ArgList) 0, ZERO);
1542
1543 /* Initialize other things. */
1544 init_standard_windows();
1545
1546 /* Give the window manager an icon to use; toplevel must be realized. */
1547 if (appResources.icon && *appResources.icon) {
1548 struct icon_info *ip;
1549
1550 for (ip = icon_data; ip->name; ip++)
1551 if (!strcmp(appResources.icon, ip->name)) {
1552 icon_pixmap = XCreateBitmapFromData(
1553 XtDisplay(toplevel), XtWindow(toplevel),
1554 (genericptr_t) ip->bits, ip->width, ip->height);
1555 if (icon_pixmap != None) {
1556 XWMHints hints;
1557
1558 (void) memset((genericptr_t) &hints, 0, sizeof(XWMHints));
1559 hints.flags = IconPixmapHint;
1560 hints.icon_pixmap = icon_pixmap;
1561 XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel),
1562 &hints);
1563 }
1564 break;
1565 }
1566 }
1567
1568 /* end of setuid hack: reset uid back to the "games" uid */
1569 (void) seteuid(savuid);
1570
1571 x_inited = TRUE; /* X is now initialized */
1572 plsel_ask_name = FALSE;
1573
1574 release_default_resources();
1575
1576 /* Display the startup banner in the message window. */
1577 for (i = 1; i <= 4 + 2; ++i) /* (values beyond 4 yield blank lines) */
1578 X11_putstr(WIN_MESSAGE, 0, copyright_banner_line(i));
1579 }
1580
1581 /*
1582 * All done.
1583 */
1584 /* ARGSUSED */
1585 void
X11_exit_nhwindows(dummy)1586 X11_exit_nhwindows(dummy)
1587 const char *dummy;
1588 {
1589 extern Pixmap tile_pixmap; /* from winmap.c */
1590
1591 nhUse(dummy);
1592
1593 /* explicitly free the icon and tile pixmaps */
1594 if (icon_pixmap != None) {
1595 XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1596 icon_pixmap = None;
1597 }
1598 if (tile_pixmap != None) {
1599 XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1600 tile_pixmap = None;
1601 }
1602 if (WIN_INVEN != WIN_ERR)
1603 X11_destroy_nhwindow(WIN_INVEN);
1604 if (WIN_STATUS != WIN_ERR)
1605 X11_destroy_nhwindow(WIN_STATUS);
1606 if (WIN_MAP != WIN_ERR)
1607 X11_destroy_nhwindow(WIN_MAP);
1608 if (WIN_MESSAGE != WIN_ERR)
1609 X11_destroy_nhwindow(WIN_MESSAGE);
1610
1611 release_getline_widgets();
1612 release_yn_widgets();
1613 }
1614
1615 #ifdef X11_HANGUP_SIGNAL
1616 static void
X11_sig(sig)1617 X11_sig(sig) /* Unix signal handler */
1618 int sig;
1619 {
1620 XtNoticeSignal(X11_sig_id);
1621 hangup(sig);
1622 }
1623
1624 static void
X11_sig_cb(not_used,id)1625 X11_sig_cb(not_used, id)
1626 XtPointer not_used;
1627 XtSignalId *id;
1628 {
1629 XEvent event;
1630 XClientMessageEvent *mesg;
1631
1632 nhUse(not_used);
1633 nhUse(id);
1634
1635 /* Set up a fake message to the event handler. */
1636 mesg = (XClientMessageEvent *) &event;
1637 mesg->type = ClientMessage;
1638 mesg->message_type = XA_STRING;
1639 mesg->format = 8;
1640
1641 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1642 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1643 (XEvent *) mesg);
1644 }
1645 #endif
1646
1647 /* delay_output ----------------------------------------------------------- */
1648
1649 /*
1650 * Timeout callback for delay_output(). Send a fake message to the map
1651 * window.
1652 */
1653 /* ARGSUSED */
1654 static void
d_timeout(client_data,id)1655 d_timeout(client_data, id)
1656 XtPointer client_data;
1657 XtIntervalId *id;
1658 {
1659 XEvent event;
1660 XClientMessageEvent *mesg;
1661
1662 nhUse(client_data);
1663 nhUse(id);
1664
1665 /* Set up a fake message to the event handler. */
1666 mesg = (XClientMessageEvent *) &event;
1667 mesg->type = ClientMessage;
1668 mesg->message_type = XA_STRING;
1669 mesg->format = 8;
1670 XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1671 XtWindow(window_list[WIN_MAP].w), False, NoEventMask,
1672 (XEvent *) mesg);
1673 }
1674
1675 /*
1676 * Delay for 50ms. This is not implemented asynch. Maybe later.
1677 * Start the timeout, then wait in the event loop. The timeout
1678 * function will send an event to the map window which will be waiting
1679 * for a sent event.
1680 */
1681 void
X11_delay_output()1682 X11_delay_output()
1683 {
1684 if (!x_inited)
1685 return;
1686
1687 (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1688
1689 /* The timeout function will enable the event loop exit. */
1690 (void) x_event(EXIT_ON_SENT_EVENT);
1691 }
1692
1693 /* X11_hangup ------------------------------------------------------------- */
1694 /* ARGSUSED */
1695 static void
X11_hangup(w,event,params,num_params)1696 X11_hangup(w, event, params, num_params)
1697 Widget w;
1698 XEvent *event;
1699 String *params;
1700 Cardinal *num_params;
1701 {
1702 nhUse(w);
1703 nhUse(event);
1704 nhUse(params);
1705 nhUse(num_params);
1706
1707 hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1708 exit_x_event = TRUE;
1709 }
1710
1711 /* askname ---------------------------------------------------------------- */
1712 /* ARGSUSED */
1713 static void
askname_delete(w,event,params,num_params)1714 askname_delete(w, event, params, num_params)
1715 Widget w;
1716 XEvent *event;
1717 String *params;
1718 Cardinal *num_params;
1719 {
1720 nhUse(event);
1721 nhUse(params);
1722 nhUse(num_params);
1723
1724 nh_XtPopdown(w);
1725 (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1726 exit_x_event = TRUE;
1727 }
1728
1729 /* Callback for askname dialog widget. */
1730 /* ARGSUSED */
1731 static void
askname_done(w,client_data,call_data)1732 askname_done(w, client_data, call_data)
1733 Widget w;
1734 XtPointer client_data;
1735 XtPointer call_data;
1736 {
1737 unsigned len;
1738 char *s;
1739 Widget dialog = (Widget) client_data;
1740
1741 nhUse(w);
1742 nhUse(call_data);
1743
1744 s = (char *) GetDialogResponse(dialog);
1745
1746 len = strlen(s);
1747 if (len == 0) {
1748 X11_nhbell();
1749 return;
1750 }
1751
1752 /* Truncate name if necessary */
1753 if (len >= sizeof plname - 1)
1754 len = sizeof plname - 1;
1755
1756 (void) strncpy(plname, s, len);
1757 plname[len] = '\0';
1758 XtFree(s);
1759
1760 nh_XtPopdown(XtParent(dialog));
1761 exit_x_event = TRUE;
1762 }
1763
1764 /* ask player for character's name to replace generic name "player" (or other
1765 values; see config.h) after 'nethack -u player' or OPTIONS=name:player */
1766 void
X11_askname()1767 X11_askname()
1768 {
1769 Widget popup, dialog;
1770 Arg args[1];
1771
1772 if (iflags.wc_player_selection == VIA_DIALOG) {
1773 /* X11_player_selection_dialog() handles name query */
1774 plsel_ask_name = TRUE;
1775 iflags.defer_plname = TRUE;
1776 return;
1777 } /* else iflags.wc_player_selection == VIA_PROMPTS */
1778
1779 XtSetArg(args[0], XtNallowShellResize, True);
1780
1781 popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel,
1782 args, ONE);
1783 XtOverrideTranslations(popup,
1784 XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1785
1786 dialog = CreateDialog(popup, nhStr("dialog"), askname_done,
1787 (XtCallbackProc) 0);
1788
1789 SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */
1790 SetDialogResponse(dialog, plname, PL_NSIZ); /* set default answer */
1791
1792 XtRealizeWidget(popup);
1793 positionpopup(popup, TRUE); /* center,bottom */
1794
1795 nh_XtPopup(popup, (int) XtGrabExclusive, dialog);
1796
1797 /* The callback will enable the event loop exit. */
1798 (void) x_event(EXIT_ON_EXIT);
1799
1800 XtDestroyWidget(dialog);
1801 XtDestroyWidget(popup);
1802 }
1803
1804 /* getline ---------------------------------------------------------------- */
1805 /* This uses Tim Theisen's dialog widget set (from GhostView). */
1806
1807 static Widget getline_popup, getline_dialog;
1808
1809 #define CANCEL_STR "\033"
1810 static char *getline_input;
1811
1812 /* Callback for getline dialog widget. */
1813 /* ARGSUSED */
1814 static void
done_button(w,client_data,call_data)1815 done_button(w, client_data, call_data)
1816 Widget w;
1817 XtPointer client_data;
1818 XtPointer call_data;
1819 {
1820 int len;
1821 char *s;
1822 Widget dialog = (Widget) client_data;
1823
1824 nhUse(w);
1825 nhUse(call_data);
1826
1827 s = (char *) GetDialogResponse(dialog);
1828 len = strlen(s);
1829
1830 /* Truncate input if necessary */
1831 if (len >= BUFSZ)
1832 len = BUFSZ - 1;
1833
1834 (void) strncpy(getline_input, s, len);
1835 getline_input[len] = '\0';
1836 XtFree(s);
1837
1838 nh_XtPopdown(XtParent(dialog));
1839 exit_x_event = TRUE;
1840 }
1841
1842 /* ARGSUSED */
1843 static void
getline_delete(w,event,params,num_params)1844 getline_delete(w, event, params, num_params)
1845 Widget w;
1846 XEvent *event;
1847 String *params;
1848 Cardinal *num_params;
1849 {
1850 nhUse(event);
1851 nhUse(params);
1852 nhUse(num_params);
1853
1854 Strcpy(getline_input, CANCEL_STR);
1855 nh_XtPopdown(w);
1856 exit_x_event = TRUE;
1857 }
1858
1859 /* Callback for getline dialog widget. */
1860 /* ARGSUSED */
1861 static void
abort_button(w,client_data,call_data)1862 abort_button(w, client_data, call_data)
1863 Widget w;
1864 XtPointer client_data;
1865 XtPointer call_data;
1866 {
1867 Widget dialog = (Widget) client_data;
1868
1869 nhUse(w);
1870 nhUse(call_data);
1871
1872 Strcpy(getline_input, CANCEL_STR);
1873 nh_XtPopdown(XtParent(dialog));
1874 exit_x_event = TRUE;
1875 }
1876
1877 static void
release_getline_widgets()1878 release_getline_widgets()
1879 {
1880 if (getline_dialog)
1881 XtDestroyWidget(getline_dialog), getline_dialog = (Widget) 0;
1882 if (getline_popup)
1883 XtDestroyWidget(getline_popup), getline_popup = (Widget) 0;
1884 }
1885
1886 void
X11_getlin(question,input)1887 X11_getlin(question, input)
1888 const char *question;
1889 char *input;
1890 {
1891 getline_input = input;
1892
1893 flush_screen(1);
1894 if (!getline_popup) {
1895 Arg args[1];
1896
1897 XtSetArg(args[0], XtNallowShellResize, True);
1898
1899 getline_popup = XtCreatePopupShell("getline",
1900 transientShellWidgetClass,
1901 toplevel, args, ONE);
1902 XtOverrideTranslations(getline_popup,
1903 XtParseTranslationTable(
1904 "<Message>WM_PROTOCOLS: getline_delete()"));
1905
1906 getline_dialog = CreateDialog(getline_popup, nhStr("dialog"),
1907 done_button, abort_button);
1908
1909 XtRealizeWidget(getline_popup);
1910 XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1911 &wm_delete_window, 1);
1912 }
1913 SetDialogPrompt(getline_dialog, (String) question); /* set prompt */
1914 /* 60: make the answer widget be wide enough to hold 60 characters,
1915 or the length of the prompt string, whichever is bigger. User's
1916 response can be longer--when limit is reached, value-so-far will
1917 slide left hiding some chars at the beginning of the response but
1918 making room to type more. [Prior to 3.6.1, width wasn't specifiable
1919 and answer box always got sized to match the width of the prompt.] */
1920 #ifdef EDIT_GETLIN
1921 SetDialogResponse(getline_dialog, input, 60); /* set default answer */
1922 #else
1923 SetDialogResponse(getline_dialog, nhStr(""), 60); /* set default answer */
1924 #endif
1925 positionpopup(getline_popup, TRUE); /* center,bottom */
1926
1927 nh_XtPopup(getline_popup, (int) XtGrabExclusive, getline_dialog);
1928
1929 /* The callback will enable the event loop exit. */
1930 (void) x_event(EXIT_ON_EXIT);
1931 }
1932
1933 /* Display file ----------------------------------------------------------- */
1934
1935 /* uses a menu (with no selectors specified) rather than a text window
1936 to allow previous_page and first_menu actions to move backwards */
1937 void
X11_display_file(str,complain)1938 X11_display_file(str, complain)
1939 const char *str;
1940 boolean complain;
1941 {
1942 dlb *fp;
1943 winid newwin;
1944 struct xwindow *wp;
1945 anything any;
1946 menu_item *menu_list;
1947 #define LLEN 128
1948 char line[LLEN];
1949
1950 /* Use the port-independent file opener to see if the file exists. */
1951 fp = dlb_fopen(str, RDTMODE);
1952 if (!fp) {
1953 if (complain)
1954 pline("Cannot open %s. Sorry.", str);
1955 return; /* it doesn't exist, ignore */
1956 }
1957
1958 newwin = X11_create_nhwindow(NHW_MENU);
1959 wp = &window_list[newwin];
1960 X11_start_menu(newwin);
1961
1962 any = zeroany;
1963 while (dlb_fgets(line, LLEN, fp)) {
1964 X11_add_menu(newwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1965 line, MENU_UNSELECTED);
1966 }
1967 (void) dlb_fclose(fp);
1968
1969 /* show file name as the window title */
1970 if (str)
1971 wp->title = dupstr(str);
1972
1973 wp->menu_information->permi = FALSE;
1974 wp->menu_information->disable_mcolors = TRUE;
1975 (void) X11_select_menu(newwin, PICK_NONE, &menu_list);
1976 X11_destroy_nhwindow(newwin);
1977 }
1978
1979 /* yn_function ------------------------------------------------------------ */
1980 /* (not threaded) */
1981
1982 static const char *yn_quitchars = " \n\r";
1983 static const char *yn_choices; /* string of acceptable input */
1984 static char yn_def;
1985 static char yn_return; /* return value */
1986 static char yn_esc_map; /* ESC maps to this char. */
1987 static Widget yn_popup; /* popup for the yn fuction (created once) */
1988 static Widget yn_label; /* label for yn function (created once) */
1989 static boolean yn_getting_num; /* TRUE if accepting digits */
1990 static boolean yn_preserve_case; /* default is to force yn to lower case */
1991 static int yn_ndigits; /* digit count */
1992 static long yn_val; /* accumulated value */
1993
1994 static const char yn_translations[] = "#override\n\
1995 <Key>: yn_key()";
1996
1997 /*
1998 * Convert the given key event into a character. If the key maps to
1999 * more than one character only the first is returned. If there is
2000 * no conversion (i.e. just the CTRL key hit) a NUL is returned.
2001 */
2002 char
key_event_to_char(key)2003 key_event_to_char(key)
2004 XKeyEvent *key;
2005 {
2006 char keystring[MAX_KEY_STRING];
2007 int nbytes;
2008 boolean meta = !!(key->state & Mod1Mask);
2009
2010 nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0,
2011 (XComposeStatus *) 0);
2012
2013 /* Modifier keys return a zero lengh string when pressed. */
2014 if (nbytes == 0)
2015 return '\0';
2016
2017 return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
2018 }
2019
2020 /*
2021 * Called when we get a WM_DELETE_WINDOW event on a yn window.
2022 */
2023 /* ARGSUSED */
2024 static void
yn_delete(w,event,params,num_params)2025 yn_delete(w, event, params, num_params)
2026 Widget w;
2027 XEvent *event;
2028 String *params;
2029 Cardinal *num_params;
2030 {
2031 nhUse(w);
2032 nhUse(event);
2033 nhUse(params);
2034 nhUse(num_params);
2035
2036 yn_getting_num = FALSE;
2037 /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */
2038 yn_return = yn_choices ? yn_esc_map : '\033';
2039 exit_x_event = TRUE; /* exit our event handler */
2040 }
2041
2042 /*
2043 * Called when we get a key press event on a yn window.
2044 */
2045 /* ARGSUSED */
2046 static void
yn_key(w,event,params,num_params)2047 yn_key(w, event, params, num_params)
2048 Widget w;
2049 XEvent *event;
2050 String *params;
2051 Cardinal *num_params;
2052 {
2053 char ch;
2054
2055 if (appResources.slow && !input_func)
2056 map_input(w, event, params, num_params);
2057
2058 ch = key_event_to_char((XKeyEvent *) event);
2059
2060 if (ch == '\0') { /* don't accept nul char or modifier event */
2061 /* no bell */
2062 return;
2063 }
2064
2065 if (!yn_choices) { /* accept any input */
2066 yn_return = ch;
2067 } else {
2068 if (!yn_preserve_case)
2069 ch = lowc(ch); /* move to lower case */
2070
2071 if (ch == '\033') {
2072 yn_getting_num = FALSE;
2073 yn_return = yn_esc_map;
2074 } else if (index(yn_quitchars, ch)) {
2075 yn_return = yn_def;
2076 } else if (index(yn_choices, ch)) {
2077 if (ch == '#') {
2078 if (yn_getting_num) { /* don't select again */
2079 X11_nhbell();
2080 return;
2081 }
2082 yn_getting_num = TRUE;
2083 yn_ndigits = 0;
2084 yn_val = 0;
2085 return; /* wait for more input */
2086 }
2087 yn_return = ch;
2088 if (ch != 'y')
2089 yn_getting_num = FALSE;
2090 } else {
2091 if (yn_getting_num) {
2092 if (digit(ch)) {
2093 yn_ndigits++;
2094 yn_val = (yn_val * 10) + (long) (ch - '0');
2095 return; /* wait for more input */
2096 }
2097 if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) {
2098 yn_ndigits--;
2099 yn_val = yn_val / 10;
2100 return; /* wait for more input */
2101 }
2102 }
2103 X11_nhbell(); /* no match */
2104 return;
2105 }
2106
2107 if (yn_getting_num) {
2108 yn_return = '#';
2109 if (yn_val < 0)
2110 yn_val = 0;
2111 yn_number = yn_val; /* assign global */
2112 }
2113 }
2114 exit_x_event = TRUE; /* exit our event handler */
2115 }
2116
2117 /* called at exit time */
2118 static void
release_yn_widgets()2119 release_yn_widgets()
2120 {
2121 if (yn_label)
2122 XtDestroyWidget(yn_label), yn_label = (Widget) 0;
2123 if (yn_popup)
2124 XtDestroyWidget(yn_popup), yn_popup = (Widget) 0;
2125 }
2126
2127 /* X11-specific edition of yn_function(), the routine called by the core
2128 to show a prompt and get a single keystroke answer, often 'y' vs 'n' */
2129 char
X11_yn_function(ques,choices,def)2130 X11_yn_function(ques, choices, def)
2131 const char *ques;
2132 const char *choices; /* string of possible response chars; any char if Null */
2133 char def; /* default response if user hits <space> or <return> */
2134 {
2135 char buf[BUFSZ];
2136 Arg args[4];
2137 Cardinal num_args;
2138
2139 yn_choices = choices; /* set up globals for callback to use */
2140 yn_def = def;
2141 yn_preserve_case = !choices; /* preserve case when an arbitrary
2142 response is allowed */
2143
2144 /*
2145 * This is sort of a kludge. There are quite a few places in the main
2146 * nethack code where a pline containing information is followed by a
2147 * call to yn_function(). There is no flush of the message window
2148 * (it is implicit in the tty window port), so the line never shows
2149 * up for us! Solution: do our own flush.
2150 */
2151 if (WIN_MESSAGE != WIN_ERR)
2152 display_message_window(&window_list[WIN_MESSAGE]);
2153
2154 if (choices) {
2155 char *cb, choicebuf[QBUFSZ];
2156
2157 Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
2158 /* default when choices are present is to force yn answer to
2159 lowercase unless one or more choices are explicitly uppercase;
2160 check this before stripping the hidden choices */
2161 for (cb = choicebuf; *cb; ++cb)
2162 if ('A' <= *cb && *cb <= 'Z') {
2163 yn_preserve_case = TRUE;
2164 break;
2165 }
2166 if ((cb = index(choicebuf, '\033')) != 0)
2167 *cb = '\0';
2168 /* ques [choices] (def) */
2169 if ((int) (1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= BUFSZ)
2170 panic("X11_yn_function: question too long");
2171 (void) strncpy(buf, ques, QBUFSZ - 1);
2172 buf[QBUFSZ - 1] = '\0';
2173 Sprintf(eos(buf), " [%s]", choicebuf);
2174 if (def)
2175 Sprintf(eos(buf), " (%c)", def);
2176 Strcat(buf, " ");
2177
2178 /* escape maps to 'q' or 'n' or default, in that order */
2179 yn_esc_map = (index(choices, 'q') ? 'q'
2180 : index(choices, 'n') ? 'n'
2181 : def);
2182 } else {
2183 if ((int) (1 + strlen(ques) + 1) >= BUFSZ)
2184 panic("X11_yn_function: question too long");
2185 Strcpy(buf, ques);
2186 Strcat(buf, " ");
2187 }
2188
2189 /*
2190 * The 'slow' resource is misleadingly named. When it is True, we
2191 * re-use the same one-line widget above the map for all yn prompt
2192 * responses instead of re-using a popup widget for each one. It's
2193 * crucial for client/server configs where the server is slow putting
2194 * up a popup or requires click-to-focus to respond to the popup, but
2195 * is also useful for players who just want to be able to look at the
2196 * same location to read questions they're being asked to answer.
2197 */
2198
2199 if (appResources.slow) {
2200 /*
2201 * 'slow': the yn_label widget was created when the map and
2202 * status widgets were, and is positioned between them. It
2203 * will persist until end of game. All we need to do for
2204 * yn_function is direct keystroke input to the yn response
2205 * handler and reset its label to be the prompt text (below).
2206 */
2207 input_func = yn_key;
2208 highlight_yn(FALSE); /* expose yn_label as separate from map */
2209 } else if (!yn_label) {
2210 /*
2211 * Not 'slow'; create a persistent widget that will be popped up
2212 * as needed, then down again, and last until end of game. The
2213 * associated yn_label widget is used to track whether it exists.
2214 */
2215 XtSetArg(args[0], XtNallowShellResize, True);
2216 yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
2217 toplevel, args, ONE);
2218 XtOverrideTranslations(yn_popup,
2219 XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
2220
2221 num_args = 0;
2222 XtSetArg(args[num_args], XtNtranslations,
2223 XtParseTranslationTable(yn_translations)); num_args++;
2224 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass,
2225 yn_popup, args, num_args);
2226
2227 XtRealizeWidget(yn_popup);
2228 XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
2229 &wm_delete_window, 1);
2230 }
2231
2232 /* set the label of the yn widget to be the prompt text */
2233 num_args = 0;
2234 XtSetArg(args[num_args], XtNlabel, buf); num_args++;
2235 XtSetValues(yn_label, args, num_args);
2236
2237 if (!appResources.slow) {
2238 /*
2239 * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
2240 * need to set the label twice to get the size to change.
2241 */
2242 num_args = 0;
2243 XtSetArg(args[num_args], XtNlabel, buf); num_args++;
2244 XtSetValues(yn_label, args, num_args);
2245
2246 positionpopup(yn_popup, TRUE);
2247 nh_XtPopup(yn_popup, (int) XtGrabExclusive, yn_label);
2248 }
2249
2250 yn_getting_num = FALSE;
2251 (void) x_event(EXIT_ON_EXIT); /* get keystroke(s) */
2252
2253 if (appResources.slow) {
2254 /* keystrokes now belong to the map */
2255 input_func = 0;
2256 /* erase the prompt */
2257 num_args = 0;
2258 XtSetArg(args[num_args], XtNlabel, " "); num_args++;
2259 XtSetValues(yn_label, args, num_args);
2260 highlight_yn(FALSE); /* disguise yn_label as part of map */
2261 } else {
2262 nh_XtPopdown(yn_popup); /* this removes the event grab */
2263 }
2264
2265 pline("%s%c", buf, (yn_return != '\033') ? yn_return : '\0');
2266
2267 return yn_return;
2268 }
2269
2270 /* used when processing window-capability-specific run-time options;
2271 we support toggling tiles on and off via iflags.wc_tiled_map */
2272 void
X11_preference_update(pref)2273 X11_preference_update(pref)
2274 const char *pref;
2275 {
2276 if (!strcmp(pref, "tiled_map")) {
2277 if (WIN_MAP != WIN_ERR)
2278 display_map_window(&window_list[WIN_MAP]);
2279 }
2280 }
2281
2282 /* End global functions =================================================== */
2283
2284 /*
2285 * Before we wait for input via nhgetch() and nh_poskey(), we need to
2286 * do some pre-processing.
2287 */
2288 static int
input_event(exit_condition)2289 input_event(exit_condition)
2290 int exit_condition;
2291 {
2292 if (appResources.fancy_status && WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
2293 check_turn_events();
2294 if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
2295 check_cursor_visibility(&window_list[WIN_MAP]);
2296 if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
2297 set_last_pause(&window_list[WIN_MESSAGE]);
2298
2299 return x_event(exit_condition);
2300 }
2301
2302 /*ARGSUSED*/
2303 void
msgkey(w,data,event)2304 msgkey(w, data, event)
2305 Widget w;
2306 XtPointer data;
2307 XEvent *event;
2308 {
2309 Cardinal num = 0;
2310
2311 nhUse(w);
2312 nhUse(data);
2313
2314 map_input(window_list[WIN_MAP].w, event, (String *) 0, &num);
2315 }
2316
2317 /* only called for autofocus */
2318 /*ARGSUSED*/
2319 static void
win_visible(w,data,event,flag)2320 win_visible(w, data, event, flag)
2321 Widget w;
2322 XtPointer data; /* client_data not used */
2323 XEvent *event;
2324 Boolean *flag; /* continue_to_dispatch flag not used */
2325 {
2326 XVisibilityEvent *vis_event = (XVisibilityEvent *) event;
2327
2328 nhUse(data);
2329 nhUse(flag);
2330
2331 if (vis_event->state != VisibilityFullyObscured) {
2332 /* one-time operation; cancel ourself */
2333 XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
2334 win_visible, (XtPointer) 0);
2335 /* grab initial input focus */
2336 XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
2337 }
2338 }
2339
2340 /* if 'slow' and 'highlight_prompt', set the yn_label widget to look like
2341 part of the map when idle or to invert background and foreground when
2342 a prompt is active */
2343 void
highlight_yn(init)2344 highlight_yn(init)
2345 boolean init;
2346 {
2347 struct xwindow *xmap;
2348
2349 if (!appResources.slow || !appResources.highlight_prompt)
2350 return;
2351
2352 /* first time through, WIN_MAP isn't fully initiialized yet */
2353 xmap = ((map_win != WIN_ERR) ? &window_list[map_win]
2354 : (WIN_MAP != WIN_ERR) ? &window_list[WIN_MAP] : 0);
2355
2356 if (init && xmap) {
2357 Arg args[2];
2358 XGCValues vals;
2359 unsigned long fg_bg = (GCForeground | GCBackground);
2360 GC gc = (xmap->map_information->is_tile
2361 ? xmap->map_information->tile_map.white_gc
2362 : xmap->map_information->text_map.copy_gc);
2363
2364 (void) memset((genericptr_t) &vals, 0, sizeof vals);
2365 if (XGetGCValues(XtDisplay(xmap->w), gc, fg_bg, &vals)) {
2366 XtSetArg(args[0], XtNforeground, vals.foreground);
2367 XtSetArg(args[1], XtNbackground, vals.background);
2368 XtSetValues(yn_label, args, TWO);
2369 }
2370 } else
2371 swap_fg_bg(yn_label);
2372 }
2373
2374 /*
2375 * Set up the playing console. This has three major parts: the
2376 * message window, the map, and the status window.
2377 *
2378 * For configs specifying the 'slow' resource, the yn_label widget
2379 * is placed above the map and below the message window. Prompts
2380 * requiring a single character response are displayed there rather
2381 * than using a popup.
2382 */
2383 static void
init_standard_windows()2384 init_standard_windows()
2385 {
2386 Widget form, message_viewport, map_viewport, status;
2387 Arg args[8];
2388 Cardinal num_args;
2389 Dimension message_vp_width, map_vp_width, status_width, max_width;
2390 int map_vp_hd, status_hd;
2391 struct xwindow *wp;
2392
2393 num_args = 0;
2394 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
2395 form = XtCreateManagedWidget("nethack", panedWidgetClass, toplevel, args,
2396 num_args);
2397
2398 XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey,
2399 (XtPointer) 0);
2400
2401 if (appResources.autofocus)
2402 XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible,
2403 (XtPointer) 0);
2404
2405 /*
2406 * Create message window.
2407 */
2408 WIN_MESSAGE = message_win = find_free_window();
2409 wp = &window_list[message_win];
2410 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2411 wp->popup = (Widget) 0;
2412 create_message_window(wp, FALSE, form);
2413 message_viewport = XtParent(wp->w);
2414
2415 /* Tell the form that contains it that resizes are OK. */
2416 num_args = 0;
2417 XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2418 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2419 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
2420 XtSetValues(message_viewport, args, num_args);
2421
2422 if (appResources.slow) {
2423 num_args = 0;
2424 XtSetArg(args[num_args], XtNtranslations,
2425 XtParseTranslationTable(yn_translations)); num_args++;
2426 yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form,
2427 args, num_args);
2428 num_args = 0;
2429 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2430 num_args++;
2431 XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft);
2432 num_args++;
2433 XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
2434 XtSetArg(args[num_args], nhStr(XtNlabel), " "); num_args++;
2435 XtSetValues(yn_label, args, num_args);
2436 }
2437
2438 /*
2439 * Create the map window & viewport and chain the viewport beneath the
2440 * message_viewport.
2441 */
2442 map_win = find_free_window();
2443 wp = &window_list[map_win];
2444 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2445 wp->popup = (Widget) 0;
2446 create_map_window(wp, FALSE, form);
2447 map_viewport = XtParent(wp->w);
2448
2449 /* Chain beneath message_viewport or yn window. */
2450 num_args = 0;
2451 if (appResources.slow) {
2452 XtSetArg(args[num_args], nhStr(XtNfromVert), yn_label); num_args++;
2453 } else {
2454 XtSetArg(args[num_args], nhStr(XtNfromVert), message_viewport);
2455 num_args++;
2456 }
2457 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2458 XtSetValues(map_viewport, args, num_args);
2459
2460 /* Create the status window, with the form as it's parent. */
2461 status_win = find_free_window();
2462 wp = &window_list[status_win];
2463 wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
2464 wp->popup = (Widget) 0;
2465 create_status_window(wp, FALSE, form);
2466 status = wp->w;
2467
2468 /*
2469 * Chain the status window beneath the viewport. Mark the left and right
2470 * edges so that they stay a fixed distance from the left edge of the
2471 * parent, as well as the top and bottom edges so that they stay a fixed
2472 * distance from the bottom of the parent. We do this so that the status
2473 * will never expand or contract.
2474 */
2475 num_args = 0;
2476 XtSetArg(args[num_args], nhStr(XtNfromVert), map_viewport); num_args++;
2477 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
2478 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
2479 XtSetArg(args[num_args], nhStr(XtNtop), XtChainBottom); num_args++;
2480 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
2481 XtSetValues(status, args, num_args);
2482
2483 /*
2484 * Realize the popup so that the status widget knows it's size.
2485 *
2486 * If we unset MappedWhenManaged then the DECwindow driver doesn't
2487 * attach the nethack toplevel to the highest virtual root window.
2488 * So don't do it.
2489 */
2490 /* XtSetMappedWhenManaged(toplevel, False); */
2491 XtRealizeWidget(toplevel);
2492 wm_delete_window = XInternAtom(XtDisplay(toplevel),
2493 "WM_DELETE_WINDOW", False);
2494 XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
2495 &wm_delete_window, 1);
2496
2497 /*
2498 * Resize to at most full-screen.
2499 */
2500 {
2501 #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
2502
2503 int screen_width = WidthOfScreen(XtScreen(wp->w));
2504 int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
2505 Dimension form_width, form_height;
2506
2507 XtSetArg(args[0], XtNwidth, &form_width);
2508 XtSetArg(args[1], XtNheight, &form_height);
2509 XtGetValues(toplevel, args, TWO);
2510
2511 if (form_width > screen_width || form_height > screen_height) {
2512 XtSetArg(args[0], XtNwidth, min(form_width, screen_width));
2513 XtSetArg(args[1], XtNheight, min(form_height, screen_height));
2514 XtSetValues(toplevel, args, TWO);
2515 XMoveWindow(XtDisplay(toplevel), XtWindow(toplevel), 0,
2516 TITLEBAR_SPACE);
2517 }
2518 #undef TITLEBAR_SPACE
2519 }
2520
2521 post_process_tiles(); /* after toplevel is realized */
2522
2523 /*
2524 * Now get the default widths of the windows.
2525 */
2526 XtSetArg(args[0], nhStr(XtNwidth), &message_vp_width);
2527 XtGetValues(message_viewport, args, ONE);
2528 XtSetArg(args[0], nhStr(XtNwidth), &map_vp_width);
2529 XtSetArg(args[1], nhStr(XtNhorizDistance), &map_vp_hd);
2530 XtGetValues(map_viewport, args, TWO);
2531 XtSetArg(args[0], nhStr(XtNwidth), &status_width);
2532 XtSetArg(args[1], nhStr(XtNhorizDistance), &status_hd);
2533 XtGetValues(status, args, TWO);
2534
2535 /*
2536 * Adjust positions and sizes. The message viewport widens out to the
2537 * widest width. Both the map and status are centered by adjusting
2538 * their horizDistance.
2539 */
2540 if (map_vp_width < status_width || map_vp_width < message_vp_width) {
2541 if (status_width > message_vp_width) {
2542 XtSetArg(args[0], nhStr(XtNwidth), status_width);
2543 XtSetValues(message_viewport, args, ONE);
2544 max_width = status_width;
2545 } else {
2546 max_width = message_vp_width;
2547 }
2548 XtSetArg(args[0], nhStr(XtNhorizDistance),
2549 map_vp_hd + ((int) (max_width - map_vp_width) / 2));
2550 XtSetValues(map_viewport, args, ONE);
2551
2552 } else { /* map is widest */
2553 XtSetArg(args[0], nhStr(XtNwidth), map_vp_width);
2554 XtSetValues(message_viewport, args, ONE);
2555 }
2556 /*
2557 * Clear all data values on the fancy status widget so that the values
2558 * used for spacing don't appear. This needs to be called some time
2559 * after the fancy status widget is realized (above, with the game popup),
2560 * but before it is popped up.
2561 */
2562 if (appResources.fancy_status)
2563 null_out_status();
2564 /*
2565 * Set the map size to its standard size. As with the message window
2566 * above, the map window needs to be set to its constrained size until
2567 * its parent (the viewport widget) was realized.
2568 *
2569 * Move the message window's slider to the bottom.
2570 */
2571 set_map_size(&window_list[map_win], COLNO, ROWNO);
2572 set_message_slider(&window_list[message_win]);
2573
2574 /* attempt to catch fatal X11 errors before the program quits */
2575 (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
2576
2577 highlight_yn(TRUE); /* switch foreground and background */
2578
2579 /* We can now print to the message window. */
2580 iflags.window_inited = 1;
2581 }
2582
2583 void
nh_XtPopup(w,g,childwid)2584 nh_XtPopup(w, g, childwid)
2585 Widget w; /* widget */
2586 int g; /* type of grab */
2587 Widget childwid; /* child to receive focus (can be None) */
2588 {
2589 XtPopup(w, (XtGrabKind) g);
2590 XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
2591 if (appResources.autofocus)
2592 XtSetKeyboardFocus(toplevel, childwid);
2593 }
2594
2595 void
nh_XtPopdown(w)2596 nh_XtPopdown(w)
2597 Widget w;
2598 {
2599 XtPopdown(w);
2600 if (appResources.autofocus)
2601 XtSetKeyboardFocus(toplevel, None);
2602 }
2603
2604 void
win_X11_init(dir)2605 win_X11_init(dir)
2606 int dir;
2607 {
2608 if (dir != WININIT)
2609 return;
2610
2611 #ifdef OPENWINBUG
2612 /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
2613 * two routines will not be found when linking. An apparently correct
2614 * executable is produced, along with nasty messages and a failure code
2615 * returned to make. The routines are in the static libXmu.a and
2616 * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with
2617 * static linking, we do this.
2618 */
2619 if (rn2(2) > 2) { /* i.e., FALSE that an optimizer probably can't find */
2620 get_wmShellWidgetClass();
2621 get_applicationShellWidgetClass();
2622 }
2623 #endif
2624 return;
2625 }
2626
2627 void
find_scrollbars(w,horiz,vert)2628 find_scrollbars(w, horiz, vert)
2629 Widget w;
2630 Widget *horiz, *vert;
2631 {
2632 if (w) {
2633 do {
2634 *horiz = XtNameToWidget(w, "*horizontal");
2635 *vert = XtNameToWidget(w, "*vertical");
2636 w = XtParent(w);
2637 } while (!*horiz && !*vert && w);
2638 }
2639 }
2640
2641 /* Callback
2642 * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
2643 */
2644 /*ARGSUSED*/
2645 void
nh_keyscroll(viewport,event,params,num_params)2646 nh_keyscroll(viewport, event, params, num_params)
2647 Widget viewport;
2648 XEvent *event;
2649 String *params;
2650 Cardinal *num_params;
2651 {
2652 Arg arg[2];
2653 Widget horiz_sb = (Widget) 0, vert_sb = (Widget) 0;
2654 float top, shown;
2655 Boolean do_call;
2656 int direction;
2657 Cardinal in_nparams = (num_params ? *num_params : 0);
2658
2659 nhUse(event);
2660
2661 if (in_nparams != 1)
2662 return; /* bad translation */
2663
2664 direction = atoi(params[0]);
2665
2666 find_scrollbars(viewport, &horiz_sb, &vert_sb);
2667
2668 #define H_DELTA 0.25 /* distance of horiz shift */
2669 /* vert shift is half of curr distance */
2670 /* The V_DELTA is 1/2 the value of shown. */
2671
2672 if (horiz_sb) {
2673 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2674 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2675 XtGetValues(horiz_sb, arg, TWO);
2676
2677 do_call = True;
2678
2679 switch (direction) {
2680 case 1:
2681 case 4:
2682 case 7:
2683 top -= H_DELTA;
2684 if (top < 0.0)
2685 top = 0.0;
2686 break;
2687 case 3:
2688 case 6:
2689 case 9:
2690 top += H_DELTA;
2691 if (top + shown > 1.0)
2692 top = 1.0 - shown;
2693 break;
2694 default:
2695 do_call = False;
2696 }
2697
2698 if (do_call) {
2699 XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2700 }
2701 }
2702
2703 if (vert_sb) {
2704 XtSetArg(arg[0], nhStr(XtNshown), &shown);
2705 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
2706 XtGetValues(vert_sb, arg, TWO);
2707
2708 do_call = True;
2709
2710 switch (direction) {
2711 case 7:
2712 case 8:
2713 case 9:
2714 top -= shown / 2.0;
2715 if (top < 0.0)
2716 top = 0;
2717 break;
2718 case 1:
2719 case 2:
2720 case 3:
2721 top += shown / 2.0;
2722 if (top + shown > 1.0)
2723 top = 1.0 - shown;
2724 break;
2725 default:
2726 do_call = False;
2727 }
2728
2729 if (do_call) {
2730 XtCallCallbacks(vert_sb, XtNjumpProc, &top);
2731 }
2732 }
2733 }
2734
2735 /*winX.c*/
2736