From: PatR Date: Sat, 14 Mar 2020 10:47:27 +0000 (-0700) Subject: X11 status overhaul X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3cd9d1afd8c442c2baa3747d8a95a311a57c2c2f;p=nethack X11 status overhaul I started out adding a few new status conditions to X11's "fancy status" (the default) to gauge how difficult it was going to be. In the process I found several latent bugs. After fixing those, I decided that the same status conditions should be added to the alternate "tty-style status". Lots more latent bugs, some of the same nature, others different. Things spiraled until the code change is very substantial. Code for the old two-line status is still present but I don't know how to activate it. Unlike tty-style status, it composes and displays two lines of text and isn't capable of highlighting portions of that text, so it would be considered deprecated anyway. All testing was done with the default NetHack.ad (except when turning 'fancy_status' off) so I don't know whether the new code might override previously customizable status settings. I'm not sure whether this list covers all the fixes.... both tty-style and fancy add new status conditions 'grabbed' (by eel), 'held', 'trapped', and 'sinking-into-lava' (others will eventually follow); grab and lava are on by default, the others have to be enabled via options both tty-style (not handled) and fancy (faulty boolean logic) polymorphing didn't change Xp to HD (silver lining: rehumanizing didn't need to reverse it) tty-style only; fancy was ok force white text (on black background) instead of settling for gray turning on optional showexp, showscore, and/or time worked but turning them back off again didn't remove the relevant fields polymorphing when showexp was on didn't suppress Exp-points tty-style only; fancy uses different layout condense conditions into simple left-to-right space separated list instead of giving them specific locations and having gaps of blank space for conditions that aren't in effect tty-style only; not applicable for fancy (status_hilites not implemented) all highlights stuck if 'statushilites' was reset to 0 to disable them displaying anything with bold attribute stuck; it wouldn't revert to normal text if a different highlight rule without bold was used for subsequent updates avoid inverting leading space that separates from preceding field when highlighting with inverse video attribute add support for 'dim' attribute using gray foreground (only viable after the fix for white foreground) fancy only reorganize the field layout so that things line up nicely instead of having columns with six, seven, or eight lines be spread over same amount of vertical space line up the values of the six characteristics, similar to how vertical status works in curses: all two digits; when exceptional strength is present, the '18' lines up and rest goes past implicit right margin use status conditions as provided by core instead of duplicating them (other fields still duplicate stuff done in botl.c); doing this required forcing 'VIA_WINDOWPORT()' if built without STATUS_HILITES --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 8e0d72f85..8ebe6bb2d 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -126,6 +126,7 @@ msdos: Add -DSTATUES_LOOK_LIKE_MONSTERS to Makefile1.cross so the VESA mode tty: role and race selection menus weren't filtering out potential choices which got excluded by OPTIONS=align:!lawful or !neutral or !chaotic windows: update for new status condition fields +X11: substantial overhaul of status display, both 'fancy' and 'tty-style' General New Features diff --git a/include/winX.h b/include/winX.h index 56aafedd1..fe7f63b56 100644 --- a/include/winX.h +++ b/include/winX.h @@ -115,10 +115,18 @@ struct mesg_info_t { }; /* - * Information specific to a "text" status window. + * Information specific to "fancy", "text", or "tty-style" status window. + * (Tty-style supports status highlighting and effectively makes "text" + * obsolete.) */ struct status_info_t { struct text_buffer text; /* Just a text buffer. */ + Pixel fg, bg; /* foreground and background */ + XFontStruct *fs; /* Status window font structure. */ + Dimension spacew; /* width of one space */ + Position x, y[3]; /* x coord (not used), y for up to three lines */ + Dimension wd, ht; /* width (not used), height (same for all lines) */ + Dimension brd, in_wd; /* border width, internal width */ }; /* @@ -285,11 +293,12 @@ E void (*input_func)(); extern struct window_procs X11_procs; /* Check for an invalid window id. */ -#define check_winid(window) \ - if ((window) < 0 || (window) >= MAX_WINDOWS) { \ - panic("illegal windid [%d] in %s at line %d", window, __FILE__, \ - __LINE__); \ - } +#define check_winid(window) \ + do { \ + if ((window) < 0 || (window) >= MAX_WINDOWS) \ + panic("illegal windid [%d] in %s at line %d", \ + window, __FILE__, __LINE__); \ + } while (0) /* ### dialogs.c ### */ E Widget @@ -442,8 +451,10 @@ E void FDECL(X11_number_pad, (int)); E void NDECL(X11_delay_output); E void NDECL(X11_status_init); E void NDECL(X11_status_finish); -E void FDECL(X11_status_enablefield, (int, const char *, const char *, BOOLEAN_P)); -E void FDECL(X11_status_update, (int, genericptr_t, int, int, int, unsigned long *)); +E void FDECL(X11_status_enablefield, (int, const char *, const char *, + BOOLEAN_P)); +E void FDECL(X11_status_update, (int, genericptr_t, int, int, int, + unsigned long *)); /* other defs that really should go away (they're tty specific) */ E void NDECL(X11_start_screen); diff --git a/win/X11/winX.c b/win/X11/winX.c index c7afa415f..e2fff7c7b 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -98,13 +98,15 @@ static XtSignalId X11_sig_id; /* Interface definition, for windows.c */ struct window_procs X11_procs = { "X11", - (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP - | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT), -#if defined(STATUS_HILITES) - WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS | + ( WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP + | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT ), + /* status requires VIA_WINDOWPORT(); WC2_FLUSH_STATUS ensures that */ + ( WC2_FLUSH_STATUS +#ifdef STATUS_HILITES + | WC2_RESET_STATUS | WC2_HILITE_STATUS #endif - 0L, - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ + | 0L ), + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow, @@ -300,6 +302,9 @@ struct xwindow *wp; XtNbright_cyan, XtNwhite, }; + static const char *wintypenames[NHW_TEXT] = { + "message", "status", "map", "menu", "text" + }; Display *dpy; Colormap screen_colormap; XrmDatabase rDB; @@ -307,13 +312,8 @@ struct xwindow *wp; Status rc; int color; char *ret_type[32]; - char clr_name[BUFSZ]; - char clrclass[BUFSZ]; - const char *wintypenames[NHW_TEXT] = { - "message", "status", "map", "menu", "text" - }; + char wtn_up[20], clr_name[BUFSZ], clrclass[BUFSZ]; const char *wtn; - char wtn_up[BUFSZ]; if (wp->nh_colors_inited || !wp->type) return; @@ -1414,7 +1414,8 @@ static XtActionsRec actions[] = { static XtResource resources[] = { { nhStr("slow"), nhStr("Slow"), XtRBoolean, sizeof(Boolean), XtOffset(AppResources *, slow), XtRString, nhStr("True") }, - { nhStr("fancy_status"), nhStr("Fancy_status"), XtRBoolean, sizeof(Boolean), + { nhStr("fancy_status"), nhStr("Fancy_status"), + XtRBoolean, sizeof(Boolean), XtOffset(AppResources *, fancy_status), XtRString, nhStr("True") }, { nhStr("autofocus"), nhStr("AutoFocus"), XtRBoolean, sizeof(Boolean), XtOffset(AppResources *, autofocus), XtRString, nhStr("False") }, @@ -1469,10 +1470,10 @@ XErrorEvent *error; { char buf[BUFSZ]; XGetErrorText(display, error->error_code, buf, BUFSZ); - fprintf(stderr, "X Error: code %i (%s), request %i, minor %i, serial %lu\n", + fprintf(stderr, + "X Error: code %i (%s), request %i, minor %i, serial %lu\n", error->error_code, buf, - error->request_code, error->minor_code, - error->serial); + error->request_code, error->minor_code, error->serial); panic("X Error"); return 0; } @@ -2289,8 +2290,8 @@ static int input_event(exit_condition) int exit_condition; { - if (appResources.fancy_status && WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */ - check_turn_events(); + if (appResources.fancy_status && WIN_STATUS != WIN_ERR) + check_turn_events(); /* hilighting on the fancy status window */ 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 */ diff --git a/win/X11/winstat.c b/win/X11/winstat.c index 9dc5ac09c..965d3859c 100644 --- a/win/X11/winstat.c +++ b/win/X11/winstat.c @@ -15,16 +15,16 @@ #endif #include -#include +#include /* for XtResizeWidget() and XtConfigureWidget() */ #include #include #include -#include +#include /* just for ONE, TWO */ #include #include #include #include -#include +/*#include */ #ifdef PRESERVE_NO_SYSV #ifdef SYSV @@ -48,43 +48,63 @@ #define F_WIS 5 #define F_CHA 6 -#define F_NAME 7 -#define F_DLEVEL 8 +#define F_NAME 7 /* title: "Name the Rank" where rank is role-specific */ +#define F_DLEVEL 8 /* location: dungeon branch and level */ #define F_GOLD 9 #define F_HP 10 #define F_MAXHP 11 #define F_POWER 12 #define F_MAXPOWER 13 #define F_AC 14 -#define F_LEVEL 15 -#define F_EXP 16 +#define F_XP_LEVL 15 +/*#define F_HD F_XP_LEVL*/ +#define F_EXP_PTS 16 #define F_ALIGN 17 #define F_TIME 18 #define F_SCORE 19 -/* status conditions grouped by columns; tty orders these differently */ -#define F_STONE 20 -#define F_SLIME 21 -#define F_STRNGL 22 -#define F_FOODPOIS 23 -#define F_TERMILL 24 - -#define F_HUNGER 25 -#define F_ENCUMBER 26 -#define F_LEV 27 -#define F_FLY 28 -#define F_RIDE 29 - -#define F_BLIND 30 -#define F_DEAF 31 -#define F_STUN 32 -#define F_CONF 33 -#define F_HALLU 34 - -#define NUM_STATS 35 - -static void FDECL(update_fancy_status, (struct xwindow *)); +/* status conditions grouped by columns; tty orders these differently; + hunger/encumbrance/movement used to be in the middle with fatal + conditions on the left but those columns have been swapped and + renumbered to match new order (forcing shown_stat[] to be reordered) */ +#define F_HUNGER 20 +#define F_ENCUMBER 21 +#define F_TRAPPED 22 +#define F_LEV 23 +#define F_FLY 24 +#define F_RIDE 25 + +#define F_GRABBED 26 +#define F_STONE 27 +#define F_SLIME 28 +#define F_STRNGL 29 +#define F_FOODPOIS 30 +#define F_TERMILL 31 +#define F_IN_LAVA 32 + +#define F_HELD 33 +#define F_BLIND 34 +#define F_DEAF 35 +#define F_STUN 36 +#define F_CONF 37 +#define F_HALLU 38 + +#define NUM_STATS 39 + +static int FDECL(condcolor, (long, unsigned long *)); +static int FDECL(condattr, (long, unsigned long *)); +static void FDECL(HiliteField, (Widget, int, int, int, XFontStruct **)); +static void FDECL(PrepStatusField, (int, Widget, const char *)); +static void FDECL(DisplayCond, (int, unsigned long *)); +static int FDECL(render_conditions, (int, int)); +#ifdef STATUS_HILITES +static void FDECL(tt_reset_color, (int, int, unsigned long *)); +#endif +static void NDECL(tt_status_fixup); +static Widget FDECL(create_tty_status_field, (int, int, Widget, Widget)); +static Widget FDECL(create_tty_status, (Widget, Widget)); static void FDECL(update_fancy_status_field, (int)); +static void FDECL(update_fancy_status, (BOOLEAN_P)); static Widget FDECL(create_fancy_status, (Widget, Widget)); static void FDECL(destroy_fancy_status, (struct xwindow *)); static void FDECL(create_status_window_fancy, (struct xwindow *, @@ -99,14 +119,21 @@ static void FDECL(adjust_status_tty, (struct xwindow *, const char *)); extern const char *status_fieldfmt[MAXBLSTATS]; extern char *status_vals[MAXBLSTATS]; extern boolean status_activefields[MAXBLSTATS]; -static long X11_condition_bits; -static int X11_status_colors[MAXBLSTATS]; + +static unsigned long X11_condition_bits, old_condition_bits; +static int X11_status_colors[MAXBLSTATS], + old_field_colors[MAXBLSTATS], + old_cond_colors[32]; static int hpbar_percent, hpbar_color; +/* Number of conditions displayed during this update and last update. + When the last update had more, the excess need to be erased. */ +static int next_cond_indx = 0, prev_cond_indx = 0; +/* TODO: support statuslines:3 in addition to 2 for the tty-style status */ #define X11_NUM_STATUS_LINES 2 #define X11_NUM_STATUS_FIELD 15 -static enum statusfields X11_fieldorder[X11_NUM_STATUS_LINES][X11_NUM_STATUS_FIELD] = { +static enum statusfields X11_fieldorder[][X11_NUM_STATUS_FIELD] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH }, @@ -115,15 +142,39 @@ static enum statusfields X11_fieldorder[X11_NUM_STATUS_LINES][X11_NUM_STATUS_FIE BL_CAP, BL_CONDITION, BL_FLUSH } }; +/* condition list for tty-style display, roughly in order of importance */ +static struct tt_condinfo { + unsigned long mask; + const char *text; +} tt_condorder[] = { + { BL_MASK_GRAB, "Grab!" }, + { BL_MASK_STONE, "Stone" }, + { BL_MASK_SLIME, "Slime" }, + { BL_MASK_STRNGL, "Strngl" }, + { BL_MASK_FOODPOIS, "FoodPois" }, + { BL_MASK_TERMILL, "TermIll" }, + { BL_MASK_INLAVA, "Lava" }, + { BL_MASK_HELD, "Held" }, + { BL_MASK_BLIND, "Blind" }, + { BL_MASK_DEAF, "Deaf" }, + { BL_MASK_STUN, "Stun" }, + { BL_MASK_CONF, "Conf" }, + { BL_MASK_HALLU, "Hallu" }, + { BL_MASK_TRAPPED, "Trap" }, + { BL_MASK_LEV, "Lev" }, + { BL_MASK_FLY, "Fly" }, + { BL_MASK_RIDE, "Ride" }, +}; + static Widget X11_status_widget; static Widget X11_status_labels[MAXBLSTATS]; static Widget X11_cond_labels[32]; /* Ugh */ +static XFontStruct *X11_status_font; +static Pixel X11_status_fg, X11_status_bg; struct xwindow *xw_status_win; -static Pixel X11_status_widget_fg, X11_status_widget_bg; - -int +static int condcolor(bm, bmarray) long bm; unsigned long *bmarray; @@ -138,7 +189,7 @@ unsigned long *bmarray; return NO_COLOR; } -int +static int condattr(bm, bmarray) long bm; unsigned long *bmarray; @@ -175,14 +226,15 @@ unsigned long *bmarray; void X11_status_init() { -#ifdef STATUS_HILITES int i; + /* no color and no attributes */ for (i = 0; i < MAXBLSTATS; ++i) - X11_status_colors[i] = NO_COLOR; /* no color */ - X11_condition_bits = 0L; + X11_status_colors[i] = old_field_colors[i] = NO_COLOR; + for (i = 0; i < SIZE(old_cond_colors); ++i) + old_cond_colors[i] = NO_COLOR; hpbar_percent = 0, hpbar_color = NO_COLOR; -#endif /* STATUS_HILITES */ + X11_condition_bits = old_condition_bits = 0L; /* let genl_status_init do most of the initialization */ genl_status_init(); } @@ -191,6 +243,7 @@ void X11_status_finish() { /* nothing */ + return; } void @@ -203,335 +256,561 @@ boolean enable; genl_status_enablefield(fieldidx, nm, fmt, enable); } - +#if 0 int cond_bm2idx(bm) unsigned long bm; { int i; + for (i = 0; i < 32; i++) if ((1 << i) == bm) return i; return -1; } +#endif -void -MaybeDisplayCond(bm, colormasks, text) -unsigned long bm; -unsigned long *colormasks; +/* highlight a tty-style status field (or condition) */ +static void +HiliteField(label, fld, cond, colrattr, font_p) +Widget label; +int fld, cond, colrattr; +XFontStruct **font_p; +{ +#ifdef STATUS_HILITES + static Pixel grayPxl, blackPxl, whitePxl; + Arg args[6]; + Cardinal num_args; + XFontStruct *font = X11_status_font; + Pixel px, fg = X11_status_fg, bg = X11_status_bg; + struct xwindow *xw = xw_status_win; + int colr, attr; + +#ifdef TEXTCOLOR + if ((colrattr & 0x00ff) >= CLR_MAX) + /* for !TEXTCOLOR, the following line is unconditional */ +#endif + colrattr = (colrattr & ~0x00ff) | NO_COLOR; + colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */ + attr = (colrattr >> 8) & 0x00ff; + + /* potentially used even for !TEXTCOLOR configuration */ + if (!grayPxl) {/* one-time init */ + grayPxl = get_nhcolor(xw, CLR_GRAY).pixel; + blackPxl = get_nhcolor(xw, CLR_BLACK).pixel; + whitePxl = get_nhcolor(xw, CLR_WHITE).pixel; + } + /* [shouldn't be necessary; setting up gray will set up all colors] */ + if (colr != NO_COLOR && !xw->nh_colors[colr].pixel) + (void) get_nhcolor(xw, colr); + + /* handle highlighting if caller has specified that; set foreground, + background, and font even if not specified this time in case they + used modified values last time (which would stick if not reset) */ + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + if (colr != NO_COLOR) + fg = xw->nh_colors[colr].pixel; + if ((attr & HL_INVERSE) != 0) { + px = fg; + fg = bg; + bg = px; + } + /* foreground and background might both default to black, so we + need to force one to be different if/when they're the same + (actually, tt_status_fixup() takes care of that nowadays); + using gray to implement 'dim' only works for black and white + (or color+'inverse' when former background was black or white) */ + if (fg == bg + || ((attr & HL_DIM) != 0 && (fg == whitePxl || fg == blackPxl))) + fg = (fg != grayPxl) ? grayPxl + : (fg != blackPxl) ? blackPxl + : whitePxl; + XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; + XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; + if (attr & HL_BOLD) { + load_boldfont(xw_status_win, label); + if (xw_status_win->boldfs) + font = xw_status_win->boldfs; + } + XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++; + XtSetValues(label, args, num_args); + + /* return possibly modified font to caller so that text width + measurement can use it */ + if (font_p) + *font_p = font; +#else /*!STATUS_HILITES*/ + nhUse(label); + nhUse(font_p); +#endif /*?STATUS_HILITES*/ + if (fld != BL_CONDITION) + old_field_colors[fld] = colrattr; + else + old_cond_colors[cond] = colrattr; +} + +/* set up a specific field other than 'condition'; its general location + was specified during widget creation but it might need adjusting */ +static void +PrepStatusField(fld, label, text) +int fld; +Widget label; const char *text; { - int idx = cond_bm2idx(bm); + Arg args[6]; + Cardinal num_args; + Dimension lbl_wid; + XFontStruct *font = X11_status_font; + int colrattr = X11_status_colors[fld]; + struct status_info_t *si = xw_status_win->Win_info.Status_info; + + /* highlight if color and/or attribute(s) are different from last time */ + if (colrattr != old_field_colors[fld]) + HiliteField(label, fld, 0, colrattr, &font); + + num_args = 0; + (void) memset((genericptr_t) args, 0, sizeof args); + /* set up the current text to be displayed */ + if (text && *text) { + lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text)); + } else { + text = ""; + lbl_wid = 1; + } + XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++; + /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/ + XtSetValues(label, args, num_args); + XtResizeWidget(label, lbl_wid, si->ht, si->brd); +} + +/* set up one status condition for tty-style status display */ +static void +DisplayCond(c_idx, colormasks) +int c_idx; /* index into tt_condorder[] */ +unsigned long *colormasks; +{ Widget label; - Arg args[10]; + Arg args[6]; Cardinal num_args; - Pixel fg = X11_status_widget_fg, bg = X11_status_widget_bg; Dimension lbl_wid; - Dimension lbl_hei; - Dimension lbl_border_wid; - Dimension lbl_iwidth; + XFontStruct *font = X11_status_font; + int coloridx, attrmask, colrattr, idx; + unsigned long bm = tt_condorder[c_idx].mask; + const char *text = tt_condorder[c_idx].text; + struct status_info_t *si = xw_status_win->Win_info.Status_info; - if (idx < 0) + if ((X11_condition_bits & bm) == 0) return; + /* widgets have been created for every condition; we allocate them + from left to right rather than keeping their original assignments */ + idx = next_cond_indx; label = X11_cond_labels[idx]; - if ((X11_condition_bits & bm) != 0) { - int attrmask, coloridx; - XFontStruct *font; - Position lbl_x; - Position lbl_y; -#ifdef TEXTCOLOR - coloridx = condcolor(bm, colormasks); -#else - coloridx = NO_COLOR; -#endif - attrmask = condattr(bm, colormasks); + /* handle highlighting if caller requests it */ + coloridx = condcolor(bm, colormasks); + attrmask = condattr(bm, colormasks); + colrattr = (attrmask << 8) | coloridx; + if (colrattr != old_cond_colors[c_idx]) + HiliteField(label, BL_CONDITION, c_idx, colrattr, &font); + + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + /* set the condition text and its width; this widget might have + been displaying a different condition last time around */ + XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++; + /* measure width after maybe changing font [HiliteField()] */ + lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text)); + /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/ + + /* make this condition widget be ready for display */ + XtSetValues(label, args, num_args); + XtResizeWidget(label, lbl_wid, si->ht, si->brd); + + ++next_cond_indx; +} + +/* display the tty-style status conditions; the number shown varies and + we might be showing more, same, or fewer than during previous status */ +static int +render_conditions(row, dx) +int row, dx; +{ + Widget label; + Arg args[6]; + Cardinal num_args; + Position lbl_x; + int i, gap = 5; + struct status_info_t *si = xw_status_win->Win_info.Status_info; + Dimension lbl_wid, brd_wid = si->brd; + + for (i = 0; i < next_cond_indx; i++) { + label = X11_cond_labels[i]; + + /* width of this widget was set in DisplayCond(); fetch it */ + (void) memset((genericptr_t) args, 0, sizeof args); num_args = 0; - XtSetArg(args[num_args], nhStr(XtNfont), &font); num_args++; - XtSetArg(args[num_args], nhStr(XtNx), &lbl_x); num_args++; - XtSetArg(args[num_args], nhStr(XtNy), &lbl_y); num_args++; XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++; - XtSetArg(args[num_args], nhStr(XtNheight), &lbl_hei); num_args++; - XtSetArg(args[num_args], nhStr(XtNinternalWidth), - &lbl_iwidth); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), - &lbl_border_wid); num_args++; XtGetValues(label, args, num_args); - if (text && *text) - lbl_wid = lbl_iwidth + font->max_bounds.width * strlen(text); - else - lbl_wid = 1; + /* figure out where to draw this widget and place it there */ + lbl_x = (Position) (dx + 1 + gap); - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNlabel), - (text && *text) ? text : ""); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++; - - fg = (coloridx != NO_COLOR) - ? get_nhcolor(xw_status_win, coloridx).pixel - : X11_status_widget_fg; - if (attrmask & HL_INVERSE) { - Pixel tmppx = fg; - - fg = bg; - bg = tmppx; - } + XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, brd_wid); - if (attrmask & HL_BOLD) { - load_boldfont(xw_status_win, label); - XtSetArg(args[num_args], nhStr(XtNfont), - xw_status_win->boldfs); num_args++; + /* keep track of where the end of our text appears */ + dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1; + } + + /* if we have fewer conditions shown now than last time, set the + excess ones to blank; unlike the set drawn above, these haven't + been prepared in advance by DisplayCond because they aren't + being displayed; they might have been highlighted last time so + we need to specify more than just an empty text string */ + if (next_cond_indx < prev_cond_indx) { + XFontStruct *font = X11_status_font; + Pixel fg = X11_status_fg, bg = X11_status_bg; + + lbl_x = dx + 1; + lbl_wid = 1 + 2 * brd_wid; + for (i = next_cond_indx; i < prev_cond_indx; ++i) { + label = X11_cond_labels[i]; + + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++; + XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++; + XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; + XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; + /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/ + /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/ + XtSetValues(label, args, num_args); + old_cond_colors[i] = NO_COLOR; /* fg, bg, font were just reset */ + XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, 0); + /* don't advance 'dx' here */ } + } - if (fg == bg) - fg = get_nhcolor(xw_status_win, CLR_GRAY).pixel; + return dx; +} - XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; - XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; - XtSetValues(label, args, num_args); - XtResizeWidget(label, lbl_wid, lbl_hei, lbl_border_wid); - } else { - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++; - XtSetArg(args[num_args], nhStr(XtNheight), &lbl_hei); num_args++; - XtSetArg(args[num_args], nhStr(XtNinternalWidth), - &lbl_iwidth); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), - &lbl_border_wid); num_args++; - XtGetValues(label, args, num_args); +#ifdef STATUS_HILITES +/* reset status_hilite for BL_RESET; if highlighting has been disabled or + this field is disabled, clear highlighting for this field or condition */ +static void +tt_reset_color(fld, cond, colormasks) +int fld, cond; +unsigned long *colormasks; +{ + Widget label; + int colrattr = NO_COLOR; - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), 1); num_args++; - XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; - XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; - XtSetValues(label, args, num_args); + if (fld != BL_CONDITION) { + if (iflags.hilite_delta != 0L && status_activefields[fld]) + colrattr = X11_status_colors[fld]; + cond = 0; + label = X11_status_labels[fld]; + } else { + unsigned long bm = tt_condorder[cond].mask; - XtResizeWidget(label, lbl_wid, lbl_hei, lbl_border_wid); + if (iflags.hilite_delta != 0L && (X11_condition_bits & bm) != 0) { + /* BL_RESET before first BL_CONDITION will have colormasks==Null + but condcolor() and condattr() can cope with that */ +#ifdef TEXTCOLOR + colrattr = condcolor(bm, colormasks); +#endif + colrattr |= (condattr(bm, colormasks) << 8); + } + label = X11_cond_labels[cond]; } + HiliteField(label, fld, cond, colrattr, (XFontStruct **) 0); } +#endif +/* make sure foreground, background, and font have reasonable values, + then explicitly set them for all the status widgets; + also cache some geometry settings in (*xw_status_win).Status_info */ +static void +tt_status_fixup() +{ + Arg args[6]; + Cardinal num_args; + Widget w; + Position lbl_y; + int x, y, ci, fld; + XFontStruct *font = 0; + Pixel fg = 0, bg = 0; + struct status_info_t *si = xw_status_win->Win_info.Status_info; -void + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfont), &font); num_args++; + XtSetArg(args[num_args], nhStr(XtNforeground), &fg); num_args++; + XtSetArg(args[num_args], nhStr(XtNbackground), &bg); num_args++; + XtGetValues(X11_status_widget, args, num_args); + if (fg == bg) { + XColor black = get_nhcolor(xw_status_win, CLR_BLACK), + white = get_nhcolor(xw_status_win, CLR_WHITE); + + fg = (bg == white.pixel) ? black.pixel : white.pixel; + } + X11_status_fg = si->fg = fg, X11_status_bg = si->bg = bg; + + if (!font) { + w = X11_status_widget; + XtSetArg(args[0], nhStr(XtNfont), &font); + do { + XtGetValues(w, args, ONE); + } while (!font && (w = XtParent(w)) != 0); + + if (!font) { + /* trial and error time -- this is where we've actually + been obtaining the font even though we aren't setting + it for any of the field widgets (until for(y,x) below) */ + XtGetValues(X11_status_labels[0], args, ONE); + + if (!font) { /* this bit is untested... */ + /* write some text and hope Xaw sets up font for us */ + XtSetArg(args[0], nhStr(XtNlabel), "NetHack"); + XtSetValues(X11_status_labels[0], args, ONE); + (void) XFlush(XtDisplay(X11_status_labels[0])); + + XtSetArg(args[0], nhStr(XtNfont), &font); + XtGetValues(X11_status_labels[0], args, ONE); + + /* if still Null, XTextWidth() would crash so bail out */ + if (!font) + panic("X11 status can't determine font."); + } + } + } + X11_status_font = si->fs = font; + + /* amount of space to advance a widget's location by one space; + increase width a tiny bit beyond the actual space */ + si->spacew = XTextWidth(X11_status_font, " ", 1) + (Dimension) 1; + + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++; + XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; + XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; + XtSetValues(X11_status_widget, args, num_args); + + for (y = 0; y < X11_NUM_STATUS_LINES; y++) { + for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { + fld = X11_fieldorder[y][x]; /* next field to handle */ + if (fld <= BL_FLUSH) + continue; /* skip fieldorder[][] padding */ + + XtSetValues(X11_status_labels[fld], args, num_args); + old_field_colors[fld] = NO_COLOR; + + if (fld == BL_CONDITION) { + for (ci = 0; ci < SIZE(X11_cond_labels); ++ci) { /* 0..31 */ + XtSetValues(X11_cond_labels[ci], args, num_args); + old_cond_colors[ci] = NO_COLOR; + } + } + } + } + + /* cache the y coordinate of each row for XtConfigureWidget() */ + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[0], nhStr(XtNy), &lbl_y); num_args++; + for (y = 0; y < X11_NUM_STATUS_LINES; y++) { + fld = X11_fieldorder[y][0]; + XtGetValues(X11_status_labels[fld], args, num_args); + si->y[y] = lbl_y; + } + + /* cache height, borderWidth, and internalWidth for XtResizeWidget() */ + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNheight), &si->ht); num_args++; + XtSetArg(args[num_args], nhStr(XtNborderWidth), &si->brd); num_args++; + XtSetArg(args[num_args], nhStr(XtNinternalWidth), &si->in_wd); num_args++; + XtGetValues(X11_status_labels[0], args, num_args); +} + +/* core requests updating one status field (or is indicating that it's time + to flush all updated fields); tty-style handling */ +static void X11_status_update_tty(fld, ptr, chg, percent, color, colormasks) int fld, chg UNUSED, percent, color; genericptr_t ptr; -unsigned long *colormasks; +unsigned long *colormasks; /* bitmask of highlights for conditions */ { - static boolean oncearound = FALSE; /* prevent premature partial display */ - long *condptr = (long *) ptr; - int coloridx = NO_COLOR; - const char *text = (char *) ptr; - char tmpbuf[BUFSZ]; - int attridx = 0; - - XFontStruct *font; - Arg args[10]; + static int xtra_space[MAXBLSTATS]; + static unsigned long *cond_colormasks = (unsigned long *) 0; + + Arg args[6]; Cardinal num_args; Position lbl_x; - Position lbl_y; - Dimension lbl_wid; - Dimension lbl_hei; - Dimension lbl_border_wid; - Dimension lbl_iwidth; + Dimension lbl_wid, brd_wid; Widget label; - Pixel fg = X11_status_widget_fg, bg = X11_status_widget_bg; -#ifndef TEXTCOLOR - color = NO_COLOR; -#endif + struct status_info_t *si; + char goldbuf[40]; + const char *fmt; + const char *text; + unsigned long *condptr; + int f, x, y, dx; - if (fld < BL_RESET || fld >= MAXBLSTATS) - return; + if (X11_status_fg == X11_status_bg || !X11_status_font) + tt_status_fixup(); + + if (fld == BL_RESET) { +#ifdef STATUS_HILITES + for (y = 0; y < X11_NUM_STATUS_LINES; y++) { + for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { + f = X11_fieldorder[y][x]; + if (f <= BL_FLUSH) + continue; /* skip padding in the fieldorder[][] layout */ + if (f != BL_CONDITION) { + tt_reset_color(f, 0, (unsigned long *) 0); + } else { + int c_i; + + for (c_i = 0; c_i < SIZE(tt_condorder); ++c_i) + tt_reset_color(f, c_i, cond_colormasks); + } + } + } +#endif + fld = BL_FLUSH; + } - if ((fld >= 0 && fld < MAXBLSTATS) && !status_activefields[fld]) + if (fld == BL_CONDITION) { + condptr = (unsigned long *) ptr; + X11_condition_bits = *condptr; + cond_colormasks = colormasks; /* expected to be non-Null */ + + prev_cond_indx = next_cond_indx; + next_cond_indx = 0; + /* if any conditions are active, set up their widgets */ + if (X11_condition_bits) + for (f = 0; f < SIZE(tt_condorder); ++f) + DisplayCond(f, cond_colormasks); return; - if (fld != BL_FLUSH && fld != BL_RESET) { - if (!status_activefields[fld]) - return; - switch (fld) { - case BL_CONDITION: - X11_condition_bits = *condptr; - oncearound = TRUE; - break; - default: - Sprintf(status_vals[fld], - (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" : - status_fieldfmt[fld] ? status_fieldfmt[fld] : "%s", - text); - X11_status_colors[fld] = color; - if (iflags.wc2_hitpointbar && fld == BL_HP) { - hpbar_percent = percent; - hpbar_color = color; - } - break; - } - if (fld == BL_CONDITION) { - MaybeDisplayCond(BL_MASK_STONE, colormasks, "Stone"); - MaybeDisplayCond(BL_MASK_SLIME, colormasks, "Slime"); - MaybeDisplayCond(BL_MASK_STRNGL, colormasks, "Strngl"); - MaybeDisplayCond(BL_MASK_FOODPOIS, colormasks, "FoodPois"); - MaybeDisplayCond(BL_MASK_TERMILL, colormasks, "TermIll"); - MaybeDisplayCond(BL_MASK_BLIND, colormasks, "Blind"); - MaybeDisplayCond(BL_MASK_DEAF, colormasks, "Deaf"); - MaybeDisplayCond(BL_MASK_STUN, colormasks, "Stun"); - MaybeDisplayCond(BL_MASK_CONF, colormasks, "Conf"); - MaybeDisplayCond(BL_MASK_HALLU, colormasks, "Hallu"); - MaybeDisplayCond(BL_MASK_LEV, colormasks, "Lev"); - MaybeDisplayCond(BL_MASK_FLY, colormasks, "Fly"); - MaybeDisplayCond(BL_MASK_RIDE, colormasks, "Ride"); + } else if (fld != BL_FLUSH) { + /* set up a specific field other than 'condition' */ + text = (char *) ptr; + if (fld == BL_GOLD) + text = decode_mixed(goldbuf, text); + xtra_space[fld] = 0; + if (status_activefields[fld]) { + fmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" + : status_fieldfmt[fld] ? status_fieldfmt[fld] : "%s"; + if (*fmt == ' ') { + ++xtra_space[fld]; + ++fmt; + } + Sprintf(status_vals[fld], fmt, text); } else { - label = X11_status_labels[fld]; - text = status_vals[fld]; - if (fld == BL_GOLD) - text = decode_mixed(tmpbuf, text); -#ifdef TEXTCOLOR - coloridx = X11_status_colors[fld] & 0x00FF; + /* don't expect this since core won't call status_update() + for a field which isn't active */ + *status_vals[fld] = '\0'; + } +#ifndef TEXTCOLOR + /* even without color, attribute(s) bits still apply */ + color = (color & ~0x00ff) | NO_COLOR; #endif - attridx = (X11_status_colors[fld] & 0xFF00) >> 8; +#ifdef STATUS_HILITES + if (!iflags.hilite_delta) + color = NO_COLOR; +#endif + X11_status_colors[fld] = color; + if (iflags.wc2_hitpointbar && fld == BL_HP) { + hpbar_percent = percent; + hpbar_color = color; + } - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNfont), &font); num_args++; - XtSetArg(args[num_args], nhStr(XtNx), &lbl_x); num_args++; - XtSetArg(args[num_args], nhStr(XtNy), &lbl_y); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++; - XtSetArg(args[num_args], nhStr(XtNheight), &lbl_hei); num_args++; - XtSetArg(args[num_args], nhStr(XtNinternalWidth), - &lbl_iwidth); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), - &lbl_border_wid); num_args++; - XtGetValues(label, args, num_args); + label = X11_status_labels[fld]; + text = status_vals[fld]; + PrepStatusField(fld, label, text); + return; + } - /*raw_printf("font: %i-%i", - font->min_bounds.width, font->max_bounds.width);*/ + /* + * BL_FLUSH: draw all the status fields. + */ + si = xw_status_win->Win_info.Status_info; /* for cached geometry */ - if (text && *text) - lbl_wid = lbl_iwidth + font->max_bounds.width * strlen(text); - else - lbl_wid = 1; + for (y = 0; y < X11_NUM_STATUS_LINES; y++) { /* row */ + dx = 0; /* no pixels written to this row yet */ - /*raw_printf("1:lbl_wid=%i('%s')", lbl_wid, text);*/ + for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { /* 'column' */ + f = X11_fieldorder[y][x]; + if (f <= BL_FLUSH) + continue; /* skip padding in the fieldorder[][] layout */ - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNlabel), - (text && *text) ? text : ""); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++; - - fg = (coloridx != NO_COLOR) - ? get_nhcolor(xw_status_win, coloridx).pixel - : X11_status_widget_fg; - if (attridx & HL_INVERSE) { - Pixel tmppx = fg; - - fg = bg; - bg = tmppx; + if (f == BL_CONDITION) { + if (next_cond_indx > 0 || prev_cond_indx > 0) + dx = render_conditions(y, dx); + continue; } - if (attridx & HL_BOLD) { - load_boldfont(xw_status_win, label); - XtSetArg(args[num_args], nhStr(XtNfont), - xw_status_win->boldfs); num_args++; - } + label = X11_status_labels[f]; + text = status_vals[f]; - if (fg == bg) - fg = get_nhcolor(xw_status_win, CLR_GRAY).pixel; + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++; + XtGetValues(label, args, num_args); + brd_wid = si->brd; - XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++; - XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++; - XtSetValues(label, args, num_args); - XtResizeWidget(label, lbl_wid, lbl_hei, lbl_border_wid); - } - } else { - int x, y; + /* for a field which shouldn't be shown, we can't just skip + it because we might need to remove its previous content + if it has just been toggled off */ + if (!status_activefields[f]) { + if (lbl_wid <= 1) + continue; + text = ""; + lbl_wid = (Dimension) 1; + brd_wid = (Dimension) 0; + } else if (xtra_space[f]) { + /* if this field was to be formatted with a leading space + to separate it from the preceding field, we suppressed + that space during formatting; insert separation between + fields here; this prevents inverse video highlighting + from inverting the separating space along with the text */ + dx += xtra_space[f] * si->spacew; + } - for (y = 0; y < X11_NUM_STATUS_LINES; y++) { - Cardinal dx = 0; + /* where to display the current widget */ + lbl_x = (Position) (dx + 1); - for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { - int f = X11_fieldorder[y][x]; + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++; + /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/ + /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/ + XtSetValues(label, args, num_args); + XtConfigureWidget(label, lbl_x, si->y[y], + lbl_wid, si->ht, brd_wid); - if (f <= BL_FLUSH) - continue; - if (!status_activefields[f]) - continue; + /* track the right-most pixel written so far */ + dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1; + } /* [x] fields/columns in current row */ + } /* [y] rows */ - if (f == BL_CONDITION) { - int i; - - for (i = 0; i < 32; i++) { - label = X11_cond_labels[i]; - - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNx), - &lbl_x); num_args++; - XtSetArg(args[num_args], nhStr(XtNy), - &lbl_y); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), - &lbl_wid); num_args++; - XtSetArg(args[num_args], nhStr(XtNheight), - &lbl_hei); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), - &lbl_border_wid); num_args++; - XtGetValues(label, args, num_args); - - lbl_x = dx; - - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNx), - lbl_x); num_args++; - XtSetValues(label, args, num_args); - XtConfigureWidget(label, lbl_x, lbl_y, - lbl_wid, lbl_hei, lbl_border_wid); - - if (lbl_wid > 1) - dx += lbl_wid; - } - continue; - } - label = X11_status_labels[f]; - - text = status_vals[f]; - if (f == BL_GOLD) - text = decode_mixed(tmpbuf, text); - - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNx), &lbl_x); num_args++; - XtSetArg(args[num_args], nhStr(XtNy), &lbl_y); num_args++; - XtSetArg(args[num_args], nhStr(XtNwidth), - &lbl_wid); num_args++; - XtSetArg(args[num_args], nhStr(XtNheight), - &lbl_hei); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), - &lbl_border_wid); num_args++; - XtGetValues(label, args, num_args); - - lbl_x = dx; - /*raw_printf("2:lbl_wid=%i('%s')", lbl_wid, text);*/ - - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++; - XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++; - XtSetValues(label, args, num_args); - XtConfigureWidget(label, lbl_x, lbl_y, - lbl_wid, lbl_hei, lbl_border_wid); - - dx += lbl_wid; - } - } - } + /* this probably doesn't buy us anything but it isn't a sure thing + that nethack will immediately ask for input (triggering auto-flush) */ + (void) XFlush(XtDisplay(X11_status_labels[0])); } /*ARGSUSED*/ -void +static void X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks) int fld, chg UNUSED, percent UNUSED, color UNUSED; genericptr_t ptr; unsigned long *colormasks UNUSED; { - static const struct { + static const struct bl_to_ff { int bl, ff; } bl_to_fancyfield[] = { { BL_TITLE, F_NAME }, @@ -547,30 +826,33 @@ unsigned long *colormasks UNUSED; { BL_GOLD, F_GOLD }, { BL_ENE, F_POWER }, { BL_ENEMAX, F_MAXPOWER }, - { BL_XP, F_LEVEL }, + { BL_XP, F_XP_LEVL }, /* shares with BL_HD, depending upon Upolyd */ { BL_AC, F_AC }, - /*{ BL_HD, F_ },*/ { BL_TIME, F_TIME }, { BL_HUNGER, F_HUNGER }, { BL_HP, F_HP }, { BL_HPMAX, F_MAXHP }, { BL_LEVELDESC, F_DLEVEL }, - { BL_EXP, F_EXP } + { BL_EXP, F_EXP_PTS } }; - static const struct { + static const struct mask_to_ff { unsigned long mask; int ff; } mask_to_fancyfield[] = { + { BL_MASK_GRAB, F_GRABBED }, { BL_MASK_STONE, F_STONE }, { BL_MASK_SLIME, F_SLIME }, { BL_MASK_STRNGL, F_STRNGL }, { BL_MASK_FOODPOIS, F_FOODPOIS }, { BL_MASK_TERMILL, F_TERMILL }, + { BL_MASK_INLAVA, F_IN_LAVA }, + { BL_MASK_HELD, F_HELD }, { BL_MASK_BLIND, F_BLIND }, { BL_MASK_DEAF, F_DEAF }, { BL_MASK_STUN, F_STUN }, { BL_MASK_CONF, F_CONF }, { BL_MASK_HALLU, F_HALLU }, + { BL_MASK_TRAPPED, F_TRAPPED }, { BL_MASK_LEV, F_LEV }, { BL_MASK_FLY, F_FLY }, { BL_MASK_RIDE, F_RIDE } @@ -579,23 +861,29 @@ unsigned long *colormasks UNUSED; if (fld == BL_RESET || fld == BL_FLUSH) { if (WIN_STATUS != WIN_ERR) { - struct xwindow *wp = &window_list[WIN_STATUS]; - - update_fancy_status(wp); + update_fancy_status(FALSE); } return; } if (fld == BL_CONDITION) { - unsigned long mask = (unsigned long) ptr; - - for (i = 0; i < SIZE(mask_to_fancyfield); i++) - if (mask_to_fancyfield[i].mask == mask) - update_fancy_status_field(mask_to_fancyfield[i].ff); + unsigned long changed_bits, *condptr = (unsigned long *) ptr; + + X11_condition_bits = *condptr; + /* process the bits that are different from last time */ + changed_bits = (X11_condition_bits ^ old_condition_bits); + if (changed_bits) { + for (i = 0; i < SIZE(mask_to_fancyfield); i++) + if ((changed_bits & mask_to_fancyfield[i].mask) != 0L) + update_fancy_status_field(mask_to_fancyfield[i].ff); + old_condition_bits = X11_condition_bits; + } } else { for (i = 0; i < SIZE(bl_to_fancyfield); i++) - if (bl_to_fancyfield[i].bl == fld) + if (bl_to_fancyfield[i].bl == fld) { update_fancy_status_field(bl_to_fancyfield[i].ff); + break; + } } } @@ -605,26 +893,72 @@ int fld, chg UNUSED, percent UNUSED, color; genericptr_t ptr; unsigned long *colormasks; { + if (fld < BL_RESET || fld >= MAXBLSTATS) + panic("X11_status_update(%d) -- invalid field", fld); + if (appResources.fancy_status) X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks); else X11_status_update_tty(fld, ptr, chg, percent, color, colormasks); } -Widget +/* create a widget for a particular status field or potential condition */ +static Widget +create_tty_status_field(fld, condindx, above, left) +int fld, condindx; +Widget above, left; +{ + Arg args[16]; + Cardinal num_args; + char labelname[40]; + int gap = condindx ? 5 : 0; + + if (!condindx) + Sprintf(labelname, "label_%s", bl_idx_to_fldname(fld)); + else + Sprintf(labelname, "cond_%02d", condindx); + + /* set up widget attributes which (mostly) aren't going to be changing */ + (void) memset((genericptr_t) args, 0, sizeof args); + num_args = 0; + if (above) + XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++; + if (left) + XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++; + + XtSetArg(args[num_args], nhStr(XtNhorizDistance), gap); num_args++; + XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++; + + XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNbottomMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNleftMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + /* internalWidth: default is 4; cut that it half and adjust regular + width to have it on both left and right instead of just on the left */ + XtSetArg(args[num_args], nhStr(XtNinternalWidth), 2); num_args++; + XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++; + return XtCreateManagedWidget(labelname, labelWidgetClass, + X11_status_widget, args, num_args); +} + +/* create an overall status widget (X11_status_widget) and also + separate widgets for all status fields and potential conditions */ +static Widget create_tty_status(parent, top) Widget parent, top; { - Widget form; /* The form that surrounds everything. */ - Widget w; - Arg args[16]; + Widget form; /* viewport that holds the form that surrounds everything */ + Widget w, over_w, prev_w; + Arg args[6]; Cardinal num_args; - int i, x, y; + int x, y, i, fld; + (void) memset((genericptr_t) args, 0, sizeof args); num_args = 0; - if (top != (Widget) 0) { + if (top) XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++; - } XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; XtSetArg(args[num_args], XtNwidth, 400); num_args++; @@ -632,83 +966,37 @@ Widget parent, top; form = XtCreateManagedWidget("status_viewport", viewportWidgetClass, parent, args, num_args); + (void) memset((genericptr_t) args, 0, sizeof args); num_args = 0; XtSetArg(args[num_args], XtNwidth, 400); num_args++; XtSetArg(args[num_args], XtNheight, 100); num_args++; - w = XtCreateManagedWidget("status_form", formWidgetClass, - form, args, num_args); + X11_status_widget = XtCreateManagedWidget("status_form", formWidgetClass, + form, args, num_args); + for (y = 0; y < X11_NUM_STATUS_LINES; y++) { + fld = (y > 0) ? X11_fieldorder[y - 1][0] : 0; /* temp, for over_w */ + /* for row(s) beyond the first, pick a widget in the previous + row to put this one underneath (in 'y' terms; 'x' is fluid) */ + over_w = (y > 0) ? X11_status_labels[fld] : (Widget) 0; + /* widget on current row to put the next one to the right of ('x') */ + prev_w = (Widget) 0; for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { - int fld = X11_fieldorder[y][x]; - char labelname[BUFSZ]; - int prevfld; - + fld = X11_fieldorder[y][x]; /* next field to handle */ if (fld <= BL_FLUSH) - continue; - Sprintf(labelname, "label_%s", bl_idx_to_fldname(fld)); - num_args = 0; - if (y > 0) { - prevfld = X11_fieldorder[y-1][0]; - XtSetArg(args[num_args], nhStr(XtNfromVert), - X11_status_labels[prevfld]); num_args++; - } - if (x > 0) { - prevfld = X11_fieldorder[y][x-1]; - XtSetArg(args[num_args], nhStr(XtNfromHoriz), - X11_status_labels[prevfld]); num_args++; - } + continue; /* skip fieldorder[][] padding */ - XtSetArg(args[num_args], nhStr(XtNhorizDistance), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++; + w = create_tty_status_field(fld, 0, over_w, prev_w); + X11_status_labels[fld] = prev_w = w; - XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNbottomMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNleftMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNjustify), - XtJustifyLeft); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++; - /* - XtSetArg(args[num_args], nhStr(XtNlabel), - bl_idx_to_fldname(fld)); num_args++; - */ - XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++; - X11_status_labels[fld] = XtCreateManagedWidget(labelname, - labelWidgetClass, w, - args, num_args); + if (fld == BL_CONDITION) { + for (i = 1; i <= SIZE(X11_cond_labels); ++i) { /* 1..32 */ + w = create_tty_status_field(fld, i, over_w, prev_w); + X11_cond_labels[i - 1] = prev_w = w; + } + } } - } - - for (i = 0; i < 32; i++) { - char condname[BUFSZ]; - int prevfld; - - Sprintf(condname, "cond_%i", i); - num_args = 0; - - prevfld = X11_fieldorder[0][0]; - XtSetArg(args[num_args], nhStr(XtNfromVert), - X11_status_labels[prevfld]); num_args++; - - XtSetArg(args[num_args], nhStr(XtNfromHoriz), - (i == 0) ? X11_status_labels[BL_CONDITION] - : X11_cond_labels[i-1]); num_args++; - - XtSetArg(args[num_args], nhStr(XtNhorizDistance), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNbottomMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNleftMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; - XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++; - XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++; - X11_cond_labels[i] = XtCreateManagedWidget(condname, - labelWidgetClass, w, - args, num_args); } - - return w; + return X11_status_widget; } /*ARGSUSED*/ @@ -718,18 +1006,8 @@ struct xwindow *wp; /* window pointer */ boolean create_popup UNUSED; Widget parent; { - Arg args[10]; - Cardinal num_args; - wp->type = NHW_STATUS; - X11_status_widget = wp->w = create_tty_status(parent, (Widget) 0); - - num_args = 0; - XtSetArg(args[num_args], nhStr(XtNforeground), - &X11_status_widget_fg); num_args++; - XtSetArg(args[num_args], nhStr(XtNbackground), - &X11_status_widget_bg); num_args++; - XtGetValues(X11_status_widget, args, num_args); + wp->w = create_tty_status(parent, (Widget) 0); } void @@ -759,6 +1037,7 @@ struct xwindow *wp UNUSED; const char *str UNUSED; { /* nothing */ + return; } void @@ -767,11 +1046,18 @@ struct xwindow *wp; /* window pointer */ boolean create_popup; Widget parent; { + struct status_info_t *si = (struct status_info_t *) alloc(sizeof *si); + xw_status_win = wp; - if (appResources.fancy_status) - create_status_window_fancy(wp, create_popup, parent); - else + if (wp->Win_info.Status_info) + free((genericptr_t) wp->Win_info.Status_info); + wp->Win_info.Status_info = si; + (void) memset((genericptr_t) si, 0, sizeof *si); + + if (!appResources.fancy_status) create_status_window_tty(wp, create_popup, parent); + else + create_status_window_fancy(wp, create_popup, parent); } void @@ -795,9 +1081,6 @@ const char *str; adjust_status_tty(wp, str); } -extern const char *hu_stat[]; /* from eat.c */ -extern const char *enc_stat[]; /* from botl.c */ - void create_status_window_fancy(wp, create_popup, parent) struct xwindow *wp; /* window pointer */ @@ -865,7 +1148,8 @@ Widget parent; XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin); num_args++; XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin); num_args++; - XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin); num_args++; + XtSetArg(args[num_args], nhStr(XtNrightMargin), + &right_margin); num_args++; XtGetValues(wp->w, args, num_args); wp->pixel_height = 2 * nhFontHeight(wp->w) + top_margin + bottom_margin; @@ -914,7 +1198,7 @@ const char *str; Cardinal num_args; if (!wp->status_information) { - update_fancy_status(wp); + update_fancy_status(TRUE); return; } @@ -932,8 +1216,7 @@ const char *str; XtSetValues(wp->w, args, num_args); } -/* Fancy Status - * -------------------------------------------------------------*/ +/* Fancy ================================================================== */ static int hilight_time = 1; /* number of turns to hilight a changed value */ struct X_status_value { @@ -950,7 +1233,7 @@ struct X_status_value { /* valid type values */ #define SV_VALUE 0 /* displays a label:value pair */ #define SV_LABEL 1 /* displays a changable label */ -#define SV_NAME 2 /* displays an unchangeable name */ +#define SV_NAME 2 /* displays an unchangeable name */ static void FDECL(hilight_label, (Widget)); static void FDECL(update_val, (struct X_status_value *, long)); @@ -959,7 +1242,7 @@ static void FDECL(create_widget, (Widget, struct X_status_value *, int)); static void FDECL(get_widths, (struct X_status_value *, int *, int *)); static void FDECL(set_widths, (struct X_status_value *, int, int)); static Widget FDECL(init_column, (const char *, Widget, Widget, Widget, - int *)); + int *, int)); static void NDECL(fixup_cond_widths); static Widget FDECL(init_info_form, (Widget, Widget, Widget)); @@ -968,50 +1251,60 @@ static Widget FDECL(init_info_form, (Widget, Widget, Widget)); * + Alignment needs a different init value, because -1 is an alignment. * + Armor Class is an schar, so 256 is out of range. * + Blank value is 0 and should never change. + * + * - These must be in the same order as the F_foo numbers. */ static struct X_status_value shown_stats[NUM_STATS] = { - { "", SV_NAME, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0*/ + { "", SV_NAME, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0, F_DUMMY */ - { "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 1*/ { "Dexterity", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Constitution", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Intelligence", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Wisdom", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 5*/ { "Charisma", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, - { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* name */ - { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* dlvl */ + { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* 7, F_NAME */ + { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* F_DLEVEL */ { "Gold", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Hit Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*10*/ { "Max HP", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Max Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Armor Class", SV_VALUE, (Widget) 0, 256, 0, FALSE, FALSE }, - { "Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/ - { "Experience", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Xp Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/ + /*{ "Hit Dice", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },==15*/ + { "Exp Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Alignment", SV_VALUE, (Widget) 0, -2, 0, FALSE, FALSE }, { "Time", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, { "Score", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, - { "Petrifying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*20*/ + { "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /*20, F_HUNGER */ + { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* F_ENCUMBER */ + { "Trapped", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Levitating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Flying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Riding", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*25, F_RIDE */ + + { "Grabbed!", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*26, F_GRAB */ + { "Petrifying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* F_STONE */ { "Slimed", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, { "Strangled", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, - { "Food Pois", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Food Pois", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*30*/ { "Term Ill", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Sinking", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* 32, F_IN_LAVA */ - { "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /*25*/ /* hunger */ - { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*encumbr */ - { "Levitating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, - { "Flying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, - { "Riding", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, - - { "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*30*/ - { "Deaf", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Held", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*33*/ + { "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Deaf", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*35*/ { "Stunned", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, { "Confused", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, - { "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*38*/ }; +extern const char *hu_stat[]; /* from eat.c */ +extern const char *enc_stat[]; /* from botl.c */ + /* * Set all widget values to a null string. This is used after all spacings * have been calculated so that when the window is popped up we don't get all @@ -1043,13 +1336,18 @@ null_out_status() } } -/* This is almost an exact duplicate of hilight_value() */ +/* this is almost an exact duplicate of hilight_value() */ static void hilight_label(w) Widget w; /* label widget */ { Arg args[2]; Pixel fg, bg; + /* + * This predates STATUS_HILITES. + * It is used to show any changed item in inverse and gets + * reset on the next turn. + */ XtSetArg(args[0], XtNforeground, &fg); XtSetArg(args[1], XtNbackground, &bg); @@ -1065,6 +1363,8 @@ update_val(attr_rec, new_value) struct X_status_value *attr_rec; long new_value; { + static boolean Exp_shown = TRUE, time_shown = TRUE, score_shown = TRUE, + Xp_was_HD = FALSE; char buf[BUFSZ]; Arg args[4]; @@ -1113,7 +1413,7 @@ long new_value; attr_rec->last_value = new_value; - /* special cases: hunger, encumbrance, sickness */ + /* special cases: hunger and encumbrance */ if (attr_rec == &shown_stats[F_HUNGER]) { XtSetArg(args[0], XtNlabel, hu_stat[new_value]); } else if (attr_rec == &shown_stats[F_ENCUMBER]) { @@ -1130,90 +1430,66 @@ long new_value; /* special case: time can be enabled & disabled */ if (attr_rec == &shown_stats[F_TIME]) { - static boolean flagtime = TRUE; - - if (flags.time && !flagtime) { + if (flags.time && !time_shown) { set_name(attr_rec->w, shown_stats[F_TIME].name); force_update = TRUE; - flagtime = flags.time; - } else if (!flags.time && flagtime) { + time_shown = TRUE; + } else if (!flags.time && time_shown) { set_name(attr_rec->w, ""); set_value(attr_rec->w, ""); - flagtime = flags.time; + time_shown = FALSE; } - if (!flagtime) + if (!time_shown) return; - /* special case: exp can be enabled & disabled */ - } else if (attr_rec == &shown_stats[F_EXP]) { - static boolean flagexp = TRUE; + /* special case: experience points can be enabled & disabled */ + } else if (attr_rec == &shown_stats[F_EXP_PTS]) { + boolean showexp = flags.showexp && !Upolyd; - if (flags.showexp && !flagexp) { - set_name(attr_rec->w, shown_stats[F_EXP].name); + if (showexp && !Exp_shown) { + set_name(attr_rec->w, shown_stats[F_EXP_PTS].name); force_update = TRUE; - flagexp = flags.showexp; - } else if (!flags.showexp && flagexp) { + Exp_shown = TRUE; + } else if (!showexp && Exp_shown) { set_name(attr_rec->w, ""); set_value(attr_rec->w, ""); - flagexp = flags.showexp; + Exp_shown = FALSE; } - if (!flagexp) + if (!Exp_shown) return; - } - /* special case: score can be enabled & disabled */ - else if (attr_rec == &shown_stats[F_SCORE]) { - static boolean flagscore = TRUE; + /* special case: when available, score can be enabled & disabled */ + } else if (attr_rec == &shown_stats[F_SCORE]) { #ifdef SCORE_ON_BOTL - - if (flags.showscore && !flagscore) { + if (flags.showscore && !score_shown) { set_name(attr_rec->w, shown_stats[F_SCORE].name); force_update = TRUE; - flagscore = flags.showscore; - } else if (!flags.showscore && flagscore) { + score_shown = TRUE; + } else +#endif + if (!flags.showscore && score_shown) { set_name(attr_rec->w, ""); set_value(attr_rec->w, ""); - flagscore = flags.showscore; + score_shown = FALSE; } - if (!flagscore) + if (!score_shown) return; -#else - if (flagscore) { - set_name(attr_rec->w, ""); - set_value(attr_rec->w, ""); - flagscore = FALSE; - } - return; -#endif - /* special case: when polymorphed, show "HD", disable exp */ - } else if (attr_rec == &shown_stats[F_LEVEL]) { - static boolean lev_was_poly = FALSE; - - if (Upolyd && !lev_was_poly) { - force_update = TRUE; - set_name(attr_rec->w, "HD"); - lev_was_poly = TRUE; - } else if (Upolyd && lev_was_poly) { - force_update = TRUE; - set_name(attr_rec->w, shown_stats[F_LEVEL].name); - lev_was_poly = FALSE; - } - } else if (attr_rec == &shown_stats[F_EXP]) { - static boolean exp_was_poly = FALSE; - - if (Upolyd && !exp_was_poly) { + /* special case: when polymorphed, show "Hit Dice" and disable Exp */ + } else if (attr_rec == &shown_stats[F_XP_LEVL]) { + if (Upolyd && !Xp_was_HD) { force_update = TRUE; - set_name(attr_rec->w, ""); - set_value(attr_rec->w, ""); - exp_was_poly = TRUE; - } else if (Upolyd && exp_was_poly) { + set_name(attr_rec->w, "Hit Dice"); + Xp_was_HD = TRUE; + } else if (!Upolyd && Xp_was_HD) { force_update = TRUE; - set_name(attr_rec->w, shown_stats[F_EXP].name); - exp_was_poly = FALSE; + set_name(attr_rec->w, shown_stats[F_XP_LEVL].name); + Xp_was_HD = FALSE; } - if (Upolyd) - return; /* no display for exp when poly */ + /* core won't call status_update() for Exp when it hasn't changed + so do so ourselves (to get Exp_shown flag to match display) */ + if (force_update) + update_fancy_status_field(F_EXP_PTS); } if (attr_rec->last_value == new_value && !force_update) /* same */ @@ -1221,23 +1497,34 @@ long new_value; attr_rec->last_value = new_value; - /* Special cases: strength, alignment and "clear". */ - if (attr_rec == &shown_stats[F_STR]) { - if (new_value > 18) { - if (new_value > 118) - Sprintf(buf, "%ld", new_value - 100); - else if (new_value < 118) - Sprintf(buf, "18/%02ld", new_value - 18); + /* Special cases: strength and other characteristics, alignment + and "clear". */ + if (attr_rec >= &shown_stats[F_STR] + && attr_rec <= &shown_stats[F_CHA]) { + static const char fmt1[] = "%ld%s", fmt2[] = "%2ld%s"; + struct xwindow *wp; + const char *fmt = fmt1, *padding = ""; + + /* for full-fledged fancy status, force two digits for all + six characteristics, followed by three spaces of padding + to match "/xx" exceptional strength */ + wp = (WIN_STATUS != WIN_ERR) ? &window_list[WIN_STATUS] : 0; + if (wp && !wp->status_information) + fmt = fmt2, padding = " "; + + if (new_value > 18L && attr_rec == &shown_stats[F_STR]) { + if (new_value > 118L) /* 19..25 encoded as 119..125 */ + Sprintf(buf, fmt, new_value - 100L, padding); + else if (new_value < 118L) /* 18/01..18/99 as 19..117*/ + Sprintf(buf, "18/%02ld", new_value - 18L); else - Strcpy(buf, "18/**"); - } else { - Sprintf(buf, "%ld", new_value); + Strcpy(buf, "18/**"); /* 18/100 encoded as 118 */ + } else { /* non-strength or less than 18/01 strength (3..18) */ + Sprintf(buf, fmt, new_value, padding); /* 3..25 */ } } else if (attr_rec == &shown_stats[F_ALIGN]) { - Strcpy(buf, - (new_value == A_CHAOTIC) - ? "Chaotic" - : (new_value == A_NEUTRAL) ? "Neutral" : "Lawful"); + Strcpy(buf, (new_value == A_CHAOTIC) ? "Chaotic" + : (new_value == A_NEUTRAL) ? "Neutral" : "Lawful"); } else { Sprintf(buf, "%ld", new_value); } @@ -1271,7 +1558,7 @@ long new_value; * the other. So only do our update when we update the second line. * * Information on the first line: - * name, attributes, alignment, score + * name, characteristics, alignment, score * * Information on the second line: * dlvl, gold, hp, power, ac, {level & exp or HD **}, time, @@ -1287,7 +1574,8 @@ update_fancy_status_field(i) int i; { struct X_status_value *sv = &shown_stats[i]; - long val; + unsigned long condmask = 0L; + long val = 0L; switch (i) { case F_DUMMY: @@ -1312,7 +1600,7 @@ int i; val = (long) ACURR(A_CHA); break; /* - * Label stats. With the exceptions of hunger, encumbrance, sick + * Label stats. With the exceptions of hunger and encumbrance * these are either on or off. Pleae leave the ternary operators * the way they are. I want to specify 0 or 1, not a boolean. */ @@ -1322,54 +1610,66 @@ int i; case F_ENCUMBER: val = (long) near_capacity(); break; + + case F_TRAPPED: + condmask = BL_MASK_TRAPPED; + break; case F_LEV: - val = Levitation ? 1L : 0L; + condmask = BL_MASK_LEV; break; case F_FLY: - val = Flying ? 1L : 0L; + condmask = BL_MASK_FLY; break; case F_RIDE: - val = u.usteed ? 1L : 0L; + condmask = BL_MASK_RIDE; break; /* fatal status conditions */ + case F_GRABBED: + condmask = BL_MASK_GRAB; + break; case F_STONE: - val = Stoned ? 1L : 0L; + condmask = BL_MASK_STONE; break; case F_SLIME: - val = Slimed ? 1L : 0L; + condmask = BL_MASK_SLIME; break; case F_STRNGL: - val = Strangled ? 1L : 0L; + condmask = BL_MASK_STRNGL; break; case F_FOODPOIS: - val = (Sick && (u.usick_type & SICK_VOMITABLE)) ? 1L : 0L; + condmask = BL_MASK_FOODPOIS; break; case F_TERMILL: - val = (Sick && (u.usick_type & SICK_NONVOMITABLE)) ? 1L : 0L; + condmask = BL_MASK_TERMILL; + break; + case F_IN_LAVA: + condmask = BL_MASK_INLAVA; break; /* non-fatal status conditions */ + case F_HELD: + condmask = BL_MASK_HELD; + break; case F_BLIND: - val = Blind ? 1L : 0L; + condmask = BL_MASK_BLIND; break; case F_DEAF: - val = Deaf ? 1L : 0L; + condmask = BL_MASK_DEAF; break; case F_STUN: - val = Stunned ? 1L : 0L; + condmask = BL_MASK_STUN; break; case F_CONF: - val = Confusion ? 1L : 0L; + condmask = BL_MASK_CONF; break; case F_HALLU: - val = Hallucination ? 1L : 0L; + condmask = BL_MASK_HALLU; break; case F_NAME: - val = (long) 0L; - break; /* special */ case F_DLEVEL: val = (long) 0L; break; /* special */ + case F_GOLD: val = money_cnt(g.invent); if (val < 0L) @@ -1391,10 +1691,10 @@ int i; case F_AC: val = (long) u.uac; break; - case F_LEVEL: + case F_XP_LEVL: val = (long) (Upolyd ? mons[u.umonnum].mlevel : u.ulevel); break; - case F_EXP: + case F_EXP_PTS: val = flags.showexp ? u.uexp : 0L; break; case F_ALIGN: @@ -1430,24 +1730,42 @@ int i; break; } /* default */ } /* switch */ + + if (condmask) + val = ((X11_condition_bits & condmask) != 0L); update_val(sv, val); } -/*ARGUSED*/ +/* fully update status after bl_flush or window resize */ static void -update_fancy_status(wp) -struct xwindow *wp UNUSED; +update_fancy_status(force_update) +boolean force_update; { + static boolean old_showtime, old_showexp, old_showscore; + static int old_upolyd = -1; /* -1: force first time update */ int i; - /*if (wp->cursy != 0) - return;*/ /* do a complete update when line 0 is done */ - - for (i = 0; i < NUM_STATS; i++) - update_fancy_status_field(i); + if (force_update + || Upolyd != old_upolyd /* Xp vs HD */ + || flags.time != old_showtime + || flags.showexp != old_showexp + || flags.showscore != old_showscore) { + /* update everything; usually only need this on the very first + time, then later if the window gets resized or if poly/unpoly + triggers Xp <-> HD switch or if an optional field gets + toggled off since there won't be a status_update() call for + the no longer displayed field; we're a bit more conservative + than that and do this when toggling on as well as off */ + for (i = 0; i < NUM_STATS; i++) + update_fancy_status_field(i); + + old_upolyd = Upolyd; + old_showtime = flags.time; + old_showexp = flags.showexp; + old_showscore = flags.showscore; + } } - /* * Turn off hilighted status values after a certain amount of turns. */ @@ -1462,6 +1780,7 @@ check_turn_events() continue; if (sv->turn_count++ >= hilight_time) { + /* unhighlights by toggling a highlit item back off again */ if (sv->type == SV_LABEL) hilight_label(sv->w); else @@ -1471,10 +1790,9 @@ check_turn_events() } } -/* Initialize alternate status ============================================= - */ +/* Initialize alternate status ============================================ */ -/* Return a string for the initial width. */ +/* Return a string for the initial width, so use longest possible value. */ static const char * width_string(sv_index) int sv_index; @@ -1500,11 +1818,15 @@ int sv_index; case F_LEV: case F_FLY: case F_RIDE: + case F_TRAPPED: + case F_GRABBED: case F_STONE: case F_SLIME: case F_STRNGL: case F_FOODPOIS: case F_TERMILL: + case F_IN_LAVA: + case F_HELD: case F_BLIND: case F_DEAF: case F_STUN: @@ -1514,7 +1836,8 @@ int sv_index; case F_NAME: case F_DLEVEL: - return ""; + return ""; /* longest possible value not needed for these */ + case F_HP: case F_MAXHP: return "9999"; @@ -1523,12 +1846,12 @@ int sv_index; return "9999"; case F_AC: return "-127"; - case F_LEVEL: + case F_XP_LEVL: return "99"; case F_GOLD: /* strongest hero can pick up roughly 30% of this much */ return "999999"; /* same limit as tty */ - case F_EXP: + case F_EXP_PTS: case F_TIME: case F_SCORE: return "123456789"; /* a tenth digit will still fit legibly */ @@ -1632,10 +1955,10 @@ int width1, width2; } static Widget -init_column(name, parent, top, left, col_indices) +init_column(name, parent, top, left, col_indices, xtrawidth) const char *name; Widget parent, top, left; -int *col_indices; +int *col_indices, xtrawidth; { Widget form; Arg args[4]; @@ -1651,7 +1974,8 @@ int *col_indices; if (left != (Widget) 0) { XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++; } - XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; + /* this was 0 but that resulted in the text being crammed together */ + XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++; form = XtCreateManagedWidget(name, formWidgetClass, parent, args, num_args); @@ -1671,6 +1995,10 @@ int *col_indices; if (width2 > max_width2) max_width2 = width2; } + + /* insert some extra spacing between columns */ + max_width1 += xtrawidth; + for (ip = col_indices; *ip >= 0; ip++) { set_widths(&shown_stats[*ip], max_width1, max_width2); } @@ -1686,48 +2014,74 @@ int *col_indices; * These are the orders of the displayed columns. Change to suit. The -1 * indicates the end of the column. The two numbers after that are used * to store widths that are calculated at run-time. + * + * 3.7: changed so that all 6 columns have 8 rows, but a few entries + * are left blank <>. Exp-points, Score, and Time are optional depending + * on run-time settings; Xp-level is replaced by Hit-Dice (and Exp-points + * suppressed) when the hero is polymorphed. Title and Dungeon-Level span + * two columns and might expand to more if 'hitpointbar' is implemented. + * + Title ("Plname the Rank") <> <> <> <> + Dungeon-Branch-and-Level <> Hunger Grabbed Held + Hit-points Max-HP Strength Encumbrance Petrifying Blind + Power-points Max-Power Dexterity Trapped Slimed Deaf + Armor-class Alignment Constitution Levitation Strangled Stunned + Xp-level [Exp-points] Intelligence Flying Food-Pois Confused + Gold [Score] Wisdom Riding Term-Ill Hallucinatg + <> [Time] Charisma <> Sinking <> + * + * A seventh column is going to be needed to fit in more conditions. + * + * Possible enhancement: if Exp-points and Score are both disabled, move + * Gold to the Exp-points slot. */ -static int attrib_indices[] = { F_STR, F_DEX, F_CON, F_INT, F_WIS, F_CHA, - -1, 0, 0 }; + /* including F_DUMMY makes the three status condition columns evenly spaced with regard to the adjacent characteristics (Str,Dex,&c) column; - we lose track of the Widget pointer for them, each use clobbering the + we lose track of the Widget pointer for F_DUMMY, each use clobbering the one before, leaving the one from leftover_indices[]; since they're never updated, that shouldn't matter */ -static int status_indices[3][9] = { { F_STONE, F_SLIME, F_STRNGL, - F_FOODPOIS, F_TERMILL, F_DUMMY, - -1, 0, 0 }, - { F_HUNGER, F_ENCUMBER, - F_LEV, F_FLY, F_RIDE, F_DUMMY, - -1, 0, 0 }, - { F_BLIND, F_DEAF, F_STUN, - F_CONF, F_HALLU, F_DUMMY, - -1, 0, 0 } }; +static int status_indices[3][11] = { + { F_DUMMY, F_HUNGER, F_ENCUMBER, F_TRAPPED, + F_LEV, F_FLY, F_RIDE, F_DUMMY, -1, 0, 0 }, + { F_DUMMY, F_GRABBED, F_STONE, F_SLIME, F_STRNGL, + F_FOODPOIS, F_TERMILL, F_IN_LAVA, -1, 0, 0 }, + { F_DUMMY, F_HELD, F_BLIND, F_DEAF, F_STUN, + F_CONF, F_HALLU, F_DUMMY, -1, 0, 0 }, +}; /* used to fill up the empty space to right of 3rd status condition column */ static int leftover_indices[] = { F_DUMMY, -1, 0, 0 }; +/* -2: top two rows of these columns are reserved for title and location */ +static int col1_indices[11 - 2] = { + F_HP, F_POWER, F_AC, F_XP_LEVL, F_GOLD, F_DUMMY, -1, 0, 0 +}; +static int col2_indices[11 - 2] = { + F_MAXHP, F_MAXPOWER, F_ALIGN, F_EXP_PTS, F_SCORE, F_TIME, -1, 0, 0 +}; +static int characteristics_indices[11 - 2] = { + F_STR, F_DEX, F_CON, F_INT, F_WIS, F_CHA, -1, 0, 0 +}; -static int col1_indices[] = { F_HP, F_POWER, F_AC, F_LEVEL, F_GOLD, - F_SCORE, -1, 0, 0 }; -static int col2_indices[] = { F_MAXHP, F_MAXPOWER, F_ALIGN, F_EXP, F_TIME, - -1, 0, 0 }; /* * Produce a form that looks like the following: * - * name - * dlevel - * col1_indices[0] col2_indices[0] - * col1_indices[1] col2_indices[1] - * . . - * . . - * col1_indices[n] col2_indices[n] + * title + * location + * col1_indices[0] col2_indices[0] col3_indices[0] + * col1_indices[1] col2_indices[1] col3_indices[1] + * ... ... ... + * col1_indices[5] col2_indices[5] col3_indices[5] * - * TODO: increase the space between the two columns. + * The status conditions are managed separately and appear to the right + * of this form. + * + * TODO: widen title field and implement hitpoint bar on it. */ static Widget init_info_form(parent, top, left) Widget parent, top, left; { - Widget form, col1; + Widget form, col1, col2; struct X_status_value *sv_name, *sv_dlevel; Arg args[6]; Cardinal num_args; @@ -1740,26 +2094,29 @@ Widget parent, top, left; if (left != (Widget) 0) { XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++; } - XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++; form = XtCreateManagedWidget("status_info", formWidgetClass, parent, args, num_args); - /* top of form */ - sv_name = &shown_stats[F_NAME]; + /* top line/row of form */ + sv_name = &shown_stats[F_NAME]; /* title */ create_widget(form, sv_name, F_NAME); - /* second */ - sv_dlevel = &shown_stats[F_DLEVEL]; + /* second line/row */ + sv_dlevel = &shown_stats[F_DLEVEL]; /* location */ create_widget(form, sv_dlevel, F_DLEVEL); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), sv_name->w); num_args++; XtSetValues(sv_dlevel->w, args, num_args); - /* two columns beneath */ + /* there are 3 columns beneath but top 2 rows are centered over first 2 */ col1 = init_column("name_col1", form, sv_dlevel->w, (Widget) 0, - col1_indices); - (void) init_column("name_col2", form, sv_dlevel->w, col1, col2_indices); + col1_indices, 0); + col2 = init_column("name_col2", form, sv_dlevel->w, col1, + col2_indices, 5); + (void) init_column("status_characteristics", form, sv_dlevel->w, col2, + characteristics_indices, 15); /* Add calculated widths. */ for (ip = col1_indices; *ip >= 0; ip++) @@ -1806,9 +2163,9 @@ fixup_cond_widths() } /* ascetics: expand the maximum width to make cond columns wider */ if (pass == 1) { - w1 += 20; + w1 += 15; if (w2 > 0) - w2 += 20; + w2 += 15; } } } @@ -1832,20 +2189,24 @@ Widget parent, top; if (top != (Widget) 0) { XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++; } - XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; XtSetArg(args[num_args], XtNorientation, XtorientHorizontal); num_args++; form = XtCreateManagedWidget("fancy_status", panedWidgetClass, parent, args, num_args); w = init_info_form(form, (Widget) 0, (Widget) 0); - w = init_column("status_attributes", form, (Widget) 0, w, attrib_indices); +#if 0 /* moved to init_info_form() */ + w = init_column("status_characteristics", form, (Widget) 0, w, + characteristics_indices, 15); +#endif for (i = 0; i < 3; i++) { Sprintf(buf, "status_condition%d", i + 1); - w = init_column(buf, form, (Widget) 0, w, status_indices[i]); + w = init_column(buf, form, (Widget) 0, w, status_indices[i], 0); } fixup_cond_widths(); /* make all 3 status_conditionN columns same width */ - w = init_column("status_leftover", form, (Widget) 0, w, leftover_indices); + w = init_column("status_leftover", form, (Widget) 0, w, + leftover_indices, 0); nhUse(w); return form; }