]> granicus.if.org Git - nethack/commitdiff
X11 status overhaul
authorPatR <rankin@nethack.org>
Sat, 14 Mar 2020 10:47:27 +0000 (03:47 -0700)
committerPatR <rankin@nethack.org>
Sat, 14 Mar 2020 10:47:27 +0000 (03:47 -0700)
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

doc/fixes37.0
include/winX.h
win/X11/winX.c
win/X11/winstat.c

index 8e0d72f85861e4aa969d4ed24da41b10eb8ab476..8ebe6bb2d4667e07a150d4514c78a1175099879e 100644 (file)
@@ -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
index 56aafedd1365f8dffc01b073bc5805b4618255e6..fe7f63b56ff27a6e09580946db0631d803522ef3 100644 (file)
@@ -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);
index c7afa415f1ebdf63817b33aed426eb53f6e5975c..e2fff7c7baf1c1a79dcb38ccd8e93f195ba04751 100644 (file)
@@ -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 */
index 9dc5ac09cf2bf5dc22e77034b42f140ca32afda7..965d3859c159f9131efafaa15c640df5cb535476 100644 (file)
 #endif
 
 #include <X11/Intrinsic.h>
-#include <X11/IntrinsicP.h>
+#include <X11/IntrinsicP.h> /* for XtResizeWidget() and XtConfigureWidget() */
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #include <X11/Xaw/AsciiText.h>
-#include <X11/Xaw/Cardinals.h>
+#include <X11/Xaw/Cardinals.h> /* just for ONE, TWO */
 #include <X11/Xaw/Form.h>
 #include <X11/Xaw/Paned.h>
 #include <X11/Xaw/Label.h>
 #include <X11/Xaw/Viewport.h>
-#include <X11/Xatom.h>
+/*#include <X11/Xatom.h>*/
 
 #ifdef PRESERVE_NO_SYSV
 #ifdef SYSV
 #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  /* 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;
 }