/* SCCS Id: @(#)winX.c 3.3 1999/12/21 */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ /* * "Main" file for the X window-port. This contains most of the interface * routines. Please see doc/window.doc for an description of the window * interface. */ #ifndef SYSV #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ #endif #ifdef MSDOS /* from compiler */ #define SHORT_FILENAMES #endif #include #include #include #include #include #include #include #include #include #include #include /* for color support */ #ifdef SHORT_FILENAMES #include #else #include #endif #ifdef PRESERVE_NO_SYSV # ifdef SYSV # undef SYSV # endif # undef PRESERVE_NO_SYSV #endif #ifdef SHORT_FILENAMES #undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */ #endif #include "hack.h" #include "winX.h" #include "dlb.h" #ifdef SHORT_FILENAMES #include "patchlev.h" #else #include "patchlevel.h" #endif /* Should be defined in but you never know */ #ifndef XtSpecificationRelease #define XtSpecificationRelease 0 #endif /* * Icons. */ #include "../win/X11/nh72icon" #include "../win/X11/nh56icon" #include "../win/X11/nh32icon" static struct icon_info { const char *name; unsigned char *bits; unsigned width, height; } icon_data[] = { { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height }, { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height }, { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height }, { (const char *)0, (unsigned char *)0, 0, 0 } }; /* * Private global variables (shared among the window port files). */ struct xwindow window_list[MAX_WINDOWS]; AppResources appResources; void FDECL((*input_func), (Widget,XEvent *,String *,Cardinal *)); int click_x, click_y, click_button; /* Click position on a map window */ /* (filled by set_button_values()). */ int updated_inventory; /* Interface definition, for windows.c */ struct window_procs X11_procs = { "X11", X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow, X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs, X11_putstr, X11_display_file, X11_start_menu, X11_add_menu, X11_end_menu, X11_select_menu, genl_message_menu, /* no need for X-specific handling */ X11_update_inventory, X11_mark_synch, X11_wait_synch, #ifdef CLIPPING X11_cliparound, #endif #ifdef POSITIONBAR donull, #endif X11_print_glyph, X11_raw_print, X11_raw_print_bold, X11_nhgetch, X11_nh_poskey, X11_nhbell, X11_doprev_message, X11_yn_function, X11_getlin, X11_get_ext_cmd, X11_number_pad, X11_delay_output, #ifdef CHANGE_COLOR /* only a Mac option currently */ donull, donull, #endif /* other defs that really should go away (they're tty specific) */ X11_start_screen, X11_end_screen, #ifdef GRAPHIC_TOMBSTONE X11_outrip, #else genl_outrip, #endif }; /* * Local functions. */ static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*)); static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*)); static int FDECL(input_event, (int)); static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *)); static void NDECL(init_standard_windows); /* * Local variables. */ static boolean x_inited = FALSE; /* TRUE if window system is set up. */ static winid message_win = WIN_ERR, /* These are the winids of the */ map_win = WIN_ERR, /* message, map, and status */ status_win = WIN_ERR; /* windows, when they are created */ /* in init_windows(). */ static Pixmap icon_pixmap = None; /* Pixmap for icon. */ /* * Find the window structure that corresponds to the given widget. Note * that this is not the popup widget, nor the viewport, but the child. */ struct xwindow * find_widget(w) Widget w; { int windex; struct xwindow *wp; /* Search to find the corresponding window. Look at the main widget, */ /* popup, the parent of the main widget, then parent of the widget. */ for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++) if (wp->type != NHW_NONE && (wp->w == w || wp->popup == w || (wp->w && (XtParent(wp->w)) == w) || (wp->popup == XtParent(w)))) break; if (windex == MAX_WINDOWS) panic("find_widget: can't match widget"); return wp; } /* * Find a free window slot for use. */ static winid find_free_window() { int windex; struct xwindow *wp; for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++) if (wp->type == NHW_NONE) break; if (windex == MAX_WINDOWS) panic("find_free_window: no free windows!"); return (winid) windex; } /* * Color conversion. The default X11 color converters don't try very * hard to find matching colors in PseudoColor visuals. If they can't * allocate the exact color, they puke and give you something stupid. * This is an attempt to find some close readonly cell and use it. */ XtConvertArgRec const nhcolorConvertArgs[] = { {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen), sizeof(Screen *)}, {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap), sizeof(Colormap)} }; #define done(type, value) \ { \ if (toVal->addr != 0) { \ if (toVal->size < sizeof(type)) { \ toVal->size = sizeof(type); \ return False; \ } \ *(type*)(toVal->addr) = (value); \ } \ else { \ static type static_val; \ static_val = (value); \ toVal->addr = (genericptr_t)&static_val; \ } \ toVal->size = sizeof(type); \ return True; \ } /* decl.h declares these, but it screws up structure references -dlc */ #undef red #undef green #undef blue /* * Find a color that approximates the color named in "str". The "str" color * may be a color name ("red") or number ("#7f0000"). If str == NULL, then * "color" is assumed to contain the RGB color wanted. * The approximate color found is returned in color as well. * Return True if something close was found. */ Boolean nhApproxColor(screen, colormap, str, color) Screen *screen; /* screen to use */ Colormap colormap; /* the colormap to use */ char *str; /* color name */ XColor *color; /* the X color structure; changed only if successful */ { int ncells; long cdiff = 16777216; /* 2^24; hopefully our map is smaller */ XColor tmp; static XColor *table = 0; register i, j; register long tdiff; /* if the screen doesn't have a big colormap, don't waste our time */ /* or if it's huge, and _some_ match should have been possible */ if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096) return False; if (str != (char *)0) { if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp)) return False; } else { tmp = *color; tmp.flags = 7; /* force to use all 3 of RGB */ } if (!table) { table = (XColor *) XtCalloc(ncells, sizeof(XColor)); for(i=0; iaddr; XColor screenColor; XColor exactColor; Screen *screen; XtAppContext app = XtDisplayToApplicationContext(dpy); Colormap colormap; Status status; String params[1]; Cardinal num_params=1; if (*num_args != 2) { XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel", "XtToolkitError", "String to pixel conversion needs screen and colormap arguments", (String *)0, (Cardinal *)0); return False; } screen = *((Screen **) args[0].addr); colormap = *((Colormap *) args[1].addr); /* If Xt colors, use the Xt routine and hope for the best */ #if (XtSpecificationRelease >= 5) if ((strcmpi(str, XtDefaultBackground) == 0) || (strcmpi(str, XtDefaultForeground) == 0)) { return XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret); } #else if (strcmpi(str, XtDefaultBackground) == 0) { *closure_ret = (char*)False; done(Pixel, WhitePixelOfScreen(screen)); } if (strcmpi(str, XtDefaultForeground) == 0) { *closure_ret = (char*)False; done(Pixel, BlackPixelOfScreen(screen)); } #endif status = XAllocNamedColor(DisplayOfScreen(screen), colormap, (char*)str, &screenColor, &exactColor); if (status == 0) { String msg, type; /* some versions of XAllocNamedColor don't allow #xxyyzz names */ if (str[0] == '#' && XParseColor(DisplayOfScreen(screen), colormap, str, &exactColor) && XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) { *closure_ret = (char*)True; done(Pixel, exactColor.pixel); } params[0] = str; /* Server returns a specific error code but Xlib discards it. Ugh */ if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str, &exactColor, &screenColor)) { /* try to find another color that will do */ if (nhApproxColor(screen, colormap, (char*) str, &screenColor)) { *closure_ret = (char*)True; done(Pixel, screenColor.pixel); } type = "noColormap"; msg = "Cannot allocate colormap entry for \"%s\""; } else { /* some versions of XLookupColor also don't allow #xxyyzz names */ if(str[0] == '#' && (nhApproxColor(screen, colormap, (char*) str, &screenColor))) { *closure_ret = (char*)True; done(Pixel, screenColor.pixel); } type = "badValue"; msg = "Color name \"%s\" is not defined"; } XtAppWarningMsg(app, type, "cvtStringToPixel", "XtToolkitError", msg, params, &num_params); *closure_ret = False; return False; } else { *closure_ret = (char*)True; done(Pixel, screenColor.pixel); } } /* ARGSUSED */ static void nhFreePixel(app, toVal, closure, args, num_args) XtAppContext app; XrmValuePtr toVal; XtPointer closure; XrmValuePtr args; Cardinal *num_args; { Screen *screen; Colormap colormap; if (*num_args != 2) { XtAppWarningMsg(app, "wrongParameters", "freePixel", "XtToolkitError", "Freeing a pixel requires screen and colormap arguments", (String *)0, (Cardinal *)0); return; } screen = *((Screen **) args[0].addr); colormap = *((Colormap *) args[1].addr); if (closure) { XFreeColors( DisplayOfScreen(screen), colormap, (unsigned long*)toVal->addr, 1, (unsigned long)0 ); } } /* Global Functions ======================================================== */ void X11_raw_print(str) const char *str; { (void) puts(str); } void X11_raw_print_bold(str) const char *str; { (void) puts(str); } void X11_curs(window, x, y) winid window; int x, y; { check_winid(window); if (x < 0 || x >= COLNO) { impossible("curs: bad x value [%d]", x); x = 0; } if (y < 0 || y >= ROWNO) { impossible("curs: bad y value [%d]", y); y = 0; } window_list[window].cursx = x; window_list[window].cursy = y; } void X11_putstr(window, attr, str) winid window; int attr; const char *str; { winid new_win; struct xwindow *wp; check_winid(window); wp = &window_list[window]; switch (wp->type) { case NHW_MESSAGE: (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */ toplines[TBUFSZ - 1] = 0; append_message(wp, str); break; case NHW_STATUS: adjust_status(wp, str); break; case NHW_MAP: impossible("putstr: called on map window \"%s\"", str); break; case NHW_MENU: if (wp->menu_information->is_menu) { impossible( "putstr: called on a menu window, \"%s\" discarded", str); break; } /* * Change this menu window into a text window by creating a * new text window, then copying it to this winid. */ new_win = X11_create_nhwindow(NHW_TEXT); X11_destroy_nhwindow(window); *wp = window_list[new_win]; window_list[new_win].type = NHW_NONE; /* allow re-use */ /* fall though to add text */ case NHW_TEXT: add_to_text_window(wp, attr, str); break; default: impossible("putstr: unknown window type [%d] \"%s\"", wp->type, str); } } /* We do event processing as a callback, so this is a null routine. */ void X11_get_nh_event() { return; } int X11_nhgetch() { return input_event(EXIT_ON_KEY_PRESS); } int X11_nh_poskey(x, y, mod) int *x, *y, *mod; { int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS); if (val == 0) { /* user clicked on a map window */ *x = click_x; *y = click_y; *mod = click_button; } return val; } winid X11_create_nhwindow(type) int type; { winid window; struct xwindow *wp; if (!x_inited) panic("create_nhwindow: windows not initialized"); /* * We have already created the standard message, map, and status * windows in the window init routine. The first window of that * type to be created becomes the standard. * * A better way to do this would be to say that init_nhwindows() * has already defined these three windows. */ if (type == NHW_MAP && map_win != WIN_ERR) { window = map_win; map_win = WIN_ERR; return window; } if (type == NHW_MESSAGE && message_win != WIN_ERR) { window = message_win; message_win = WIN_ERR; return window; } if (type == NHW_STATUS && status_win != WIN_ERR) { window = status_win; status_win = WIN_ERR; return window; } window = find_free_window(); wp = &window_list[window]; /* The create routines will set type, popup, w, and Win_info. */ wp->prevx = wp->prevy = wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; wp->keep_window = FALSE; switch (type) { case NHW_MAP: create_map_window(wp, TRUE, (Widget) 0); break; case NHW_MESSAGE: create_message_window(wp, TRUE, (Widget) 0); break; case NHW_STATUS: create_status_window(wp, TRUE, (Widget) 0); break; case NHW_MENU: create_menu_window(wp); break; case NHW_TEXT: create_text_window(wp); break; default: panic("create_nhwindow: unknown type [%d]\n", type); break; } return window; } void X11_clear_nhwindow(window) winid window; { struct xwindow *wp; check_winid(window); wp = &window_list[window]; switch (wp->type) { case NHW_MAP: clear_map_window(wp); break; case NHW_TEXT: clear_text_window(wp); break; case NHW_STATUS: case NHW_MENU: case NHW_MESSAGE: /* do nothing for these window types */ break; default: panic("clear_nhwindow: unknown window type [%d]\n", wp->type); break; } } void X11_display_nhwindow(window, blocking) winid window; boolean blocking; { struct xwindow *wp; check_winid(window); wp = &window_list[window]; switch (wp->type) { case NHW_MAP: if (wp->popup) nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); display_map_window(wp); /* flush map */ /* * We need to flush the message window here due to the way the tty * port is set up. To flush a window, you need to call this * routine. However, the tty port _pauses_ with a --more-- if we * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we * get a --more-- after every line. * * Perhaps the window document should mention that when the map * is flushed, everything on the three main windows should be * flushed. Note: we don't need to flush the status window * because we don't buffer changes. */ if (WIN_MESSAGE != WIN_ERR) display_message_window(&window_list[WIN_MESSAGE]); if (blocking) (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS); break; case NHW_MESSAGE: if (wp->popup) nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); display_message_window(wp); /* flush messages */ break; case NHW_STATUS: if (wp->popup) nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); break; /* no flushing necessary */ case NHW_MENU: { int n; menu_item *selected; /* pop up menu */ n = X11_select_menu(window, PICK_NONE, &selected); if (n) { impossible("perminvent: %d selected??", n); free((genericptr_t)selected); } break; } case NHW_TEXT: display_text_window(wp, blocking); /* pop up text window */ break; default: panic("display_nhwindow: unknown window type [%d]\n", wp->type); break; } } void X11_destroy_nhwindow(window) winid window; { struct xwindow *wp; check_winid(window); wp = &window_list[window]; /* * "Zap" known windows, but don't destroy them. We need to keep the * toplevel widget popped up so that later windows (e.g. tombstone) * are visible on DECWindow systems. This is due to the virtual * roots that the DECWindow wm creates. */ if (window == WIN_MESSAGE) { wp->keep_window = TRUE; WIN_MESSAGE = WIN_ERR; iflags.window_inited = 0; } else if (window == WIN_MAP) { wp->keep_window = TRUE; WIN_MAP = WIN_ERR; } else if (window == WIN_STATUS) { wp->keep_window = TRUE; WIN_STATUS = WIN_ERR; } else if (window == WIN_INVEN) { /* don't need to keep this one */ WIN_INVEN = WIN_ERR; } switch (wp->type) { case NHW_MAP: destroy_map_window(wp); break; case NHW_MENU: destroy_menu_window(wp); break; case NHW_TEXT: destroy_text_window(wp); break; case NHW_STATUS: destroy_status_window(wp); break; case NHW_MESSAGE: destroy_message_window(wp); break; default: panic("destroy_nhwindow: unknown window type [%d]", wp->type); break; } } void X11_update_inventory() { if (x_inited && window_list[WIN_INVEN].menu_information->is_up) { updated_inventory = 1; /* hack to avoid mapping&raising window */ (void) display_inventory((char *)0, FALSE); updated_inventory = 0; } } /* The current implementation has all of the saved lines on the screen. */ int X11_doprev_message() { return 0; } void X11_nhbell() { /* We can't use XBell until toplevel has been initialized. */ if (x_inited) XBell(XtDisplay(toplevel), 0); /* else print ^G ?? */ } void X11_mark_synch() { if (x_inited) { /* * The window document is unclear about the status of text * that has been pline()d but not displayed w/display_nhwindow(). * Both the main and tty code assume that a pline() followed * by mark_synch() results in the text being seen, even if * display_nhwindow() wasn't called. Duplicate this behavior. */ if (WIN_MESSAGE != WIN_ERR) display_message_window(&window_list[WIN_MESSAGE]); XSync(XtDisplay(toplevel), False); } } void X11_wait_synch() { if (x_inited) XFlush(XtDisplay(toplevel)); } /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */ void X11_resume_nhwindows() { return; } /* ARGSUSED */ void X11_suspend_nhwindows(str) const char *str; { return; } /* Under X, we don't need to initialize the number pad. */ /* ARGSUSED */ void X11_number_pad(state) int state; { return; } /* called from options.c */ void X11_start_screen() { return; } /* called from setftty() in unixtty.c */ void X11_end_screen() { return; } /* called from settty() in unixtty.c */ #ifdef GRAPHIC_TOMBSTONE void X11_outrip(window, how) winid window; int how; { struct xwindow *wp; check_winid(window); wp = &window_list[window]; if (wp->type == NHW_TEXT) { wp->text_information->is_rip = TRUE; } else { panic("ripout on non-text window (window type [%d])\n", wp->type); } calculate_rip_text(how); } #endif /* init and exit nhwindows ------------------------------------------------- */ XtAppContext app_context; /* context of application */ Widget toplevel = (Widget) 0; /* toplevel widget */ Atom wm_delete_window; /* pop down windows */ static XtActionsRec actions[] = { {"dismiss_file", dismiss_file}, /* action for file viewing widget */ {"delete_file", delete_file}, /* action for file delete-window */ {"dismiss_text", dismiss_text}, /* button action for text widget */ {"delete_text", delete_text}, /* delete action for text widget */ {"key_dismiss_text",key_dismiss_text},/* key action for text widget */ #ifdef GRAPHIC_TOMBSTONE {"rip_dismiss_text",rip_dismiss_text},/* action for rip in text widget */ #endif {"menu_key", menu_key}, /* action for menu accelerators */ {"yn_key", yn_key}, /* action for yn accelerators */ {"yn_delete", yn_delete}, /* action for yn delete-window */ {"askname_delete", askname_delete},/* action for askname delete-window */ {"getline_delete", getline_delete},/* action for getline delete-window */ {"menu_delete", menu_delete}, /* action for menu delete-window */ {"ec_key", ec_key}, /* action for extended commands */ {"ec_delete", ec_delete}, /* action for ext-com menu delete */ {"ps_key", ps_key}, /* action for player selection */ {"race_key", race_key}, /* action for race selection */ {"gend_key", gend_key}, /* action for gender selection */ {"algn_key", algn_key}, /* action for alignment selection */ {"X11_hangup", X11_hangup}, /* action for delete of top-level */ {"input", map_input}, /* action for key input */ {"scroll", nh_keyscroll}, /* action for scrolling by keys */ }; static XtResource resources[] = { { "slow", "Slow", XtRBoolean, sizeof(Boolean), XtOffset(AppResources *,slow), XtRString, "False" }, { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean), XtOffset(AppResources *,autofocus), XtRString, "False" }, { "message_line", "Message_line", XtRBoolean, sizeof(Boolean), XtOffset(AppResources *,message_line), XtRString, "False" }, { "double_tile_size", "Double_tile_size", XtRBoolean, sizeof(Boolean), XtOffset(AppResources *,double_tile_size), XtRString, "False" }, { "tile_file", "Tile_file", XtRString, sizeof(String), XtOffset(AppResources *,tile_file), XtRString, "" }, { "icon", "Icon", XtRString, sizeof(String), XtOffset(AppResources *,icon), XtRString, "nh72" }, { "message_lines", "Message_lines", XtRInt, sizeof(int), XtOffset(AppResources *,message_lines), XtRString, "12" }, { "pet_mark_bitmap", "Pet_mark_bitmap", XtRString, sizeof(String), XtOffset(AppResources *,pet_mark_bitmap), XtRString, "pet_mark.xbm" }, { "pet_mark_color", "Pet_mark_color", XtRPixel, sizeof(XtRPixel), XtOffset(AppResources *,pet_mark_color), XtRString, "Red" }, #ifdef GRAPHIC_TOMBSTONE { "tombstone", "Tombstone", XtRString, sizeof(String), XtOffset(AppResources *,tombstone), XtRString, "rip.xpm" }, { "tombtext_x", "Tombtext_x", XtRInt, sizeof(int), XtOffset(AppResources *,tombtext_x), XtRString, "155" }, { "tombtext_y", "Tombtext_y", XtRInt, sizeof(int), XtOffset(AppResources *,tombtext_y), XtRString, "78" }, { "tombtext_dx", "Tombtext_dx", XtRInt, sizeof(int), XtOffset(AppResources *,tombtext_dx), XtRString, "0" }, { "tombtext_dy", "Tombtext_dy", XtRInt, sizeof(int), XtOffset(AppResources *,tombtext_dy), XtRString, "13" }, #endif }; void X11_init_nhwindows(argcp,argv) int* argcp; char** argv; { static const char *banner_text[] = { COPYRIGHT_BANNER_A, COPYRIGHT_BANNER_B, COPYRIGHT_BANNER_C, "", "", 0 }; register const char **pp; int i; Cardinal num_args; Arg args[4]; uid_t savuid; /* Init windows to nothing. */ for (i = 0; i < MAX_WINDOWS; i++) window_list[i].type = NHW_NONE; /* * setuid hack: make sure that if nethack is setuid, to use real uid * when opening X11 connections, in case the user is using xauth, since * the "games" or whatever user probably doesn't have permission to open * a window on the user's display. This code is harmless if the binary * is not installed setuid. See include/system.h on compilation failures. */ savuid = geteuid(); (void) seteuid(getuid()); XSetIOErrorHandler((XIOErrorHandler) hangup); num_args = 0; XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; toplevel = XtAppInitialize( &app_context, "NetHack", /* application class */ (XrmOptionDescList)0, 0, /* options list */ argcp, (String *)argv, /* command line args */ (String *)0, /* fallback resources */ (ArgList)args, num_args); XtOverrideTranslations(toplevel, XtParseTranslationTable("WM_PROTOCOLS: X11_hangup()")); /* We don't need to realize the top level widget. */ #ifdef TEXTCOLOR /* add new color converter to deal with overused colormaps */ XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel, (XtConvertArgList)nhcolorConvertArgs, XtNumber(nhcolorConvertArgs), XtCacheByDisplay, nhFreePixel); #endif /* TEXTCOLOR */ /* Register the actions mentioned in "actions". */ XtAppAddActions(app_context, actions, XtNumber(actions)); /* Get application-wide resources */ XtGetApplicationResources(toplevel, (XtPointer)&appResources, resources, XtNumber(resources), (ArgList)0, ZERO); /* Initialize other things. */ init_standard_windows(); /* Give the window manager an icon to use; toplevel must be realized. */ if (appResources.icon && *appResources.icon) { struct icon_info *ip; for (ip = icon_data; ip->name; ip++) if (!strcmp(appResources.icon, ip->name)) { icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel), XtWindow(toplevel), (genericptr_t)ip->bits, ip->width, ip->height); if (icon_pixmap != None) { XWMHints hints; (void) memset((genericptr_t)&hints, 0, sizeof(XWMHints)); hints.flags = IconPixmapHint; hints.icon_pixmap = icon_pixmap; XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel), &hints); } break; } } /* end of setuid hack: reset uid back to the "games" uid */ (void) seteuid(savuid); x_inited = TRUE; /* X is now initialized */ /* Display the startup banner in the message window. */ for (pp = banner_text; *pp; pp++) X11_putstr(WIN_MESSAGE, 0, *pp); } /* * All done. */ /* ARGSUSED */ void X11_exit_nhwindows(dummy) const char *dummy; { extern Pixmap tile_pixmap; /* from winmap.c */ /* explicitly free the icon and tile pixmaps */ if (icon_pixmap != None) { XFreePixmap(XtDisplay(toplevel), icon_pixmap); icon_pixmap = None; } if (tile_pixmap != None) { XFreePixmap(XtDisplay(toplevel), tile_pixmap); tile_pixmap = None; } if (WIN_INVEN != WIN_ERR) X11_destroy_nhwindow(WIN_INVEN); if (WIN_STATUS != WIN_ERR) X11_destroy_nhwindow(WIN_STATUS); if (WIN_MAP != WIN_ERR) X11_destroy_nhwindow(WIN_MAP); if (WIN_MESSAGE != WIN_ERR) X11_destroy_nhwindow(WIN_MESSAGE); } /* delay_output ------------------------------------------------------------ */ /* * Timeout callback for delay_output(). Send a fake message to the map * window. */ /* ARGSUSED */ static void d_timeout(client_data, id) XtPointer client_data; XtIntervalId *id; { XEvent event; XClientMessageEvent *mesg; /* Set up a fake message to the event handler. */ mesg = (XClientMessageEvent *) &event; mesg->type = ClientMessage; mesg->message_type = XA_STRING; mesg->format = 8; XSendEvent(XtDisplay(window_list[WIN_MAP].w), XtWindow(window_list[WIN_MAP].w), False, NoEventMask, (XEvent*) mesg); } /* * Delay for 50ms. This is not implemented asynch. Maybe later. * Start the timeout, then wait in the event loop. The timeout * function will send an event to the map window which will be waiting * for a sent event. */ void X11_delay_output() { if (!x_inited) return; (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0); /* The timeout function will enable the event loop exit. */ (void) x_event(EXIT_ON_SENT_EVENT); } /* X11_hangup -------------------------------------------------------------- */ /* ARGSUSED */ static void X11_hangup(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */ } /* askname ----------------------------------------------------------------- */ /* ARGSUSED */ static void askname_delete(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { nh_XtPopdown(w); (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */ exit_x_event = TRUE; } /* Callback for askname dialog widget. */ /* ARGSUSED */ static void askname_done(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { int len; char *s; Widget dialog = (Widget) client_data; s = (char *) GetDialogResponse(dialog); len = strlen(s); if (len == 0) { X11_nhbell(); return; } /* Truncate name if necessary */ if (len >= sizeof(plname)-1) len = sizeof(plname)-1; (void) strncpy(plname, s, len); plname[len] = '\0'; XtFree(s); nh_XtPopdown(XtParent(dialog)); exit_x_event = TRUE; } void X11_askname() { Widget popup, dialog; Arg args[1]; XtSetArg(args[0], XtNallowShellResize, True); popup = XtCreatePopupShell("askname", transientShellWidgetClass, toplevel, args, ONE); XtOverrideTranslations(popup, XtParseTranslationTable("WM_PROTOCOLS: askname_delete()")); dialog = CreateDialog(popup, "dialog", askname_done, (XtCallbackProc) 0); SetDialogPrompt(dialog, "What is your name?"); /* set prompt */ SetDialogResponse(dialog, ""); /* set default answer */ XtRealizeWidget(popup); positionpopup(popup, TRUE); /* center,bottom */ nh_XtPopup(popup, (int)XtGrabExclusive, dialog); /* The callback will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); } /* getline ----------------------------------------------------------------- */ /* This uses Tim Theisen's dialog widget set (from GhostView). */ static Widget getline_popup, getline_dialog; #define CANCEL_STR "\033" static char *getline_input; /* Callback for getline dialog widget. */ /* ARGSUSED */ static void done_button(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { char *s; Widget dialog = (Widget) client_data; s = (char *) GetDialogResponse(dialog); Strcpy(getline_input, s); XtFree(s); nh_XtPopdown(XtParent(dialog)); exit_x_event = TRUE; } /* ARGSUSED */ static void getline_delete(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { Strcpy(getline_input, CANCEL_STR); nh_XtPopdown(w); exit_x_event = TRUE; } /* Callback for getline dialog widget. */ /* ARGSUSED */ static void abort_button(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { Widget dialog = (Widget) client_data; Strcpy(getline_input, CANCEL_STR); nh_XtPopdown(XtParent(dialog)); exit_x_event = TRUE; } void X11_getlin(question, input) const char *question; char *input; { static boolean need_to_init = True; getline_input = input; flush_screen(1); if (need_to_init) { Arg args[1]; need_to_init = False; XtSetArg(args[0], XtNallowShellResize, True); getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass, toplevel, args, ONE); XtOverrideTranslations(getline_popup, XtParseTranslationTable("WM_PROTOCOLS: getline_delete()")); getline_dialog = CreateDialog(getline_popup, "dialog", done_button, abort_button); XtRealizeWidget(getline_popup); XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup), &wm_delete_window, 1); } SetDialogPrompt(getline_dialog, (String)question); /* set prompt */ SetDialogResponse(getline_dialog, ""); /* set default answer */ positionpopup(getline_popup, TRUE); /* center,bottom */ nh_XtPopup(getline_popup, (int)XtGrabExclusive, getline_dialog); /* The callback will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); } /* Display file ------------------------------------------------------------ */ static const char display_translations[] = "#override\n\ q: dismiss_file()\n\ Escape: dismiss_file()\n\ : dismiss_file()"; /* WM_DELETE_WINDOW callback for file dismissal. */ /*ARGSUSED*/ static void delete_file(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { nh_XtPopdown(w); XtDestroyWidget(w); } /* Callback for file dismissal. */ /*ARGSUSED*/ static void dismiss_file(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { Widget popup = XtParent(w); nh_XtPopdown(popup); XtDestroyWidget(popup); } void X11_display_file(str, complain) const char *str; boolean complain; { dlb *fp; Arg args[12]; Cardinal num_args; Widget popup, dispfile; Position top_margin, bottom_margin, left_margin, right_margin; XFontStruct *fs; int new_width, new_height; #define LLEN 128 char line[LLEN]; int num_lines; char *textlines; int charcount; /* Use the port-independent file opener to see if the file exists. */ fp = dlb_fopen(str, RDTMODE); if (!fp) { if(complain) pline("Cannot open %s. Sorry.", str); return; /* it doesn't exist, ignore */ } /* * Count the number of lines and characters in the file. */ num_lines = 0; charcount = 1; while (dlb_fgets(line, LLEN, fp)) { num_lines++; charcount += strlen(line); } (void) dlb_fclose(fp); /* Ignore empty files */ if (num_lines == 0) return; /* If over the max window size, truncate the window size to the max */ if (num_lines >= DISPLAY_FILE_SIZE) num_lines = DISPLAY_FILE_SIZE; /* * Re-open the file and read the data into a buffer. Cannot use * the XawAsciiFile type of widget, because that is not DLB-aware. */ textlines = (char *) alloc((unsigned int) charcount); textlines[0] = '\0'; fp = dlb_fopen(str, RDTMODE); while (dlb_fgets(line, LLEN, fp)) { (void) strcat(textlines, line); } (void) dlb_fclose(fp); num_args = 0; XtSetArg(args[num_args], XtNtitle, str); num_args++; popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass, toplevel, args, num_args); XtOverrideTranslations(popup, XtParseTranslationTable("WM_PROTOCOLS: delete_file()")); num_args = 0; XtSetArg(args[num_args], XtNscrollHorizontal, XawtextScrollWhenNeeded); num_args++; XtSetArg(args[num_args], XtNscrollVertical, XawtextScrollWhenNeeded); num_args++; XtSetArg(args[num_args], XtNtype, XawAsciiString); num_args++; XtSetArg(args[num_args], XtNstring, textlines); num_args++; XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(display_translations)); num_args++; dispfile = XtCreateManagedWidget( "text", /* name */ asciiTextWidgetClass, popup, /* parent widget */ args, /* set some values */ num_args); /* number of values to set */ /* Get font and border information. */ num_args = 0; XtSetArg(args[num_args], XtNfont, &fs); num_args++; XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++; XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++; XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++; XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++; XtGetValues(dispfile, args, num_args); /* * Font height is ascent + descent. * * The data files are currently set up assuming an 80 char wide window * and a fixed width font. Soo.. */ new_height = num_lines * (fs->ascent + fs->descent) + top_margin + bottom_margin; new_width = 80 * fs->max_bounds.width + left_margin + right_margin; /* Set the new width and height. */ num_args = 0; XtSetArg(args[num_args], XtNwidth, new_width); num_args++; XtSetArg(args[num_args], XtNheight, new_height); num_args++; XtSetValues(dispfile, args, num_args); nh_XtPopup(popup, (int)XtGrabNone, (Widget)0); free(textlines); } /* yn_function ------------------------------------------------------------- */ /* (not threaded) */ static const char *yn_quitchars = " \n\r"; static const char *yn_choices; /* string of acceptable input */ static char yn_def; static char yn_return; /* return value */ static char yn_esc_map; /* ESC maps to this char. */ static Widget yn_popup; /* popup for the yn fuction (created once) */ static Widget yn_label; /* label for yn function (created once) */ static boolean yn_getting_num; /* TRUE if accepting digits */ static int yn_ndigits; /* digit count */ static long yn_val; /* accumulated value */ static const char yn_translations[] = "#override\n\ : yn_key()"; /* * Convert the given key event into a character. If the key maps to * more than one character only the first is returned. If there is * no conversion (i.e. just the CTRL key hit) a NUL is returned. */ char key_event_to_char(key) XKeyEvent *key; { char keystring[MAX_KEY_STRING]; int nbytes; boolean meta = !!(key->state & Mod1Mask); nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *)0, (XComposeStatus *)0); /* Modifier keys return a zero lengh string when pressed. */ if (nbytes == 0) return '\0'; return (char) (((int) keystring[0]) + (meta ? 0x80 : 0)); } /* * Called when we get a WM_DELETE_WINDOW event on a yn window. */ /* ARGSUSED */ static void yn_delete(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { yn_getting_num = FALSE; /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */ yn_return = yn_choices ? yn_esc_map : '\033'; exit_x_event = TRUE; /* exit our event handler */ } /* * Called when we get a key press event on a yn window. */ /* ARGSUSED */ static void yn_key(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { char ch; if(appResources.slow && !input_func) map_input(w, event, params, num_params); ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char or modifier event */ /* no bell */ return; } if (!yn_choices) { /* accept any input */ yn_return = ch; } else { ch = lowc(ch); /* move to lower case */ if (ch == '\033') { yn_getting_num = FALSE; yn_return = yn_esc_map; } else if (index(yn_quitchars, ch)) { yn_return = yn_def; } else if (index(yn_choices, ch)) { if (ch == '#') { if (yn_getting_num) { /* don't select again */ X11_nhbell(); return; } yn_getting_num = TRUE; yn_ndigits = 0; yn_val = 0; return; /* wait for more input */ } yn_return = ch; if (ch != 'y') yn_getting_num = FALSE; } else { if (yn_getting_num) { if (digit(ch)) { yn_ndigits++; yn_val = (yn_val * 10) + (long) (ch - '0'); return; /* wait for more input */ } if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) { yn_ndigits--; yn_val = yn_val/ 10; return; /* wait for more input */ } } X11_nhbell(); /* no match */ return; } if (yn_getting_num) { yn_return = '#'; if (yn_val < 0) yn_val = 0; yn_number = yn_val; /* assign global */ } } exit_x_event = TRUE; /* exit our event handler */ } char X11_yn_function(ques, choices, def) const char *ques; const char *choices; char def; { static Boolean need_to_init = True; char buf[QBUFSZ]; Arg args[4]; Cardinal num_args; yn_choices = choices; /* set up globals for callback to use */ yn_def = def; /* * This is sort of a kludge. There are quite a few places in the main * nethack code where a pline containing information is followed by a * call to yn_function(). There is no flush of the message window * (it is implicit in the tty window port), so the line never shows * up for us! Solution: do our own flush. */ if (WIN_MESSAGE != WIN_ERR) display_message_window(&window_list[WIN_MESSAGE]); if (choices) { char *cb, choicebuf[QBUFSZ]; Strcpy(choicebuf, choices); /* anything beyond is hidden */ if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0'; /* ques [choices] (def) */ if ((int)(1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= QBUFSZ) panic("yn_function: question too long"); Sprintf(buf, "%s [%s] ", ques, choicebuf); if (def) Sprintf(eos(buf), "(%c) ", def); /* escape maps to 'q' or 'n' or default, in that order */ yn_esc_map = (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def)); } else { if ((int)(1 + strlen(ques)) >= QBUFSZ) panic("yn_function: question too long"); Strcpy(buf, ques); } if (!appResources.slow && need_to_init) { need_to_init = False; XtSetArg(args[0], XtNallowShellResize, True); yn_popup = XtCreatePopupShell("query", transientShellWidgetClass, toplevel, args, ONE); XtOverrideTranslations(yn_popup, XtParseTranslationTable("WM_PROTOCOLS: yn_delete()")); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(yn_translations)); num_args++; yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, yn_popup, args, num_args); XtRealizeWidget(yn_popup); XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup), &wm_delete_window, 1); } if(appResources.slow) input_func = yn_key; num_args = 0; XtSetArg(args[num_args], XtNlabel, buf); num_args++; XtSetValues(yn_label, args, num_args); if(!appResources.slow) { /* * Due to some kind of weird bug in the X11R4 and X11R5 shell, we * need to set the label twice to get the size to change. */ num_args = 0; XtSetArg(args[num_args], XtNlabel, buf); num_args++; XtSetValues(yn_label, args, num_args); positionpopup(yn_popup, TRUE); nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label); } yn_getting_num = FALSE; (void) x_event(EXIT_ON_EXIT); if(appResources.slow) { input_func = 0; num_args = 0; XtSetArg(args[num_args], XtNlabel, " "); num_args++; XtSetValues(yn_label, args, num_args); } else { nh_XtPopdown(yn_popup); /* this removes the event grab */ } return yn_return; } /* End global functions ==================================================== */ /* * Before we wait for input via nhgetch() and nh_poskey(), we need to * do some pre-processing. */ static int input_event(exit_condition) int exit_condition; { if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */ check_turn_events(); if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */ check_cursor_visibility(&window_list[WIN_MAP]); if (WIN_MESSAGE != WIN_ERR) /* reset pause line */ set_last_pause(&window_list[WIN_MESSAGE]); return x_event(exit_condition); } /*ARGSUSED*/ void msgkey(w, data, event) Widget w; XtPointer data; XEvent *event; { Cardinal num = 0; map_input(window_list[WIN_MAP].w, event, (String*) 0, &num); } /*ARGSUSED*/ static void win_visible(w, data, event, flag) /* only called for autofocus */ Widget w; XtPointer data; /* client_data not used */ XEvent *event; Boolean *flag; /* continue_to_dispatch flag not used */ { XVisibilityEvent *vis_event = (XVisibilityEvent *)event; if (vis_event->state != VisibilityFullyObscured) { /* one-time operation; cancel ourself */ XtRemoveEventHandler(toplevel, VisibilityChangeMask, False, win_visible, (XtPointer) 0); /* grab initial input focus */ XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime); } } /* * Set up the playing console. This has three major parts: the * message window, the map, and the status window. */ static void init_standard_windows() { Widget form, message_viewport, map_viewport, status; Arg args[8]; Cardinal num_args; Dimension message_vp_width, map_vp_width, status_width, max_width; int map_vp_hd, status_hd; struct xwindow *wp; num_args = 0; XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; form = XtCreateManagedWidget("nethack", panedWidgetClass, toplevel, args, num_args); XtAddEventHandler(form, KeyPressMask, False, (XtEventHandler) msgkey, (XtPointer) 0); if (appResources.autofocus) XtAddEventHandler(toplevel, VisibilityChangeMask, False, win_visible, (XtPointer) 0); /* * Create message window. */ WIN_MESSAGE = message_win = find_free_window(); wp = &window_list[message_win]; wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; wp->popup = (Widget) 0; create_message_window(wp, FALSE, form); message_viewport = XtParent(wp->w); /* Tell the form that contains it that resizes are OK. */ num_args = 0; XtSetArg(args[num_args], XtNresizable, True); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; XtSetValues(message_viewport, args, num_args); if(appResources.slow) { num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(yn_translations)); num_args++; yn_label = XtCreateManagedWidget("yn_label", labelWidgetClass, form, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++; XtSetArg(args[num_args], XtNjustify, XtJustifyLeft); num_args++; XtSetArg(args[num_args], XtNresizable, True); num_args++; XtSetArg(args[num_args], XtNlabel, " "); num_args++; XtSetValues(yn_label, args, num_args); } /* * Create the map window & viewport and chain the viewport beneath the * message_viewport. */ map_win = find_free_window(); wp = &window_list[map_win]; wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; wp->popup = (Widget) 0; create_map_window(wp, FALSE, form); map_viewport = XtParent(wp->w); /* Chain beneath message_viewport or yn window. */ num_args = 0; if(appResources.slow) { XtSetArg(args[num_args], XtNfromVert, yn_label); num_args++; } else { XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++; } XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; XtSetValues(map_viewport, args, num_args); /* Create the status window, with the form as it's parent. */ status_win = find_free_window(); wp = &window_list[status_win]; wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; wp->popup = (Widget) 0; create_status_window(wp, FALSE, form); status = wp->w; /* * Chain the status window beneath the viewport. Mark the left and right * edges so that they stay a fixed distance from the left edge of the * parent, as well as the top and bottom edges so that they stay a fixed * distance from the bottom of the parent. We do this so that the status * will never expand or contract. */ num_args = 0; XtSetArg(args[num_args], XtNfromVert, map_viewport); num_args++; XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; XtSetArg(args[num_args], XtNtop, XtChainBottom); num_args++; XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; XtSetValues(status, args, num_args); /* * Realize the popup so that the status widget knows it's size. * * If we unset MappedWhenManaged then the DECwindow driver doesn't * attach the nethack toplevel to the highest virtual root window. * So don't do it. */ /* XtSetMappedWhenManaged(toplevel, False); */ XtRealizeWidget(toplevel); wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW", False); XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel), &wm_delete_window, 1); /* * Resize to at most full-screen. */ { #define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */ int screen_width = WidthOfScreen(XtScreen(wp->w)); int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE; Dimension form_width, form_height; XtSetArg(args[0], XtNwidth, &form_width); XtSetArg(args[1], XtNheight, &form_height); XtGetValues(toplevel, args, TWO); if (form_width > screen_width || form_height > screen_height) { XtSetArg(args[0], XtNwidth, min(form_width,screen_width)); XtSetArg(args[1], XtNheight, min(form_height,screen_height)); XtSetValues(toplevel, args, TWO); XMoveWindow(XtDisplay(toplevel),XtWindow(toplevel), 0, TITLEBAR_SPACE); } #undef TITLEBAR_SPACE } post_process_tiles(); /* after toplevel is realized */ /* * Now get the default widths of the windows. */ XtSetArg(args[0], XtNwidth, &message_vp_width); XtGetValues(message_viewport, args, ONE); XtSetArg(args[0], XtNwidth, &map_vp_width); XtSetArg(args[1], XtNhorizDistance, &map_vp_hd); XtGetValues(map_viewport, args, TWO); XtSetArg(args[0], XtNwidth, &status_width); XtSetArg(args[1], XtNhorizDistance, &status_hd); XtGetValues(status, args, TWO); /* * Adjust positions and sizes. The message viewport widens out to the * widest width. Both the map and status are centered by adjusting * their horizDistance. */ if (map_vp_width < status_width || map_vp_width < message_vp_width) { if (status_width > message_vp_width) { XtSetArg(args[0], XtNwidth, status_width); XtSetValues(message_viewport, args, ONE); max_width = status_width; } else { /***** The status display looks better when left justified. XtSetArg(args[0], XtNhorizDistance, status_hd+((message_vp_width-status_width)/2)); XtSetValues(status, args, ONE); *****/ max_width = message_vp_width; } XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2)); XtSetValues(map_viewport, args, ONE); } else { /* map is widest */ XtSetArg(args[0], XtNwidth, map_vp_width); XtSetValues(message_viewport, args, ONE); /***** The status display looks better when left justified. XtSetArg(args[0], XtNhorizDistance, status_hd+((map_vp_width-status_width)/2)); XtSetValues(status, args, ONE); *****/ } /* * Clear all data values on the fancy status widget so that the values * used for spacing don't appear. This needs to be called some time * after the fancy status widget is realized (above, with the game popup), * but before it is popped up. */ null_out_status(); /* * Set the map size to its standard size. As with the message window * above, the map window needs to be set to its constrained size until * its parent (the viewport widget) was realized. * * Move the message window's slider to the bottom. */ set_map_size(&window_list[map_win], COLNO, ROWNO); set_message_slider(&window_list[message_win]); /* attempt to catch fatal X11 errors before the program quits */ (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup); /* We can now print to the message window. */ iflags.window_inited = 1; } void nh_XtPopup(w, g, childwid) Widget w; /* widget */ int g; /* type of grab */ Widget childwid; /* child to recieve focus (can be None) */ { XtPopup(w, (XtGrabKind)g); XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1); if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid); } void nh_XtPopdown(w) Widget w; { XtPopdown(w); if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None); } void win_X11_init() { #ifdef OPENWINBUG /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these * two routines will not be found when linking. An apparently correct * executable is produced, along with nasty messages and a failure code * returned to make. The routines are in the static libXmu.a and * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with * static linking, we do this. */ if (rn2(2) > 2) { /* i.e., FALSE that an optimizer probably can't find */ get_wmShellWidgetClass(); get_applicationShellWidgetClass(); } #endif return; } /* Callback * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions. */ /*ARGSUSED*/ void nh_keyscroll(viewport, event, params, num_params) Widget viewport; XEvent *event; String *params; Cardinal *num_params; { Arg arg[2]; Widget horiz_sb, vert_sb; float top, shown; Boolean do_call; int direction; Cardinal in_nparams = (num_params ? *num_params : 0); if (in_nparams != 1) return; /* bad translation */ direction=atoi(params[0]); horiz_sb = XtNameToWidget(viewport, "*horizontal"); vert_sb = XtNameToWidget(viewport, "*vertical"); if (!horiz_sb && !vert_sb) { /* Perhaps the widget enclosing this has scrollbars (could use while) */ Widget parent=XtParent(viewport); if (parent) { horiz_sb = XtNameToWidget(parent, "horizontal"); vert_sb = XtNameToWidget(parent, "vertical"); } } #define H_DELTA 0.25 /* distance of horiz shift */ /* vert shift is half of curr distance */ /* The V_DELTA is 1/2 the value of shown. */ if (horiz_sb) { XtSetArg(arg[0], XtNshown, &shown); XtSetArg(arg[1], XtNtopOfThumb, &top); XtGetValues(horiz_sb, arg, TWO); do_call = True; switch (direction) { case 1: case 4: case 7: top -= H_DELTA; if (top < 0.0) top = 0.0; break; case 3: case 6: case 9: top += H_DELTA; if (top + shown > 1.0) top = 1.0 - shown; break; default: do_call = False; } if (do_call) { XtCallCallbacks(horiz_sb, XtNjumpProc, &top); } } if (vert_sb) { XtSetArg(arg[0], XtNshown, &shown); XtSetArg(arg[1], XtNtopOfThumb, &top); XtGetValues(vert_sb, arg, TWO); do_call = True; switch (direction) { case 7: case 8: case 9: top -= shown / 2.0; if (top < 0.0) top = 0; break; case 1: case 2: case 3: top += shown / 2.0; if (top + shown > 1.0) top = 1.0 - shown; break; default: do_call = False; } if (do_call) { XtCallCallbacks(vert_sb, XtNjumpProc, &top); } } } /*winX.c*/