1 | /* SCCS Id: @(#)winX.c 3.3 1999/12/21 */
2 | /* Copyright (c) Dean Luick, 1992 */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | /*
6 | * "Main" file for the X window-port. This contains most of the interface
7 | * routines. Please see doc/window.doc for an description of the window
8 | * interface.
9 | */
10 |
11 | #ifndef SYSV
12 | #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 | #endif
14 |
15 | #ifdef MSDOS /* from compiler */
16 | #define SHORT_FILENAMES
17 | #endif
18 |
19 | #include <X11/Intrinsic.h>
20 | #include <X11/StringDefs.h>
21 | #include <X11/Shell.h>
22 | #include <X11/Xaw/AsciiText.h>
23 | #include <X11/Xaw/Label.h>
24 | #include <X11/Xaw/Form.h>
25 | #include <X11/Xaw/Scrollbar.h>
26 | #include <X11/Xaw/Paned.h>
27 | #include <X11/Xaw/Cardinals.h>
28 | #include <X11/Xatom.h>
29 | #include <X11/Xos.h>
30 |
31 | /* for color support */
32 | #ifdef SHORT_FILENAMES
33 | #include <X11/IntrinsP.h>
34 | #else
35 | #include <X11/IntrinsicP.h>
36 | #endif
37 |
38 | #ifdef PRESERVE_NO_SYSV
39 | # ifdef SYSV
40 | # undef SYSV
41 | # endif
42 | # undef PRESERVE_NO_SYSV
43 | #endif
44 |
45 | #ifdef SHORT_FILENAMES
46 | #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */
47 | #endif
48 |
49 | #include "hack.h"
50 | #include "winX.h"
51 | #include "dlb.h"
52 | #ifdef SHORT_FILENAMES
53 | #include "patchlev.h"
54 | #else
55 | #include "patchlevel.h"
56 | #endif
57 |
58 | /* Should be defined in <X11/Intrinsic.h> but you never know */
59 | #ifndef XtSpecificationRelease
60 | #define XtSpecificationRelease 0
61 | #endif
62 |
63 | /*
64 | * Icons.
65 | */
66 | #include "../win/X11/nh72icon"
67 | #include "../win/X11/nh56icon"
68 | #include "../win/X11/nh32icon"
69 |
70 | static struct icon_info {
71 | const char *name;
72 | unsigned char *bits;
73 | unsigned width, height;
74 | } icon_data[] = {
75 | { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
76 | { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
77 | { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
78 | { (const char *)0, (unsigned char *)0, 0, 0 }
79 | };
80 |
81 | /*
82 | * Private global variables (shared among the window port files).
83 | */
84 | struct xwindow window_list[MAX_WINDOWS];
85 | AppResources appResources;
86 | void FDECL((*input_func), (Widget,XEvent *,String *,Cardinal *));
87 | int click_x, click_y, click_button; /* Click position on a map window */
88 | /* (filled by set_button_values()). */
89 | int updated_inventory;
90 |
91 |
92 | /* Interface definition, for windows.c */
93 | struct window_procs X11_procs = {
94 | "X11",
95 | X11_init_nhwindows,
96 | X11_player_selection,
97 | X11_askname,
98 | X11_get_nh_event,
99 | X11_exit_nhwindows,
100 | X11_suspend_nhwindows,
101 | X11_resume_nhwindows,
102 | X11_create_nhwindow,
103 | X11_clear_nhwindow,
104 | X11_display_nhwindow,
105 | X11_destroy_nhwindow,
106 | X11_curs,
107 | X11_putstr,
108 | X11_display_file,
109 | X11_start_menu,
110 | X11_add_menu,
111 | X11_end_menu,
112 | X11_select_menu,
113 | genl_message_menu, /* no need for X-specific handling */
114 | X11_update_inventory,
115 | X11_mark_synch,
116 | X11_wait_synch,
117 | #ifdef CLIPPING
118 | X11_cliparound,
119 | #endif
120 | #ifdef POSITIONBAR
121 | donull,
122 | #endif
123 | X11_print_glyph,
124 | X11_raw_print,
125 | X11_raw_print_bold,
126 | X11_nhgetch,
127 | X11_nh_poskey,
128 | X11_nhbell,
129 | X11_doprev_message,
130 | X11_yn_function,
131 | X11_getlin,
132 | X11_get_ext_cmd,
133 | X11_number_pad,
134 | X11_delay_output,
135 | #ifdef CHANGE_COLOR /* only a Mac option currently */
136 | donull,
137 | donull,
138 | #endif
139 | /* other defs that really should go away (they're tty specific) */
140 | X11_start_screen,
141 | X11_end_screen,
142 | #ifdef GRAPHIC_TOMBSTONE
143 | X11_outrip,
144 | #else
145 | genl_outrip,
146 | #endif
147 | };
148 |
149 | /*
150 | * Local functions.
151 | */
152 | static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*));
153 | static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*));
154 | static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*));
155 | static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*));
156 | static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*));
157 | static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*));
158 | static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*));
159 | static int FDECL(input_event, (int));
160 | static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *));
161 | static void NDECL(init_standard_windows);
162 |
163 |
164 | /*
165 | * Local variables.
166 | */
167 | static boolean x_inited = FALSE; /* TRUE if window system is set up. */
168 | static winid message_win = WIN_ERR, /* These are the winids of the */
169 | map_win = WIN_ERR, /* message, map, and status */
170 | status_win = WIN_ERR; /* windows, when they are created */
171 | /* in init_windows(). */
172 | static Pixmap icon_pixmap = None; /* Pixmap for icon. */
173 |
174 | /*
175 | * Find the window structure that corresponds to the given widget. Note
176 | * that this is not the popup widget, nor the viewport, but the child.
177 | */
178 | struct xwindow *
179 | find_widget(w)
180 | Widget w;
181 | {
182 | int windex;
183 | struct xwindow *wp;
184 |
185 | /* Search to find the corresponding window. Look at the main widget, */
186 | /* popup, the parent of the main widget, then parent of the widget. */
187 | for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
188 | if (wp->type != NHW_NONE &&
189 | (wp->w == w || wp->popup == w || (wp->w && (XtParent(wp->w)) == w)
190 | || (wp->popup == XtParent(w))))
191 | break;
192 |
193 | if (windex == MAX_WINDOWS) panic("find_widget: can't match widget");
194 | return wp;
195 | }
196 |
197 | /*
198 | * Find a free window slot for use.
199 | */
200 | static winid
201 | find_free_window()
202 | {
203 | int windex;
204 | struct xwindow *wp;
205 |
206 | for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++)
207 | if (wp->type == NHW_NONE) break;
208 |
209 | if (windex == MAX_WINDOWS)
210 | panic("find_free_window: no free windows!");
211 | return (winid) windex;
212 | }
213 |
214 | /*
215 | * Color conversion. The default X11 color converters don't try very
216 | * hard to find matching colors in PseudoColor visuals. If they can't
217 | * allocate the exact color, they puke and give you something stupid.
218 | * This is an attempt to find some close readonly cell and use it.
219 | */
220 | XtConvertArgRec const nhcolorConvertArgs[] = {
221 | {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen),
222 | sizeof(Screen *)},
223 | {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap),
224 | sizeof(Colormap)}
225 | };
226 |
227 | #define done(type, value) \
228 | { \
229 | if (toVal->addr != 0) { \
230 | if (toVal->size < sizeof(type)) { \
231 | toVal->size = sizeof(type); \
232 | return False; \
233 | } \
234 | *(type*)(toVal->addr) = (value); \
235 | } \
236 | else { \
237 | static type static_val; \
238 | static_val = (value); \
239 | toVal->addr = (genericptr_t)&static_val; \
240 | } \
241 | toVal->size = sizeof(type); \
242 | return True; \
243 | }
244 |
245 | /* decl.h declares these, but it screws up structure references -dlc */
246 | #undef red
247 | #undef green
248 | #undef blue
249 |
250 | /*
251 | * Find a color that approximates the color named in "str". The "str" color
252 | * may be a color name ("red") or number ("#7f0000"). If str == NULL, then
253 | * "color" is assumed to contain the RGB color wanted.
254 | * The approximate color found is returned in color as well.
255 | * Return True if something close was found.
256 | */
257 | Boolean
258 | nhApproxColor(screen, colormap, str, color)
259 | Screen *screen; /* screen to use */
260 | Colormap colormap; /* the colormap to use */
261 | char *str; /* color name */
262 | XColor *color; /* the X color structure; changed only if successful */
263 | {
264 | int ncells;
265 | long cdiff = 16777216; /* 2^24; hopefully our map is smaller */
266 | XColor tmp;
267 | static XColor *table = 0;
268 | register i, j;
269 | register long tdiff;
270 |
271 | /* if the screen doesn't have a big colormap, don't waste our time */
272 | /* or if it's huge, and _some_ match should have been possible */
273 | if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
274 | return False;
275 |
276 | if (str != (char *)0) {
277 | if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
278 | return False;
279 | } else {
280 | tmp = *color;
281 | tmp.flags = 7; /* force to use all 3 of RGB */
282 | }
283 |
284 | if (!table) {
285 | table = (XColor *) XtCalloc(ncells, sizeof(XColor));
286 | for(i=0; i<ncells; i++)
287 | table[i].pixel = i;
288 | XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
289 | }
290 |
291 | /* go thru cells and look for the one with smallest diff */
292 | /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
293 | /* a more knowledgeable color person might improve this -dlc */
294 | try_again:
295 | for(i=0; i<ncells; i++) {
296 | if(table[i].flags == tmp.flags) {
297 | j = (int)table[i].red - (int)tmp.red;
298 | if(j < 0) j = -j;
299 | tdiff = j;
300 | j = (int)table[i].green - (int)tmp.green;
301 | if(j < 0) j = -j;
302 | tdiff += j;
303 | j = (int)table[i].blue - (int)tmp.blue;
304 | if(j < 0) j = -j;
305 | tdiff += j;
306 | if(tdiff < cdiff) {
307 | cdiff = tdiff;
308 | tmp.pixel = i; /* table[i].pixel == i */
309 | }
310 | }
311 | }
312 |
313 | if(cdiff == 16777216) return False; /* nothing found?! */
314 |
315 | /*
316 | * Found something. Return it and mark this color as used to avoid
317 | * reuse. Reuse causes major contrast problems :-)
318 | */
319 | *color = table[tmp.pixel];
320 | table[tmp.pixel].flags = 0;
321 | /* try to alloc the color, so no one else can change it */
322 | if(!XAllocColor(DisplayOfScreen(screen), colormap, color)) {
323 | cdiff = 16777216;
324 | goto try_again;
325 | }
326 | return True;
327 | }
328 |
329 | Boolean
330 | nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
331 | Display* dpy;
332 | XrmValuePtr args;
333 | Cardinal *num_args;
334 | XrmValuePtr fromVal;
335 | XrmValuePtr toVal;
336 | XtPointer *closure_ret;
337 | {
338 | String str = (String)fromVal->addr;
339 | XColor screenColor;
340 | XColor exactColor;
341 | Screen *screen;
342 | XtAppContext app = XtDisplayToApplicationContext(dpy);
343 | Colormap colormap;
344 | Status status;
345 | String params[1];
346 | Cardinal num_params=1;
347 |
348 | if (*num_args != 2) {
349 | XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel",
350 | "XtToolkitError",
351 | "String to pixel conversion needs screen and colormap arguments",
352 | (String *)0, (Cardinal *)0);
353 | return False;
354 | }
355 |
356 | screen = *((Screen **) args[0].addr);
357 | colormap = *((Colormap *) args[1].addr);
358 |
359 | /* If Xt colors, use the Xt routine and hope for the best */
360 | #if (XtSpecificationRelease >= 5)
361 | if ((strcmpi(str, XtDefaultBackground) == 0) ||
362 | (strcmpi(str, XtDefaultForeground) == 0)) {
363 | return
364 | XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret);
365 | }
366 | #else
367 | if (strcmpi(str, XtDefaultBackground) == 0) {
368 | *closure_ret = (char*)False;
369 | done(Pixel, WhitePixelOfScreen(screen));
370 | }
371 | if (strcmpi(str, XtDefaultForeground) == 0) {
372 | *closure_ret = (char*)False;
373 | done(Pixel, BlackPixelOfScreen(screen));
374 | }
375 | #endif
376 |
377 | status = XAllocNamedColor(DisplayOfScreen(screen), colormap,
378 | (char*)str, &screenColor, &exactColor);
379 | if (status == 0) {
380 | String msg, type;
381 |
382 | /* some versions of XAllocNamedColor don't allow #xxyyzz names */
383 | if (str[0] == '#' &&
384 | XParseColor(DisplayOfScreen(screen), colormap, str, &exactColor) &&
385 | XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) {
386 | *closure_ret = (char*)True;
387 | done(Pixel, exactColor.pixel);
388 | }
389 |
390 | params[0] = str;
391 | /* Server returns a specific error code but Xlib discards it. Ugh */
392 | if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str,
393 | &exactColor, &screenColor)) {
394 | /* try to find another color that will do */
395 | if (nhApproxColor(screen, colormap, (char*) str, &screenColor)) {
396 | *closure_ret = (char*)True;
397 | done(Pixel, screenColor.pixel);
398 | }
399 | type = "noColormap";
400 | msg = "Cannot allocate colormap entry for \"%s\"";
401 | }
402 | else {
403 | /* some versions of XLookupColor also don't allow #xxyyzz names */
404 | if(str[0] == '#' &&
405 | (nhApproxColor(screen, colormap, (char*) str, &screenColor))) {
406 | *closure_ret = (char*)True;
407 | done(Pixel, screenColor.pixel);
408 | }
409 | type = "badValue";
410 | msg = "Color name \"%s\" is not defined";
411 | }
412 |
413 | XtAppWarningMsg(app, type, "cvtStringToPixel",
414 | "XtToolkitError", msg, params, &num_params);
415 | *closure_ret = False;
416 | return False;
417 | } else {
418 | *closure_ret = (char*)True;
419 | done(Pixel, screenColor.pixel);
420 | }
421 | }
422 |
423 | /* ARGSUSED */
424 | static void
425 | nhFreePixel(app, toVal, closure, args, num_args)
426 | XtAppContext app;
427 | XrmValuePtr toVal;
428 | XtPointer closure;
429 | XrmValuePtr args;
430 | Cardinal *num_args;
431 | {
432 | Screen *screen;
433 | Colormap colormap;
434 |
435 | if (*num_args != 2) {
436 | XtAppWarningMsg(app, "wrongParameters",
437 | "freePixel", "XtToolkitError",
438 | "Freeing a pixel requires screen and colormap arguments",
439 | (String *)0, (Cardinal *)0);
440 | return;
441 | }
442 |
443 | screen = *((Screen **) args[0].addr);
444 | colormap = *((Colormap *) args[1].addr);
445 |
446 | if (closure) {
447 | XFreeColors( DisplayOfScreen(screen), colormap,
448 | (unsigned long*)toVal->addr, 1, (unsigned long)0
449 | );
450 | }
451 | }
452 |
453 | /* Global Functions ======================================================== */
454 | void
455 | X11_raw_print(str)
456 | const char *str;
457 | {
458 | (void) puts(str);
459 | }
460 |
461 | void
462 | X11_raw_print_bold(str)
463 | const char *str;
464 | {
465 | (void) puts(str);
466 | }
467 |
468 | void
469 | X11_curs(window, x, y)
470 | winid window;
471 | int x, y;
472 | {
473 | check_winid(window);
474 |
475 | if (x < 0 || x >= COLNO) {
476 | impossible("curs: bad x value [%d]", x);
477 | x = 0;
478 | }
479 | if (y < 0 || y >= ROWNO) {
480 | impossible("curs: bad y value [%d]", y);
481 | y = 0;
482 | }
483 |
484 | window_list[window].cursx = x;
485 | window_list[window].cursy = y;
486 | }
487 |
488 | void
489 | X11_putstr(window, attr, str)
490 | winid window;
491 | int attr;
492 | const char *str;
493 | {
494 | winid new_win;
495 | struct xwindow *wp;
496 |
497 | check_winid(window);
498 | wp = &window_list[window];
499 |
500 | switch (wp->type) {
501 | case NHW_MESSAGE:
502 | (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */
503 | toplines[TBUFSZ - 1] = 0;
504 | append_message(wp, str);
505 | break;
506 | case NHW_STATUS:
507 | adjust_status(wp, str);
508 | break;
509 | case NHW_MAP:
510 | impossible("putstr: called on map window \"%s\"", str);
511 | break;
512 | case NHW_MENU:
513 | if (wp->menu_information->is_menu) {
514 | impossible(
515 | "putstr: called on a menu window, \"%s\" discarded",
516 | str);
517 | break;
518 | }
519 | /*
520 | * Change this menu window into a text window by creating a
521 | * new text window, then copying it to this winid.
522 | */
523 | new_win = X11_create_nhwindow(NHW_TEXT);
524 | X11_destroy_nhwindow(window);
525 | *wp = window_list[new_win];
526 | window_list[new_win].type = NHW_NONE; /* allow re-use */
527 | /* fall though to add text */
528 | case NHW_TEXT:
529 | add_to_text_window(wp, attr, str);
530 | break;
531 | default:
532 | impossible("putstr: unknown window type [%d] \"%s\"",
533 | wp->type, str);
534 | }
535 | }
536 |
537 | /* We do event processing as a callback, so this is a null routine. */
538 | void X11_get_nh_event() { return; }
539 |
540 | int
541 | X11_nhgetch()
542 | {
543 | return input_event(EXIT_ON_KEY_PRESS);
544 | }
545 |
546 |
547 | int
548 | X11_nh_poskey(x, y, mod)
549 | int *x, *y, *mod;
550 | {
551 | int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
552 |
553 | if (val == 0) { /* user clicked on a map window */
554 | *x = click_x;
555 | *y = click_y;
556 | *mod = click_button;
557 | }
558 | return val;
559 | }
560 |
561 |
562 | winid
563 | X11_create_nhwindow(type)
564 | int type;
565 | {
566 | winid window;
567 | struct xwindow *wp;
568 |
569 | if (!x_inited)
570 | panic("create_nhwindow: windows not initialized");
571 |
572 | /*
573 | * We have already created the standard message, map, and status
574 | * windows in the window init routine. The first window of that
575 | * type to be created becomes the standard.
576 | *
577 | * A better way to do this would be to say that init_nhwindows()
578 | * has already defined these three windows.
579 | */
580 | if (type == NHW_MAP && map_win != WIN_ERR) {
581 | window = map_win;
582 | map_win = WIN_ERR;
583 | return window;
584 | }
585 | if (type == NHW_MESSAGE && message_win != WIN_ERR) {
586 | window = message_win;
587 | message_win = WIN_ERR;
588 | return window;
589 | }
590 | if (type == NHW_STATUS && status_win != WIN_ERR) {
591 | window = status_win;
592 | status_win = WIN_ERR;
593 | return window;
594 | }
595 |
596 | window = find_free_window();
597 | wp = &window_list[window];
598 |
599 | /* The create routines will set type, popup, w, and Win_info. */
600 | wp->prevx = wp->prevy = wp->cursx = wp->cursy =
601 | wp->pixel_width = wp->pixel_height = 0;
602 | wp->keep_window = FALSE;
603 |
604 | switch (type) {
605 | case NHW_MAP:
606 | create_map_window(wp, TRUE, (Widget) 0);
607 | break;
608 | case NHW_MESSAGE:
609 | create_message_window(wp, TRUE, (Widget) 0);
610 | break;
611 | case NHW_STATUS:
612 | create_status_window(wp, TRUE, (Widget) 0);
613 | break;
614 | case NHW_MENU:
615 | create_menu_window(wp);
616 | break;
617 | case NHW_TEXT:
618 | create_text_window(wp);
619 | break;
620 | default:
621 | panic("create_nhwindow: unknown type [%d]\n", type);
622 | break;
623 | }
624 | return window;
625 | }
626 |
627 | void
628 | X11_clear_nhwindow(window)
629 | winid window;
630 | {
631 | struct xwindow *wp;
632 |
633 | check_winid(window);
634 | wp = &window_list[window];
635 |
636 | switch (wp->type) {
637 | case NHW_MAP: clear_map_window(wp); break;
638 | case NHW_TEXT: clear_text_window(wp); break;
639 | case NHW_STATUS:
640 | case NHW_MENU:
641 | case NHW_MESSAGE:
642 | /* do nothing for these window types */
643 | break;
644 | default:
645 | panic("clear_nhwindow: unknown window type [%d]\n", wp->type);
646 | break;
647 | }
648 | }
649 |
650 | void
651 | X11_display_nhwindow(window, blocking)
652 | winid window;
653 | boolean blocking;
654 | {
655 | struct xwindow *wp;
656 |
657 | check_winid(window);
658 | wp = &window_list[window];
659 |
660 | switch (wp->type) {
661 | case NHW_MAP:
662 | if (wp->popup)
663 | nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
664 | display_map_window(wp); /* flush map */
665 | /*
666 | * We need to flush the message window here due to the way the tty
667 | * port is set up. To flush a window, you need to call this
668 | * routine. However, the tty port _pauses_ with a --more-- if we
669 | * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call
670 | * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
671 | * get a --more-- after every line.
672 | *
673 | * Perhaps the window document should mention that when the map
674 | * is flushed, everything on the three main windows should be
675 | * flushed. Note: we don't need to flush the status window
676 | * because we don't buffer changes.
677 | */
678 | if (WIN_MESSAGE != WIN_ERR)
679 | display_message_window(&window_list[WIN_MESSAGE]);
680 | if (blocking)
681 | (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
682 | break;
683 | case NHW_MESSAGE:
684 | if (wp->popup)
685 | nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
686 | display_message_window(wp); /* flush messages */
687 | break;
688 | case NHW_STATUS:
689 | if (wp->popup)
690 | nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
691 | break; /* no flushing necessary */
692 | case NHW_MENU: {
693 | int n;
694 | menu_item *selected;
695 |
696 | /* pop up menu */
697 | n = X11_select_menu(window, PICK_NONE, &selected);
698 | if (n) {
699 | impossible("perminvent: %d selected??", n);
700 | free((genericptr_t)selected);
701 | }
702 | break;
703 | }
704 | case NHW_TEXT:
705 | display_text_window(wp, blocking); /* pop up text window */
706 | break;
707 | default:
708 | panic("display_nhwindow: unknown window type [%d]\n", wp->type);
709 | break;
710 | }
711 | }
712 |
713 | void
714 | X11_destroy_nhwindow(window)
715 | winid window;
716 | {
717 | struct xwindow *wp;
718 |
719 | check_winid(window);
720 | wp = &window_list[window];
721 | /*
722 | * "Zap" known windows, but don't destroy them. We need to keep the
723 | * toplevel widget popped up so that later windows (e.g. tombstone)
724 | * are visible on DECWindow systems. This is due to the virtual
725 | * roots that the DECWindow wm creates.
726 | */
727 | if (window == WIN_MESSAGE) {
728 | wp->keep_window = TRUE;
729 | WIN_MESSAGE = WIN_ERR;
730 | iflags.window_inited = 0;
731 | } else if (window == WIN_MAP) {
732 | wp->keep_window = TRUE;
733 | WIN_MAP = WIN_ERR;
734 | } else if (window == WIN_STATUS) {
735 | wp->keep_window = TRUE;
736 | WIN_STATUS = WIN_ERR;
737 | } else if (window == WIN_INVEN) {
738 | /* don't need to keep this one */
739 | WIN_INVEN = WIN_ERR;
740 | }
741 |
742 | switch (wp->type) {
743 | case NHW_MAP:
744 | destroy_map_window(wp);
745 | break;
746 | case NHW_MENU:
747 | destroy_menu_window(wp);
748 | break;
749 | case NHW_TEXT:
750 | destroy_text_window(wp);
751 | break;
752 | case NHW_STATUS:
753 | destroy_status_window(wp);
754 | break;
755 | case NHW_MESSAGE:
756 | destroy_message_window(wp);
757 | break;
758 | default:
759 | panic("destroy_nhwindow: unknown window type [%d]", wp->type);
760 | break;
761 | }
762 | }
763 |
764 | void
765 | X11_update_inventory()
766 | {
767 | if (x_inited && window_list[WIN_INVEN].menu_information->is_up) {
768 | updated_inventory = 1; /* hack to avoid mapping&raising window */
769 | (void) display_inventory((char *)0, FALSE);
770 | updated_inventory = 0;
771 | }
772 | }
773 |
774 | /* The current implementation has all of the saved lines on the screen. */
775 | int X11_doprev_message() { return 0; }
776 |
777 | void
778 | X11_nhbell()
779 | {
780 | /* We can't use XBell until toplevel has been initialized. */
781 | if (x_inited)
782 | XBell(XtDisplay(toplevel), 0);
783 | /* else print ^G ?? */
784 | }
785 |
786 | void X11_mark_synch()
787 | {
788 | if (x_inited) {
789 | /*
790 | * The window document is unclear about the status of text
791 | * that has been pline()d but not displayed w/display_nhwindow().
792 | * Both the main and tty code assume that a pline() followed
793 | * by mark_synch() results in the text being seen, even if
794 | * display_nhwindow() wasn't called. Duplicate this behavior.
795 | */
796 | if (WIN_MESSAGE != WIN_ERR)
797 | display_message_window(&window_list[WIN_MESSAGE]);
798 | XSync(XtDisplay(toplevel), False);
799 | }
800 | }
801 |
802 | void X11_wait_synch() { if (x_inited) XFlush(XtDisplay(toplevel)); }
803 |
804 |
805 | /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
806 | void X11_resume_nhwindows() { return; }
807 |
808 | /* ARGSUSED */
809 | void X11_suspend_nhwindows(str) const char *str; { return; }
810 |
811 | /* Under X, we don't need to initialize the number pad. */
812 | /* ARGSUSED */
813 | void X11_number_pad(state) int state; { return; } /* called from options.c */
814 |
815 |
816 | void X11_start_screen() { return; } /* called from setftty() in unixtty.c */
817 | void X11_end_screen() { return; } /* called from settty() in unixtty.c */
818 |
819 | #ifdef GRAPHIC_TOMBSTONE
820 | void X11_outrip(window, how)
821 | winid window;
822 | int how;
823 | {
824 | struct xwindow *wp;
825 |
826 | check_winid(window);
827 | wp = &window_list[window];
828 |
829 | if (wp->type == NHW_TEXT) {
830 | wp->text_information->is_rip = TRUE;
831 | } else {
832 | panic("ripout on non-text window (window type [%d])\n", wp->type);
833 | }
834 |
835 | calculate_rip_text(how);
836 | }
837 | #endif
838 |
839 | /* init and exit nhwindows ------------------------------------------------- */
840 |
841 | XtAppContext app_context; /* context of application */
842 | Widget toplevel = (Widget) 0; /* toplevel widget */
843 | Atom wm_delete_window; /* pop down windows */
844 |
845 | static XtActionsRec actions[] = {
846 | {"dismiss_file", dismiss_file}, /* action for file viewing widget */
847 | {"delete_file", delete_file}, /* action for file delete-window */
848 | {"dismiss_text", dismiss_text}, /* button action for text widget */
849 | {"delete_text", delete_text}, /* delete action for text widget */
850 | {"key_dismiss_text",key_dismiss_text},/* key action for text widget */
851 | #ifdef GRAPHIC_TOMBSTONE
852 | {"rip_dismiss_text",rip_dismiss_text},/* action for rip in text widget */
853 | #endif
854 | {"menu_key", menu_key}, /* action for menu accelerators */
855 | {"yn_key", yn_key}, /* action for yn accelerators */
856 | {"yn_delete", yn_delete}, /* action for yn delete-window */
857 | {"askname_delete", askname_delete},/* action for askname delete-window */
858 | {"getline_delete", getline_delete},/* action for getline delete-window */
859 | {"menu_delete", menu_delete}, /* action for menu delete-window */
860 | {"ec_key", ec_key}, /* action for extended commands */
861 | {"ec_delete", ec_delete}, /* action for ext-com menu delete */
862 | {"ps_key", ps_key}, /* action for player selection */
863 | {"race_key", race_key}, /* action for race selection */
864 | {"gend_key", gend_key}, /* action for gender selection */
865 | {"algn_key", algn_key}, /* action for alignment selection */
866 | {"X11_hangup", X11_hangup}, /* action for delete of top-level */
867 | {"input", map_input}, /* action for key input */
868 | {"scroll", nh_keyscroll}, /* action for scrolling by keys */
869 | };
870 |
871 | static XtResource resources[] = {
872 | { "slow", "Slow", XtRBoolean, sizeof(Boolean),
873 | XtOffset(AppResources *,slow), XtRString, "False" },
874 | { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean),
875 | XtOffset(AppResources *,autofocus), XtRString, "False" },
876 | { "message_line", "Message_line", XtRBoolean, sizeof(Boolean),
877 | XtOffset(AppResources *,message_line), XtRString, "False" },
878 | { "double_tile_size", "Double_tile_size", XtRBoolean, sizeof(Boolean),
879 | XtOffset(AppResources *,double_tile_size), XtRString, "False" },
880 | { "tile_file", "Tile_file", XtRString, sizeof(String),
881 | XtOffset(AppResources *,tile_file), XtRString, "" },
882 | { "icon", "Icon", XtRString, sizeof(String),
883 | XtOffset(AppResources *,icon), XtRString, "nh72" },
884 | { "message_lines", "Message_lines", XtRInt, sizeof(int),
885 | XtOffset(AppResources *,message_lines), XtRString, "12" },
886 | { "pet_mark_bitmap", "Pet_mark_bitmap", XtRString, sizeof(String),
887 | XtOffset(AppResources *,pet_mark_bitmap), XtRString, "pet_mark.xbm" },
888 | { "pet_mark_color", "Pet_mark_color", XtRPixel, sizeof(XtRPixel),
889 | XtOffset(AppResources *,pet_mark_color), XtRString, "Red" },
890 | #ifdef GRAPHIC_TOMBSTONE
891 | { "tombstone", "Tombstone", XtRString, sizeof(String),
892 | XtOffset(AppResources *,tombstone), XtRString, "rip.xpm" },
893 | { "tombtext_x", "Tombtext_x", XtRInt, sizeof(int),
894 | XtOffset(AppResources *,tombtext_x), XtRString, "155" },
895 | { "tombtext_y", "Tombtext_y", XtRInt, sizeof(int),
896 | XtOffset(AppResources *,tombtext_y), XtRString, "78" },
897 | { "tombtext_dx", "Tombtext_dx", XtRInt, sizeof(int),
898 | XtOffset(AppResources *,tombtext_dx), XtRString, "0" },
899 | { "tombtext_dy", "Tombtext_dy", XtRInt, sizeof(int),
900 | XtOffset(AppResources *,tombtext_dy), XtRString, "13" },
901 | #endif
902 | };
903 |
904 | void
905 | X11_init_nhwindows(argcp,argv)
906 | int* argcp;
907 | char** argv;
908 | {
909 | static const char *banner_text[] = {
910 | COPYRIGHT_BANNER_A,
911 | COPYRIGHT_BANNER_B,
912 | COPYRIGHT_BANNER_C,
913 | "",
914 | "",
915 | 0
916 | };
917 | register const char **pp;
918 | int i;
919 | Cardinal num_args;
920 | Arg args[4];
921 | uid_t savuid;
922 |
923 | /* Init windows to nothing. */
924 | for (i = 0; i < MAX_WINDOWS; i++)
925 | window_list[i].type = NHW_NONE;
926 |
927 | /*
928 | * setuid hack: make sure that if nethack is setuid, to use real uid
929 | * when opening X11 connections, in case the user is using xauth, since
930 | * the "games" or whatever user probably doesn't have permission to open
931 | * a window on the user's display. This code is harmless if the binary
932 | * is not installed setuid. See include/system.h on compilation failures.
933 | */
934 | savuid = geteuid();
935 | (void) seteuid(getuid());
936 |
937 | XSetIOErrorHandler((XIOErrorHandler) hangup);
938 |
939 | num_args = 0;
940 | XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
941 | toplevel = XtAppInitialize(
942 | &app_context,
943 | "NetHack", /* application class */
944 | (XrmOptionDescList)0, 0, /* options list */
945 | argcp, (String *)argv, /* command line args */
946 | (String *)0, /* fallback resources */
947 | (ArgList)args, num_args);
948 | XtOverrideTranslations(toplevel,
949 | XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
950 |
951 | /* We don't need to realize the top level widget. */
952 |
953 | #ifdef TEXTCOLOR
954 | /* add new color converter to deal with overused colormaps */
955 | XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
956 | (XtConvertArgList)nhcolorConvertArgs,
957 | XtNumber(nhcolorConvertArgs),
958 | XtCacheByDisplay, nhFreePixel);
959 | #endif /* TEXTCOLOR */
960 |
961 | /* Register the actions mentioned in "actions". */
962 | XtAppAddActions(app_context, actions, XtNumber(actions));
963 |
964 | /* Get application-wide resources */
965 | XtGetApplicationResources(toplevel, (XtPointer)&appResources,
966 | resources, XtNumber(resources),
967 | (ArgList)0, ZERO);
968 |
969 | /* Initialize other things. */
970 | init_standard_windows();
971 |
972 | /* Give the window manager an icon to use; toplevel must be realized. */
973 | if (appResources.icon && *appResources.icon) {
974 | struct icon_info *ip;
975 |
976 | for (ip = icon_data; ip->name; ip++)
977 | if (!strcmp(appResources.icon, ip->name)) {
978 | icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel),
979 | XtWindow(toplevel),
980 | (genericptr_t)ip->bits, ip->width, ip->height);
981 | if (icon_pixmap != None) {
982 | XWMHints hints;
983 |
984 | (void) memset((genericptr_t)&hints, 0, sizeof(XWMHints));
985 | hints.flags = IconPixmapHint;
986 | hints.icon_pixmap = icon_pixmap;
987 | XSetWMHints(XtDisplay(toplevel),
988 | XtWindow(toplevel), &hints);
989 | }
990 | break;
991 | }
992 | }
993 |
994 | /* end of setuid hack: reset uid back to the "games" uid */
995 | (void) seteuid(savuid);
996 |
997 | x_inited = TRUE; /* X is now initialized */
998 |
999 | /* Display the startup banner in the message window. */
1000 | for (pp = banner_text; *pp; pp++)
1001 | X11_putstr(WIN_MESSAGE, 0, *pp);
1002 | }
1003 |
1004 | /*
1005 | * All done.
1006 | */
1007 | /* ARGSUSED */
1008 | void X11_exit_nhwindows(dummy)
1009 | const char *dummy;
1010 | {
1011 | extern Pixmap tile_pixmap; /* from winmap.c */
1012 |
1013 | /* explicitly free the icon and tile pixmaps */
1014 | if (icon_pixmap != None) {
1015 | XFreePixmap(XtDisplay(toplevel), icon_pixmap);
1016 | icon_pixmap = None;
1017 | }
1018 | if (tile_pixmap != None) {
1019 | XFreePixmap(XtDisplay(toplevel), tile_pixmap);
1020 | tile_pixmap = None;
1021 | }
1022 | if (WIN_INVEN != WIN_ERR)
1023 | X11_destroy_nhwindow(WIN_INVEN);
1024 | if (WIN_STATUS != WIN_ERR)
1025 | X11_destroy_nhwindow(WIN_STATUS);
1026 | if (WIN_MAP != WIN_ERR)
1027 | X11_destroy_nhwindow(WIN_MAP);
1028 | if (WIN_MESSAGE != WIN_ERR)
1029 | X11_destroy_nhwindow(WIN_MESSAGE);
1030 | }
1031 |
1032 |
1033 | /* delay_output ------------------------------------------------------------ */
1034 |
1035 | /*
1036 | * Timeout callback for delay_output(). Send a fake message to the map
1037 | * window.
1038 | */
1039 | /* ARGSUSED */
1040 | static void
1041 | d_timeout(client_data, id)
1042 | XtPointer client_data;
1043 | XtIntervalId *id;
1044 | {
1045 | XEvent event;
1046 | XClientMessageEvent *mesg;
1047 |
1048 | /* Set up a fake message to the event handler. */
1049 | mesg = (XClientMessageEvent *) &event;
1050 | mesg->type = ClientMessage;
1051 | mesg->message_type = XA_STRING;
1052 | mesg->format = 8;
1053 | XSendEvent(XtDisplay(window_list[WIN_MAP].w),
1054 | XtWindow(window_list[WIN_MAP].w),
1055 | False,
1056 | NoEventMask,
1057 | (XEvent*) mesg);
1058 | }
1059 |
1060 | /*
1061 | * Delay for 50ms. This is not implemented asynch. Maybe later.
1062 | * Start the timeout, then wait in the event loop. The timeout
1063 | * function will send an event to the map window which will be waiting
1064 | * for a sent event.
1065 | */
1066 | void
1067 | X11_delay_output()
1068 | {
1069 | if (!x_inited) return;
1070 |
1071 | (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
1072 |
1073 | /* The timeout function will enable the event loop exit. */
1074 | (void) x_event(EXIT_ON_SENT_EVENT);
1075 | }
1076 |
1077 | /* X11_hangup -------------------------------------------------------------- */
1078 | /* ARGSUSED */
1079 | static void
1080 | X11_hangup(w, event, params, num_params)
1081 | Widget w;
1082 | XEvent *event;
1083 | String *params;
1084 | Cardinal *num_params;
1085 | {
1086 | hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */
1087 | }
1088 |
1089 | /* askname ----------------------------------------------------------------- */
1090 | /* ARGSUSED */
1091 | static void
1092 | askname_delete(w, event, params, num_params)
1093 | Widget w;
1094 | XEvent *event;
1095 | String *params;
1096 | Cardinal *num_params;
1097 | {
1098 | nh_XtPopdown(w);
1099 | (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */
1100 | exit_x_event = TRUE;
1101 | }
1102 |
1103 | /* Callback for askname dialog widget. */
1104 | /* ARGSUSED */
1105 | static void
1106 | askname_done(w, client_data, call_data)
1107 | Widget w;
1108 | XtPointer client_data;
1109 | XtPointer call_data;
1110 | {
1111 | int len;
1112 | char *s;
1113 | Widget dialog = (Widget) client_data;
1114 |
1115 | s = (char *) GetDialogResponse(dialog);
1116 |
1117 | len = strlen(s);
1118 | if (len == 0) {
1119 | X11_nhbell();
1120 | return;
1121 | }
1122 |
1123 | /* Truncate name if necessary */
1124 | if (len >= sizeof(plname)-1)
1125 | len = sizeof(plname)-1;
1126 |
1127 | (void) strncpy(plname, s, len);
1128 | plname[len] = '\0';
1129 | XtFree(s);
1130 |
1131 | nh_XtPopdown(XtParent(dialog));
1132 | exit_x_event = TRUE;
1133 | }
1134 |
1135 | void
1136 | X11_askname()
1137 | {
1138 | Widget popup, dialog;
1139 | Arg args[1];
1140 |
1141 | XtSetArg(args[0], XtNallowShellResize, True);
1142 |
1143 | popup = XtCreatePopupShell("askname", transientShellWidgetClass,
1144 | toplevel, args, ONE);
1145 | XtOverrideTranslations(popup,
1146 | XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
1147 |
1148 | dialog = CreateDialog(popup, "dialog",
1149 | askname_done, (XtCallbackProc) 0);
1150 |
1151 | SetDialogPrompt(dialog, "What is your name?"); /* set prompt */
1152 | SetDialogResponse(dialog, ""); /* set default answer */
1153 |
1154 | XtRealizeWidget(popup);
1155 | positionpopup(popup, TRUE); /* center,bottom */
1156 |
1157 | nh_XtPopup(popup, (int)XtGrabExclusive, dialog);
1158 |
1159 | /* The callback will enable the event loop exit. */
1160 | (void) x_event(EXIT_ON_EXIT);
1161 | }
1162 |
1163 |
1164 | /* getline ----------------------------------------------------------------- */
1165 | /* This uses Tim Theisen's dialog widget set (from GhostView). */
1166 |
1167 | static Widget getline_popup, getline_dialog;
1168 |
1169 | #define CANCEL_STR "\033"
1170 | static char *getline_input;
1171 |
1172 |
1173 | /* Callback for getline dialog widget. */
1174 | /* ARGSUSED */
1175 | static void
1176 | done_button(w, client_data, call_data)
1177 | Widget w;
1178 | XtPointer client_data;
1179 | XtPointer call_data;
1180 | {
1181 | char *s;
1182 | Widget dialog = (Widget) client_data;
1183 |
1184 | s = (char *) GetDialogResponse(dialog);
1185 | Strcpy(getline_input, s);
1186 | XtFree(s);
1187 | nh_XtPopdown(XtParent(dialog));
1188 | exit_x_event = TRUE;
1189 | }
1190 |
1191 | /* ARGSUSED */
1192 | static void
1193 | getline_delete(w, event, params, num_params)
1194 | Widget w;
1195 | XEvent *event;
1196 | String *params;
1197 | Cardinal *num_params;
1198 | {
1199 | Strcpy(getline_input, CANCEL_STR);
1200 | nh_XtPopdown(w);
1201 | exit_x_event = TRUE;
1202 | }
1203 |
1204 | /* Callback for getline dialog widget. */
1205 | /* ARGSUSED */
1206 | static void
1207 | abort_button(w, client_data, call_data)
1208 | Widget w;
1209 | XtPointer client_data;
1210 | XtPointer call_data;
1211 | {
1212 | Widget dialog = (Widget) client_data;
1213 |
1214 | Strcpy(getline_input, CANCEL_STR);
1215 | nh_XtPopdown(XtParent(dialog));
1216 | exit_x_event = TRUE;
1217 | }
1218 |
1219 |
1220 | void
1221 | X11_getlin(question, input)
1222 | const char *question;
1223 | char *input;
1224 | {
1225 | static boolean need_to_init = True;
1226 |
1227 | getline_input = input;
1228 |
1229 | flush_screen(1);
1230 | if (need_to_init) {
1231 | Arg args[1];
1232 |
1233 | need_to_init = False;
1234 |
1235 | XtSetArg(args[0], XtNallowShellResize, True);
1236 |
1237 | getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass,
1238 | toplevel, args, ONE);
1239 | XtOverrideTranslations(getline_popup,
1240 | XtParseTranslationTable("<Message>WM_PROTOCOLS: getline_delete()"));
1241 |
1242 | getline_dialog = CreateDialog(getline_popup, "dialog",
1243 | done_button, abort_button);
1244 |
1245 | XtRealizeWidget(getline_popup);
1246 | XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
1247 | &wm_delete_window, 1);
1248 | }
1249 | SetDialogPrompt(getline_dialog, (String)question); /* set prompt */
1250 | SetDialogResponse(getline_dialog, ""); /* set default answer */
1251 | positionpopup(getline_popup, TRUE); /* center,bottom */
1252 |
1253 | nh_XtPopup(getline_popup, (int)XtGrabExclusive, getline_dialog);
1254 |
1255 | /* The callback will enable the event loop exit. */
1256 | (void) x_event(EXIT_ON_EXIT);
1257 | }
1258 |
1259 |
1260 | /* Display file ------------------------------------------------------------ */
1261 | static const char display_translations[] =
1262 | "#override\n\
1263 | <Key>q: dismiss_file()\n\
1264 | <Key>Escape: dismiss_file()\n\
1265 | <BtnDown>: dismiss_file()";
1266 |
1267 |
1268 | /* WM_DELETE_WINDOW callback for file dismissal. */
1269 | /*ARGSUSED*/
1270 | static void
1271 | delete_file(w, event, params, num_params)
1272 | Widget w;
1273 | XEvent *event;
1274 | String *params;
1275 | Cardinal *num_params;
1276 | {
1277 | nh_XtPopdown(w);
1278 | XtDestroyWidget(w);
1279 | }
1280 |
1281 | /* Callback for file dismissal. */
1282 | /*ARGSUSED*/
1283 | static void
1284 | dismiss_file(w, event, params, num_params)
1285 | Widget w;
1286 | XEvent *event;
1287 | String *params;
1288 | Cardinal *num_params;
1289 | {
1290 | Widget popup = XtParent(w);
1291 | nh_XtPopdown(popup);
1292 | XtDestroyWidget(popup);
1293 | }
1294 |
1295 | void
1296 | X11_display_file(str, complain)
1297 | const char *str;
1298 | boolean complain;
1299 | {
1300 | dlb *fp;
1301 | Arg args[12];
1302 | Cardinal num_args;
1303 | Widget popup, dispfile;
1304 | Position top_margin, bottom_margin, left_margin, right_margin;
1305 | XFontStruct *fs;
1306 | int new_width, new_height;
1307 | #define LLEN 128
1308 | char line[LLEN];
1309 | int num_lines;
1310 | char *textlines;
1311 | int charcount;
1312 |
1313 | /* Use the port-independent file opener to see if the file exists. */
1314 | fp = dlb_fopen(str, RDTMODE);
1315 |
1316 | if (!fp) {
1317 | if(complain) pline("Cannot open %s. Sorry.", str);
1318 |
1319 | return; /* it doesn't exist, ignore */
1320 | }
1321 |
1322 | /*
1323 | * Count the number of lines and characters in the file.
1324 | */
1325 | num_lines = 0;
1326 | charcount = 1;
1327 | while (dlb_fgets(line, LLEN, fp)) {
1328 | num_lines++;
1329 | charcount += strlen(line);
1330 | }
1331 |
1332 | (void) dlb_fclose(fp);
1333 |
1334 | /* Ignore empty files */
1335 | if (num_lines == 0) return;
1336 |
1337 | /* If over the max window size, truncate the window size to the max */
1338 | if (num_lines >= DISPLAY_FILE_SIZE)
1339 | num_lines = DISPLAY_FILE_SIZE;
1340 |
1341 | /*
1342 | * Re-open the file and read the data into a buffer. Cannot use
1343 | * the XawAsciiFile type of widget, because that is not DLB-aware.
1344 | */
1345 | textlines = (char *) alloc((unsigned int) charcount);
1346 | textlines[0] = '\0';
1347 |
1348 | fp = dlb_fopen(str, RDTMODE);
1349 |
1350 | while (dlb_fgets(line, LLEN, fp)) {
1351 | (void) strcat(textlines, line);
1352 | }
1353 |
1354 | (void) dlb_fclose(fp);
1355 |
1356 | num_args = 0;
1357 | XtSetArg(args[num_args], XtNtitle, str); num_args++;
1358 |
1359 | popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
1360 | toplevel, args, num_args);
1361 | XtOverrideTranslations(popup,
1362 | XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
1363 |
1364 | num_args = 0;
1365 | XtSetArg(args[num_args], XtNscrollHorizontal,
1366 | XawtextScrollWhenNeeded); num_args++;
1367 | XtSetArg(args[num_args], XtNscrollVertical,
1368 | XawtextScrollWhenNeeded); num_args++;
1369 | XtSetArg(args[num_args], XtNtype, XawAsciiString); num_args++;
1370 | XtSetArg(args[num_args], XtNstring, textlines); num_args++;
1371 | XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++;
1372 | XtSetArg(args[num_args], XtNtranslations,
1373 | XtParseTranslationTable(display_translations)); num_args++;
1374 |
1375 | dispfile = XtCreateManagedWidget(
1376 | "text", /* name */
1377 | asciiTextWidgetClass,
1378 | popup, /* parent widget */
1379 | args, /* set some values */
1380 | num_args); /* number of values to set */
1381 |
1382 | /* Get font and border information. */
1383 | num_args = 0;
1384 | XtSetArg(args[num_args], XtNfont, &fs); num_args++;
1385 | XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++;
1386 | XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
1387 | XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++;
1388 | XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++;
1389 | XtGetValues(dispfile, args, num_args);
1390 |
1391 | /*
1392 | * Font height is ascent + descent.
1393 | *
1394 | * The data files are currently set up assuming an 80 char wide window
1395 | * and a fixed width font. Soo..
1396 | */
1397 | new_height = num_lines * (fs->ascent + fs->descent) +
1398 | top_margin + bottom_margin;
1399 | new_width = 80 * fs->max_bounds.width + left_margin + right_margin;
1400 |
1401 | /* Set the new width and height. */
1402 | num_args = 0;
1403 | XtSetArg(args[num_args], XtNwidth, new_width); num_args++;
1404 | XtSetArg(args[num_args], XtNheight, new_height); num_args++;
1405 | XtSetValues(dispfile, args, num_args);
1406 |
1407 | nh_XtPopup(popup, (int)XtGrabNone, (Widget)0);
1408 | free(textlines);
1409 | }
1410 |
1411 |
1412 | /* yn_function ------------------------------------------------------------- */
1413 | /* (not threaded) */
1414 |
1415 | static const char *yn_quitchars = " \n\r";
1416 | static const char *yn_choices; /* string of acceptable input */
1417 | static char yn_def;
1418 | static char yn_return; /* return value */
1419 | static char yn_esc_map; /* ESC maps to this char. */
1420 | static Widget yn_popup; /* popup for the yn fuction (created once) */
1421 | static Widget yn_label; /* label for yn function (created once) */
1422 | static boolean yn_getting_num; /* TRUE if accepting digits */
1423 | static int yn_ndigits; /* digit count */
1424 | static long yn_val; /* accumulated value */
1425 |
1426 | static const char yn_translations[] =
1427 | "#override\n\
1428 | <Key>: yn_key()";
1429 |
1430 | /*
1431 | * Convert the given key event into a character. If the key maps to
1432 | * more than one character only the first is returned. If there is
1433 | * no conversion (i.e. just the CTRL key hit) a NUL is returned.
1434 | */
1435 | char
1436 | key_event_to_char(key)
1437 | XKeyEvent *key;
1438 | {
1439 | char keystring[MAX_KEY_STRING];
1440 | int nbytes;
1441 | boolean meta = !!(key->state & Mod1Mask);
1442 |
1443 | nbytes = XLookupString(key, keystring, MAX_KEY_STRING,
1444 | (KeySym *)0, (XComposeStatus *)0);
1445 |
1446 | /* Modifier keys return a zero lengh string when pressed. */
1447 | if (nbytes == 0) return '\0';
1448 |
1449 | return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
1450 | }
1451 |
1452 | /*
1453 | * Called when we get a WM_DELETE_WINDOW event on a yn window.
1454 | */
1455 | /* ARGSUSED */
1456 | static void
1457 | yn_delete(w, event, params, num_params)
1458 | Widget w;
1459 | XEvent *event;
1460 | String *params;
1461 | Cardinal *num_params;
1462 | {
1463 | yn_getting_num = FALSE;
1464 | /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */
1465 | yn_return = yn_choices ? yn_esc_map : '\033';
1466 | exit_x_event = TRUE; /* exit our event handler */
1467 | }
1468 |
1469 | /*
1470 | * Called when we get a key press event on a yn window.
1471 | */
1472 | /* ARGSUSED */
1473 | static void
1474 | yn_key(w, event, params, num_params)
1475 | Widget w;
1476 | XEvent *event;
1477 | String *params;
1478 | Cardinal *num_params;
1479 | {
1480 | char ch;
1481 |
1482 | if(appResources.slow && !input_func)
1483 | map_input(w, event, params, num_params);
1484 |
1485 | ch = key_event_to_char((XKeyEvent *) event);
1486 |
1487 | if (ch == '\0') { /* don't accept nul char or modifier event */
1488 | /* no bell */
1489 | return;
1490 | }
1491 |
1492 | if (!yn_choices) { /* accept any input */
1493 | yn_return = ch;
1494 | } else {
1495 | ch = lowc(ch); /* move to lower case */
1496 |
1497 | if (ch == '\033') {
1498 | yn_getting_num = FALSE;
1499 | yn_return = yn_esc_map;
1500 | } else if (index(yn_quitchars, ch)) {
1501 | yn_return = yn_def;
1502 | } else if (index(yn_choices, ch)) {
1503 | if (ch == '#') {
1504 | if (yn_getting_num) { /* don't select again */
1505 | X11_nhbell();
1506 | return;
1507 | }
1508 | yn_getting_num = TRUE;
1509 | yn_ndigits = 0;
1510 | yn_val = 0;
1511 | return; /* wait for more input */
1512 | }
1513 | yn_return = ch;
1514 | if (ch != 'y') yn_getting_num = FALSE;
1515 | } else {
1516 | if (yn_getting_num) {
1517 | if (digit(ch)) {
1518 | yn_ndigits++;
1519 | yn_val = (yn_val * 10) + (long) (ch - '0');
1520 | return; /* wait for more input */
1521 | }
1522 | if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) {
1523 | yn_ndigits--;
1524 | yn_val = yn_val/ 10;
1525 | return; /* wait for more input */
1526 | }
1527 | }
1528 | X11_nhbell(); /* no match */
1529 | return;
1530 | }
1531 |
1532 | if (yn_getting_num) {
1533 | yn_return = '#';
1534 | if (yn_val < 0) yn_val = 0;
1535 | yn_number = yn_val; /* assign global */
1536 | }
1537 | }
1538 | exit_x_event = TRUE; /* exit our event handler */
1539 | }
1540 |
1541 |
1542 | char
1543 | X11_yn_function(ques, choices, def)
1544 | const char *ques;
1545 | const char *choices;
1546 | char def;
1547 | {
1548 | static Boolean need_to_init = True;
1549 | char buf[QBUFSZ];
1550 | Arg args[4];
1551 | Cardinal num_args;
1552 |
1553 | yn_choices = choices; /* set up globals for callback to use */
1554 | yn_def = def;
1555 |
1556 | /*
1557 | * This is sort of a kludge. There are quite a few places in the main
1558 | * nethack code where a pline containing information is followed by a
1559 | * call to yn_function(). There is no flush of the message window
1560 | * (it is implicit in the tty window port), so the line never shows
1561 | * up for us! Solution: do our own flush.
1562 | */
1563 | if (WIN_MESSAGE != WIN_ERR)
1564 | display_message_window(&window_list[WIN_MESSAGE]);
1565 |
1566 | if (choices) {
1567 | char *cb, choicebuf[QBUFSZ];
1568 |
1569 | Strcpy(choicebuf, choices); /* anything beyond <esc> is hidden */
1570 | if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0';
1571 | /* ques [choices] (def) */
1572 | if ((int)(1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= QBUFSZ)
1573 | panic("yn_function: question too long");
1574 | Sprintf(buf, "%s [%s] ", ques, choicebuf);
1575 | if (def) Sprintf(eos(buf), "(%c) ", def);
1576 |
1577 | /* escape maps to 'q' or 'n' or default, in that order */
1578 | yn_esc_map = (index(choices, 'q') ? 'q' :
1579 | (index(choices, 'n') ? 'n' :
1580 | def));
1581 | } else {
1582 | if ((int)(1 + strlen(ques)) >= QBUFSZ)
1583 | panic("yn_function: question too long");
1584 | Strcpy(buf, ques);
1585 | }
1586 |
1587 | if (!appResources.slow && need_to_init) {
1588 | need_to_init = False;
1589 |
1590 | XtSetArg(args[0], XtNallowShellResize, True);
1591 | yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
1592 | toplevel, args, ONE);
1593 | XtOverrideTranslations(yn_popup,
1594 | XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
1595 |
1596 | num_args = 0;
1597 | XtSetArg(args[num_args], XtNtranslations,
1598 | XtParseTranslationTable(yn_translations)); num_args++;
1599 | yn_label = XtCreateManagedWidget("yn_label",
1600 | labelWidgetClass,
1601 | yn_popup,
1602 | args, num_args);
1603 |
1604 | XtRealizeWidget(yn_popup);
1605 | XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
1606 | &wm_delete_window, 1);
1607 | }
1608 |
1609 | if(appResources.slow)
1610 | input_func = yn_key;
1611 |
1612 | num_args = 0;
1613 | XtSetArg(args[num_args], XtNlabel, buf); num_args++;
1614 | XtSetValues(yn_label, args, num_args);
1615 |
1616 | if(!appResources.slow) {
1617 | /*
1618 | * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
1619 | * need to set the label twice to get the size to change.
1620 | */
1621 | num_args = 0;
1622 | XtSetArg(args[num_args], XtNlabel, buf); num_args++;
1623 | XtSetValues(yn_label, args, num_args);
1624 |
1625 | positionpopup(yn_popup, TRUE);
1626 | nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label);
1627 | }
1628 |
1629 | yn_getting_num = FALSE;
1630 | (void) x_event(EXIT_ON_EXIT);
1631 |
1632 | if(appResources.slow) {
1633 | input_func = 0;
1634 | num_args = 0;
1635 | XtSetArg(args[num_args], XtNlabel, " "); num_args++;
1636 | XtSetValues(yn_label, args, num_args);
1637 | } else {
1638 | nh_XtPopdown(yn_popup); /* this removes the event grab */
1639 | }
1640 |
1641 | return yn_return;
1642 | }
1643 |
1644 | /* End global functions ==================================================== */
1645 |
1646 | /*
1647 | * Before we wait for input via nhgetch() and nh_poskey(), we need to
1648 | * do some pre-processing.
1649 | */
1650 | static int
1651 | input_event(exit_condition)
1652 | int exit_condition;
1653 | {
1654 | if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */
1655 | check_turn_events();
1656 | if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */
1657 | check_cursor_visibility(&window_list[WIN_MAP]);
1658 | if (WIN_MESSAGE != WIN_ERR) /* reset pause line */
1659 | set_last_pause(&window_list[WIN_MESSAGE]);
1660 |
1661 | return x_event(exit_condition);
1662 | }
1663 |
1664 |
1665 | /*ARGSUSED*/
1666 | void
1667 | msgkey(w, data, event)
1668 | Widget w;
1669 | XtPointer data;
1670 | XEvent *event;
1671 | {
1672 | Cardinal num = 0;
1673 | map_input(window_list[WIN_MAP].w, event, (String*) 0, &num);
1674 | }
1675 |
1676 | /*ARGSUSED*/
1677 | static void
1678 | win_visible(w, data, event, flag) /* only called for autofocus */
1679 | Widget w;
1680 | XtPointer data; /* client_data not used */
1681 | XEvent *event;
1682 | Boolean *flag; /* continue_to_dispatch flag not used */
1683 | {
1684 | XVisibilityEvent *vis_event = (XVisibilityEvent *)event;
1685 |
1686 | if (vis_event->state != VisibilityFullyObscured) {
1687 | /* one-time operation; cancel ourself */
1688 | XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
1689 | win_visible, (XtPointer) 0);
1690 | /* grab initial input focus */
1691 | XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
1692 | }
1693 | }
1694 |
1695 | /*
1696 | * Set up the playing console. This has three major parts: the
1697 | * message window, the map, and the status window.
1698 | */
1699 | static void
1700 | init_standard_windows()
1701 | {
1702 | Widget form, message_viewport, map_viewport, status;
1703 | Arg args[8];
1704 | Cardinal num_args;
1705 | Dimension message_vp_width, map_vp_width, status_width, max_width;
1706 | int map_vp_hd, status_hd;
1707 | struct xwindow *wp;
1708 |
1709 |
1710 | num_args = 0;
1711 | XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
1712 | form = XtCreateManagedWidget("nethack",
1713 | panedWidgetClass,
1714 | toplevel, args, num_args);
1715 |
1716 | XtAddEventHandler(form, KeyPressMask, False,
1717 | (XtEventHandler) msgkey, (XtPointer) 0);
1718 |
1719 | if (appResources.autofocus)
1720 | XtAddEventHandler(toplevel, VisibilityChangeMask, False,
1721 | win_visible, (XtPointer) 0);
1722 |
1723 | /*
1724 | * Create message window.
1725 | */
1726 | WIN_MESSAGE = message_win = find_free_window();
1727 | wp = &window_list[message_win];
1728 | wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1729 | wp->popup = (Widget) 0;
1730 | create_message_window(wp, FALSE, form);
1731 | message_viewport = XtParent(wp->w);
1732 |
1733 |
1734 | /* Tell the form that contains it that resizes are OK. */
1735 | num_args = 0;
1736 | XtSetArg(args[num_args], XtNresizable, True); num_args++;
1737 | XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
1738 | XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++;
1739 | XtSetValues(message_viewport, args, num_args);
1740 |
1741 | if(appResources.slow) {
1742 | num_args = 0;
1743 | XtSetArg(args[num_args], XtNtranslations,
1744 | XtParseTranslationTable(yn_translations)); num_args++;
1745 | yn_label = XtCreateManagedWidget("yn_label",
1746 | labelWidgetClass,
1747 | form,
1748 | args, num_args);
1749 | num_args = 0;
1750 | XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++;
1751 | XtSetArg(args[num_args], XtNjustify, XtJustifyLeft); num_args++;
1752 | XtSetArg(args[num_args], XtNresizable, True); num_args++;
1753 | XtSetArg(args[num_args], XtNlabel, " "); num_args++;
1754 | XtSetValues(yn_label, args, num_args);
1755 | }
1756 |
1757 | /*
1758 | * Create the map window & viewport and chain the viewport beneath the
1759 | * message_viewport.
1760 | */
1761 | map_win = find_free_window();
1762 | wp = &window_list[map_win];
1763 | wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1764 | wp->popup = (Widget) 0;
1765 | create_map_window(wp, FALSE, form);
1766 | map_viewport = XtParent(wp->w);
1767 |
1768 | /* Chain beneath message_viewport or yn window. */
1769 | num_args = 0;
1770 | if(appResources.slow) {
1771 | XtSetArg(args[num_args], XtNfromVert, yn_label); num_args++;
1772 | } else {
1773 | XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++;
1774 | }
1775 | XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++;
1776 | XtSetValues(map_viewport, args, num_args);
1777 |
1778 | /* Create the status window, with the form as it's parent. */
1779 | status_win = find_free_window();
1780 | wp = &window_list[status_win];
1781 | wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
1782 | wp->popup = (Widget) 0;
1783 | create_status_window(wp, FALSE, form);
1784 | status = wp->w;
1785 |
1786 | /*
1787 | * Chain the status window beneath the viewport. Mark the left and right
1788 | * edges so that they stay a fixed distance from the left edge of the
1789 | * parent, as well as the top and bottom edges so that they stay a fixed
1790 | * distance from the bottom of the parent. We do this so that the status
1791 | * will never expand or contract.
1792 | */
1793 | num_args = 0;
1794 | XtSetArg(args[num_args], XtNfromVert, map_viewport); num_args++;
1795 | XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
1796 | XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
1797 | XtSetArg(args[num_args], XtNtop, XtChainBottom); num_args++;
1798 | XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++;
1799 | XtSetValues(status, args, num_args);
1800 |
1801 |
1802 | /*
1803 | * Realize the popup so that the status widget knows it's size.
1804 | *
1805 | * If we unset MappedWhenManaged then the DECwindow driver doesn't
1806 | * attach the nethack toplevel to the highest virtual root window.
1807 | * So don't do it.
1808 | */
1809 | /* XtSetMappedWhenManaged(toplevel, False); */
1810 | XtRealizeWidget(toplevel);
1811 | wm_delete_window = XInternAtom(XtDisplay(toplevel),
1812 | "WM_DELETE_WINDOW", False);
1813 | XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
1814 | &wm_delete_window, 1);
1815 |
1816 | /*
1817 | * Resize to at most full-screen.
1818 | */
1819 | {
1820 | #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */
1821 |
1822 | int screen_width = WidthOfScreen(XtScreen(wp->w));
1823 | int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE;
1824 | Dimension form_width, form_height;
1825 |
1826 | XtSetArg(args[0], XtNwidth, &form_width);
1827 | XtSetArg(args[1], XtNheight, &form_height);
1828 | XtGetValues(toplevel, args, TWO);
1829 |
1830 | if (form_width > screen_width || form_height > screen_height) {
1831 | XtSetArg(args[0], XtNwidth, min(form_width,screen_width));
1832 | XtSetArg(args[1], XtNheight, min(form_height,screen_height));
1833 | XtSetValues(toplevel, args, TWO);
1834 | XMoveWindow(XtDisplay(toplevel),XtWindow(toplevel),
1835 | 0, TITLEBAR_SPACE);
1836 | }
1837 | #undef TITLEBAR_SPACE
1838 | }
1839 |
1840 | post_process_tiles(); /* after toplevel is realized */
1841 |
1842 | /*
1843 | * Now get the default widths of the windows.
1844 | */
1845 | XtSetArg(args[0], XtNwidth, &message_vp_width);
1846 | XtGetValues(message_viewport, args, ONE);
1847 | XtSetArg(args[0], XtNwidth, &map_vp_width);
1848 | XtSetArg(args[1], XtNhorizDistance, &map_vp_hd);
1849 | XtGetValues(map_viewport, args, TWO);
1850 | XtSetArg(args[0], XtNwidth, &status_width);
1851 | XtSetArg(args[1], XtNhorizDistance, &status_hd);
1852 | XtGetValues(status, args, TWO);
1853 |
1854 | /*
1855 | * Adjust positions and sizes. The message viewport widens out to the
1856 | * widest width. Both the map and status are centered by adjusting
1857 | * their horizDistance.
1858 | */
1859 | if (map_vp_width < status_width || map_vp_width < message_vp_width) {
1860 | if (status_width > message_vp_width) {
1861 | XtSetArg(args[0], XtNwidth, status_width);
1862 | XtSetValues(message_viewport, args, ONE);
1863 | max_width = status_width;
1864 | } else {
1865 | /***** The status display looks better when left justified.
1866 | XtSetArg(args[0], XtNhorizDistance,
1867 | status_hd+((message_vp_width-status_width)/2));
1868 | XtSetValues(status, args, ONE);
1869 | *****/
1870 | max_width = message_vp_width;
1871 | }
1872 | XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2));
1873 | XtSetValues(map_viewport, args, ONE);
1874 |
1875 | } else { /* map is widest */
1876 | XtSetArg(args[0], XtNwidth, map_vp_width);
1877 | XtSetValues(message_viewport, args, ONE);
1878 |
1879 | /***** The status display looks better when left justified.
1880 | XtSetArg(args[0], XtNhorizDistance,
1881 | status_hd+((map_vp_width-status_width)/2));
1882 |
1883 | XtSetValues(status, args, ONE);
1884 | *****/
1885 | }
1886 | /*
1887 | * Clear all data values on the fancy status widget so that the values
1888 | * used for spacing don't appear. This needs to be called some time
1889 | * after the fancy status widget is realized (above, with the game popup),
1890 | * but before it is popped up.
1891 | */
1892 | null_out_status();
1893 | /*
1894 | * Set the map size to its standard size. As with the message window
1895 | * above, the map window needs to be set to its constrained size until
1896 | * its parent (the viewport widget) was realized.
1897 | *
1898 | * Move the message window's slider to the bottom.
1899 | */
1900 | set_map_size(&window_list[map_win], COLNO, ROWNO);
1901 | set_message_slider(&window_list[message_win]);
1902 |
1903 | /* attempt to catch fatal X11 errors before the program quits */
1904 | (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
1905 |
1906 | /* We can now print to the message window. */
1907 | iflags.window_inited = 1;
1908 | }
1909 |
1910 |
1911 | void
1912 | nh_XtPopup(w, g, childwid)
1913 | Widget w; /* widget */
1914 | int g; /* type of grab */
1915 | Widget childwid; /* child to recieve focus (can be None) */
1916 | {
1917 | XtPopup(w, (XtGrabKind)g);
1918 | XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
1919 | if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid);
1920 | }
1921 |
1922 | void
1923 | nh_XtPopdown(w)
1924 | Widget w;
1925 | {
1926 | XtPopdown(w);
1927 | if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None);
1928 | }
1929 |
1930 | void
1931 | win_X11_init()
1932 | {
1933 | #ifdef OPENWINBUG
1934 | /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
1935 | * two routines will not be found when linking. An apparently correct
1936 | * executable is produced, along with nasty messages and a failure code
1937 | * returned to make. The routines are in the static libXmu.a and
1938 | * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with
1939 | * static linking, we do this.
1940 | */
1941 | if (rn2(2) > 2) {
1942 | /* i.e., FALSE that an optimizer probably can't find */
1943 | get_wmShellWidgetClass();
1944 | get_applicationShellWidgetClass();
1945 | }
1946 | #endif
1947 | return;
1948 | }
1949 |
1950 | /* Callback
1951 | * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions.
1952 | */
1953 | /*ARGSUSED*/
1954 | void
1955 | nh_keyscroll(viewport, event, params, num_params)
1956 | Widget viewport;
1957 | XEvent *event;
1958 | String *params;
1959 | Cardinal *num_params;
1960 | {
1961 | Arg arg[2];
1962 | Widget horiz_sb, vert_sb;
1963 | float top, shown;
1964 | Boolean do_call;
1965 | int direction;
1966 | Cardinal in_nparams = (num_params ? *num_params : 0);
1967 |
1968 | if (in_nparams != 1) return; /* bad translation */
1969 |
1970 | direction=atoi(params[0]);
1971 |
1972 | horiz_sb = XtNameToWidget(viewport, "*horizontal");
1973 | vert_sb = XtNameToWidget(viewport, "*vertical");
1974 |
1975 | if (!horiz_sb && !vert_sb) {
1976 | /* Perhaps the widget enclosing this has scrollbars (could use while) */
1977 | Widget parent=XtParent(viewport);
1978 | if (parent) {
1979 | horiz_sb = XtNameToWidget(parent, "horizontal");
1980 | vert_sb = XtNameToWidget(parent, "vertical");
1981 | }
1982 | }
1983 |
1984 | #define H_DELTA 0.25 /* distance of horiz shift */
1985 | /* vert shift is half of curr distance */
1986 | /* The V_DELTA is 1/2 the value of shown. */
1987 |
1988 | if (horiz_sb) {
1989 | XtSetArg(arg[0], XtNshown, &shown);
1990 | XtSetArg(arg[1], XtNtopOfThumb, &top);
1991 | XtGetValues(horiz_sb, arg, TWO);
1992 |
1993 | do_call = True;
1994 |
1995 | switch (direction) {
1996 | case 1: case 4: case 7:
1997 | top -= H_DELTA;
1998 | if (top < 0.0) top = 0.0;
1999 | break; case 3: case 6: case 9:
2000 | top += H_DELTA;
2001 | if (top + shown > 1.0) top = 1.0 - shown;
2002 | break; default:
2003 | do_call = False;
2004 | }
2005 |
2006 | if (do_call) {
2007 | XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
2008 | }
2009 | }
2010 |
2011 | if (vert_sb) {
2012 | XtSetArg(arg[0], XtNshown, &shown);
2013 | XtSetArg(arg[1], XtNtopOfThumb, &top);
2014 | XtGetValues(vert_sb, arg, TWO);
2015 |
2016 | do_call = True;
2017 |
2018 | switch (direction) {
2019 | case 7: case 8: case 9:
2020 | top -= shown / 2.0;
2021 | if (top < 0.0) top = 0;
2022 | break; case 1: case 2: case 3:
2023 | top += shown / 2.0;
2024 | if (top + shown > 1.0) top = 1.0 - shown;
2025 | break; default:
2026 | do_call = False;
2027 | }
2028 |
2029 | if (do_call) {
2030 | XtCallCallbacks(vert_sb, XtNjumpProc, &top);
2031 | }
2032 | }
2033 | }
2034 |
2035 | /*winX.c*/