OSDN Git Service

update year to 2020
[jnethack/source.git] / win / tty / wintty.c
index d94a0db..fc801db 100644 (file)
@@ -1,12 +1,11 @@
-/* NetHack 3.6 wintty.c        $NHDT-Date: 1449052583 2015/12/02 10:36:23 $  $NHDT-Branch: master $:$NHDT-Revision: 1.116 $ */
+/* NetHack 3.6 wintty.c        $NHDT-Date: 1575245194 2019/12/02 00:06:34 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.227 $ */
 /* Copyright (c) David Cohrs, 1991                                */
 /* NetHack may be freely redistributed.  See license for details. */
 
-/*
-**     Japanese version (by Issei Numata)
-**     For 3.4, Copyright (c) Kentaro Shirakata, 2002-2003
-**     JNetHack may be freely redistributed.  See license for details. 
-*/
+/* JNetHack Copyright */
+/* (c) Issei Numata 1994-2000                                      */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
+/* JNetHack may be freely redistributed.  See license for details. */
 
 /*
  * Neither a standard out nor character-based control codes should be
@@ -37,31 +36,74 @@ extern void msmsg(const char *, ...);
 #include "wintty.h"
 
 #ifdef CLIPPING /* might want SIGWINCH */
-#if defined(BSD) || defined(ULTRIX) || defined(AIX_31) \
-    || defined(_BULL_SOURCE)
+#if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
 #include <signal.h>
 #endif
 #endif
 
-extern char mapped_menu_cmds[]; /* from options.c */
+#ifdef TTY_TILES_ESCCODES
+extern short glyph2tile[];
+#define TILE_ANSI_COMMAND 'z'
+#define AVTC_GLYPH_START   0
+#define AVTC_GLYPH_END     1
+#define AVTC_SELECT_WINDOW 2
+#define AVTC_INLINE_SYNC   3
+#endif
 
-/* this is only needed until tty_status_* routines are written */
-extern NEARDATA winid WIN_STATUS;
+#ifdef HANGUP_HANDLING
+/*
+ * NetHack's core switches to a dummy windowing interface when it
+ * detects SIGHUP, but that's no help for any interface routine which
+ * is already in progress at the time, and there have been reports of
+ * runaway disconnected processes which use up all available CPU time.
+ * HUPSKIP() and HUPSKIP_RETURN(x) are used to try to cut them off so
+ * that they return to the core instead attempting more terminal I/O.
+ */
+#define HUPSKIP() \
+    do {                                        \
+        if (program_state.done_hup) {           \
+            morc = '\033';                      \
+            return;                             \
+        }                                       \
+    } while (0)
+    /* morc=ESC - in case we bypass xwaitforspace() which sets that */
+#define HUPSKIP_RESULT(RES) \
+    do {                                        \
+        if (program_state.done_hup)             \
+            return (RES);                       \
+    } while (0)
+#else /* !HANGUP_HANDLING */
+#define HUPSKIP() /*empty*/
+#define HUPSKIP_RESULT(RES) /*empty*/
+#endif /* ?HANGUP_HANDLING */
+
+extern char mapped_menu_cmds[]; /* from options.c */
 
 /* Interface definition, for windows.c */
 struct window_procs tty_procs = {
     "tty",
+    (0
 #ifdef MSDOS
-    WC_TILED_MAP | WC_ASCII_MAP |
+     | WC_TILED_MAP | WC_ASCII_MAP
 #endif
 #if defined(WIN32CON)
-        WC_MOUSE_SUPPORT |
+     | WC_MOUSE_SUPPORT
 #endif
-        WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN,
+     | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN),
+    (0
 #if defined(SELECTSAVED)
-    WC2_SELECTSAVED |
+     | WC2_SELECTSAVED
+#endif
+#if defined(STATUS_HILITES)
+     | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS
+     | WC2_RESET_STATUS
+#endif
+     | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES),
+#ifdef TEXTCOLOR
+    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
+#else
+    {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
 #endif
-        WC2_DARKGRAY,
     tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
     tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
     tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
@@ -88,24 +130,18 @@ struct window_procs tty_procs = {
 
     /* other defs that really should go away (they're tty specific) */
     tty_start_screen, tty_end_screen, genl_outrip,
-#if defined(WIN32)
-    nttty_preference_update,
-#else
-    genl_preference_update,
-#endif
+    tty_preference_update,
     tty_getmsghistory, tty_putmsghistory,
-#ifdef STATUS_VIA_WINDOWPORT
     tty_status_init,
-    genl_status_finish, genl_status_enablefield,
-    tty_status_update,
+    genl_status_finish, tty_status_enablefield,
 #ifdef STATUS_HILITES
-    tty_status_threshold,
-#endif
+    tty_status_update,
+#else
+    genl_status_update,
 #endif
     genl_can_suspend_yes,
 };
 
-static int maxwin = 0; /* number of windows in use */
 winid BASE_WINDOW;
 struct WinDesc *wins[MAXWIN];
 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
@@ -145,30 +181,45 @@ static const char to_continue[] = "to continue";
 #else
 STATIC_DCL void NDECL(getret);
 #endif
-STATIC_DCL void FDECL(erase_menu_or_text,
-                      (winid, struct WinDesc *, BOOLEAN_P));
+STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
+STATIC_DCL void NDECL(new_status_window);
+STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *,
+                                           BOOLEAN_P));
 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
-STATIC_DCL void FDECL(set_all_on_page,
-                      (winid, tty_menu_item *, tty_menu_item *));
-STATIC_DCL void FDECL(unset_all_on_page,
-                      (winid, tty_menu_item *, tty_menu_item *));
-STATIC_DCL void FDECL(invert_all_on_page,
-                      (winid, tty_menu_item *, tty_menu_item *, CHAR_P));
-STATIC_DCL void FDECL(invert_all,
-                      (winid, tty_menu_item *, tty_menu_item *, CHAR_P));
+STATIC_DCL void FDECL(set_all_on_page, (winid, tty_menu_item *,
+                                        tty_menu_item *));
+STATIC_DCL void FDECL(unset_all_on_page, (winid, tty_menu_item *,
+                                          tty_menu_item *));
+STATIC_DCL void FDECL(invert_all_on_page, (winid, tty_menu_item *,
+                                           tty_menu_item *, CHAR_P));
+STATIC_DCL void FDECL(invert_all, (winid, tty_menu_item *,
+                                   tty_menu_item *, CHAR_P));
+STATIC_DCL void FDECL(toggle_menu_attr, (BOOLEAN_P, int, int));
 STATIC_DCL void FDECL(process_menu_window, (winid, struct WinDesc *));
 STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *));
 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
 STATIC_DCL const char *FDECL(compress_str, (const char *));
 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
-STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
 STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int));
 STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int));
 STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int));
 STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int));
 STATIC_DCL boolean NDECL(reset_role_filtering);
+#ifdef STATUS_HILITES
+STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *));
+STATIC_DCL void NDECL(render_status);
+STATIC_DCL void FDECL(tty_putstatusfield, (const char *, int, int));
+STATIC_DCL boolean NDECL(check_windowdata);
+STATIC_DCL void NDECL(set_condition_length);
+STATIC_DCL int FDECL(make_things_fit, (BOOLEAN_P));
+STATIC_DCL void FDECL(shrink_enc, (int));
+STATIC_DCL void FDECL(shrink_dlvl, (int));
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
+STATIC_DCL void NDECL(status_sanity_check);
+#endif /* NH_DEVEL_STATUS */
+#endif
 
 /*
  * A string containing all the default commands -- to add to a list
@@ -181,6 +232,38 @@ static const char default_menu_cmds[] = {
     MENU_INVERT_PAGE,   MENU_SEARCH,      0 /* null terminator */
 };
 
+#ifdef TTY_TILES_ESCCODES
+static int vt_tile_current_window = -2;
+
+void
+print_vt_code(i, c, d)
+int i, c, d;
+{
+    HUPSKIP();
+    if (iflags.vt_tiledata) {
+        if (c >= 0) {
+            if (i == AVTC_SELECT_WINDOW) {
+                if (c == vt_tile_current_window)
+                    return;
+                vt_tile_current_window = c;
+            }
+            if (d >= 0)
+                printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND);
+            else
+                printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND);
+        } else {
+            printf("\033[1;%d%c", i, TILE_ANSI_COMMAND);
+        }
+    }
+}
+#else
+# define print_vt_code(i, c, d) ;
+#endif /* !TTY_TILES_ESCCODES */
+#define print_vt_code1(i)     print_vt_code((i), -1, -1)
+#define print_vt_code2(i,c)   print_vt_code((i), (c), -1)
+#define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
+
+
 /* clean up and quit */
 STATIC_OVL void
 bail(mesg)
@@ -188,13 +271,26 @@ const char *mesg;
 {
     clearlocks();
     tty_exit_nhwindows(mesg);
-    terminate(EXIT_SUCCESS);
+    nh_terminate(EXIT_SUCCESS);
     /*NOTREACHED*/
 }
 
 #if defined(SIGWINCH) && defined(CLIPPING)
+STATIC_DCL void FDECL(winch_handler, (int));
+
+    /*
+     * This really ought to just set a flag like the hangup handler does,
+     * then check the flag at "safe" times, in case the signal arrives
+     * while something fragile is executing.  Better to have a brief period
+     * where display updates don't fit the new size than for tty internals
+     * to become corrupted.
+     *
+     * 'winch_seen' has been "notyet" for a long time....
+     */
+/*ARGUSED*/
 STATIC_OVL void
-winch()
+winch_handler(sig_unused) /* signal handler is called with at least 1 arg */
+int sig_unused UNUSED;
 {
     int oldLI = LI, oldCO = CO, i;
     register struct WinDesc *cw;
@@ -230,19 +326,8 @@ winch()
             cw = wins[WIN_MESSAGE];
             cw->curx = cw->cury = 0;
 
-            tty_destroy_nhwindow(WIN_STATUS);
-            WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
-
+            new_status_window();
             if (u.ux) {
-#ifdef CLIPPING
-                if (CO < COLNO || LI < ROWNO + 3) {
-                    setclipped();
-                    tty_cliparound(u.ux, u.uy);
-                } else {
-                    clipping = FALSE;
-                    clipx = clipy = 0;
-                }
-#endif
                 i = ttyDisplay->toplin;
                 ttyDisplay->toplin = 0;
                 docrt();
@@ -267,6 +352,41 @@ winch()
 }
 #endif
 
+/* destroy and recreate status window; extracted from winch_handler()
+   and augmented for use by tty_preference_update() */
+STATIC_OVL void
+new_status_window()
+{
+    if (WIN_STATUS != WIN_ERR) {
+        /* if it's shrinking, clear it before destroying so that
+           dropped portion won't show anything that's now becoming stale */
+        if (wins[WIN_STATUS]->maxrow > iflags.wc2_statuslines)
+            tty_clear_nhwindow(WIN_STATUS);
+
+        tty_destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
+    }
+    /* frees some status tracking data */
+    genl_status_finish();
+    /* creates status window and allocates tracking data */
+    tty_status_init();
+    tty_clear_nhwindow(WIN_STATUS); /* does some init, sets context.botlx */
+#ifdef STATUS_HILITES
+    status_initialize(REASSESS_ONLY);
+#endif
+
+#ifdef CLIPPING
+    if (u.ux) {
+        if (LI < 1 + ROWNO + iflags.wc2_statuslines) {
+            setclipped();
+            tty_cliparound(u.ux, u.uy);
+        } else {
+            clipping = FALSE;
+            clipx = clipy = 0;
+        }
+    }
+#endif
+}
+
 /*ARGSUSED*/
 void
 tty_init_nhwindows(argcp, argv)
@@ -275,14 +395,17 @@ char **argv UNUSED;
 {
     int wid, hgt, i;
 
-/*
- *  Remember tty modes, to be restored on exit.
- *
- *  gettty() must be called before tty_startup()
- *    due to ordering of LI/CO settings
- *  tty_startup() must be called before initoptions()
- *    due to ordering of graphics settings
- */
+    /* options aren't processed yet so wc2_statuslines might be 0;
+       make sure that it has a reasonable value during tty setup */
+    iflags.wc2_statuslines = (iflags.wc2_statuslines < 3) ? 2 : 3;
+    /*
+     *  Remember tty modes, to be restored on exit.
+     *
+     *  gettty() must be called before tty_startup()
+     *    due to ordering of LI/CO settings
+     *  tty_startup() must be called before initoptions()
+     *    due to ordering of graphics settings
+     */
 #if defined(UNIX) || defined(VMS)
     setbuf(stdout, obuf);
 #endif
@@ -293,7 +416,7 @@ char **argv UNUSED;
     setftty(); /* calls start_screen */
 
     /* set up tty descriptor */
-    ttyDisplay = (struct DisplayDesc *) alloc(sizeof(struct DisplayDesc));
+    ttyDisplay = (struct DisplayDesc *) alloc(sizeof (struct DisplayDesc));
     ttyDisplay->toplin = 0;
     ttyDisplay->rows = hgt;
     ttyDisplay->cols = wid;
@@ -312,12 +435,9 @@ char **argv UNUSED;
     ttyDisplay->lastwin = WIN_ERR;
 
 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
-    (void) signal(SIGWINCH, winch);
+    (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler);
 #endif
 
-    /* add one a space forward menu command alias */
-    add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
-
     tty_clear_nhwindow(BASE_WINDOW);
 
     tty_putstr(BASE_WINDOW, 0, "");
@@ -332,6 +452,31 @@ char **argv UNUSED;
     tty_putstr(BASE_WINDOW, 0, "");
 #endif
     tty_display_nhwindow(BASE_WINDOW, FALSE);
+
+    /* 'statuslines' defaults to SET_IN_FILE, allowed but invisible;
+       make it dynamically settable if feasible, otherwise visible */
+    if (tty_procs.wincap2 & WC2_STATUSLINES)
+        set_wc2_option_mod_status(WC2_STATUSLINES,
+#ifndef CLIPPING
+                                  (LI < 1 + ROWNO + 2) ? DISP_IN_GAME :
+#endif
+                                   SET_IN_GAME);
+}
+
+void
+tty_preference_update(pref)
+const char *pref;
+{
+    if (!strcmp(pref, "statuslines") && iflags.window_inited) {
+        new_status_window();
+    }
+
+#if defined(WIN32)
+    nttty_preference_update(pref);
+#else
+    genl_preference_update(pref);
+#endif
+    return;
 }
 
 /* try to reduce clutter in the code below... */
@@ -344,14 +489,25 @@ void
 tty_player_selection()
 {
     int i, k, n, choice, nextpick;
-    boolean getconfirmation;
+    boolean getconfirmation, picksomething;
     char pick4u = 'n';
     char pbuf[QBUFSZ], plbuf[QBUFSZ];
     winid win;
     anything any;
     menu_item *selected = 0;
 
-    if (flags.randomall) {
+    /* Used to avoid "Is this ok?" if player has already specified all
+     * four facets of role.
+     * Note that rigid_role_checks might force any unspecified facets to
+     * have a specific value, but that will still require confirmation;
+     * player can specify the forced ones if avoiding that is demanded.
+     */
+    picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
+                     || GEND == ROLE_NONE || ALGN == ROLE_NONE);
+    /* Used for '-@';
+     * choose randomly without asking for all unspecified facets.
+     */
+    if (flags.randomall && picksomething) {
         if (ROLE == ROLE_NONE)
             ROLE = ROLE_RANDOM;
         if (RACE == ROLE_NONE)
@@ -362,7 +518,8 @@ tty_player_selection()
             ALGN = ROLE_RANDOM;
     }
 
-    /* prevent an unnecessary prompt */
+    /* prevent unnecessary prompting if role forces race (samurai) or gender
+       (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
     rigid_role_checks();
 
     /* Should we randomly pick for the player? */
@@ -403,7 +560,7 @@ tty_player_selection()
             goto give_up;
     }
 
-makepicks:
+ makepicks:
     nextpick = RS_ROLE;
     do {
         if (nextpick == RS_ROLE) {
@@ -418,7 +575,7 @@ makepicks:
                     k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
                     if (k < 0) {
                         tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
-                        k = randrole();
+                        k = randrole(FALSE);
                     }
                 } else {
                     /* Prompt for a role */
@@ -429,23 +586,38 @@ makepicks:
                     /* populate the menu with role choices */
                     setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
                     /* add miscellaneous menu entries */
-                    role_menu_extra(ROLE_RANDOM, win);
-                    any.a_int = 0; /* separator, not a choice */
-                    add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "",
+                    role_menu_extra(ROLE_RANDOM, win, TRUE);
+                    any = zeroany; /* separator, not a choice */
+                    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
                              MENU_UNSELECTED);
-                    role_menu_extra(RS_RACE, win);
-                    role_menu_extra(RS_GENDER, win);
-                    role_menu_extra(RS_ALGNMNT, win);
+                    role_menu_extra(RS_RACE, win, FALSE);
+                    role_menu_extra(RS_GENDER, win, FALSE);
+                    role_menu_extra(RS_ALGNMNT, win, FALSE);
                     if (gotrolefilter())
-                        role_menu_extra(RS_filter, win);
-                    role_menu_extra(ROLE_NONE, win); /* quit */
+                        role_menu_extra(RS_filter, win, FALSE);
+                    role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
 /*JP
                     Strcpy(pbuf, "Pick a role or profession");
 */
                     Strcpy(pbuf, "\90E\8bÆ\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
                     end_menu(win, pbuf);
                     n = select_menu(win, PICK_ONE, &selected);
-                    choice = (n == 1) ? selected[0].item.a_int : ROLE_NONE;
+                    /*
+                     * PICK_ONE with preselected choice behaves strangely:
+                     *  n == -1 -- <escape>, so use quit choice;
+                     *  n ==  0 -- explicitly chose preselected entry,
+                     *             toggling it off, so use it;
+                     *  n ==  1 -- implicitly chose preselected entry
+                     *             with <space> or <return>;
+                     *  n ==  2 -- explicitly chose a different entry, so
+                     *             both it and preselected one are in list.
+                     */
+                    if (n > 0) {
+                        choice = selected[0].item.a_int;
+                        if (n > 1 && choice == ROLE_RANDOM)
+                            choice = selected[1].item.a_int;
+                    } else
+                        choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
                     if (selected)
                         free((genericptr_t) selected), selected = 0;
                     destroy_nhwindow(win);
@@ -468,7 +640,7 @@ makepicks:
                     } else if (choice == ROLE_RANDOM) {
                         k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
                         if (k < 0)
-                            k = randrole();
+                            k = randrole(FALSE);
                     } else {
                         k = choice - 1;
                     }
@@ -516,24 +688,28 @@ makepicks:
                         /* populate the menu with role choices */
                         setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
                         /* add miscellaneous menu entries */
-                        role_menu_extra(ROLE_RANDOM, win);
+                        role_menu_extra(ROLE_RANDOM, win, TRUE);
                         any.a_int = 0; /* separator, not a choice */
-                        add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "",
+                        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
                                  MENU_UNSELECTED);
-                        role_menu_extra(RS_ROLE, win);
-                        role_menu_extra(RS_GENDER, win);
-                        role_menu_extra(RS_ALGNMNT, win);
+                        role_menu_extra(RS_ROLE, win, FALSE);
+                        role_menu_extra(RS_GENDER, win, FALSE);
+                        role_menu_extra(RS_ALGNMNT, win, FALSE);
                         if (gotrolefilter())
-                            role_menu_extra(RS_filter, win);
-                        role_menu_extra(ROLE_NONE, win); /* quit */
+                            role_menu_extra(RS_filter, win, FALSE);
+                        role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
 /*JP
                         Strcpy(pbuf, "Pick a race or species");
 */
                         Strcpy(pbuf, "\8eí\91°\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
                         end_menu(win, pbuf);
                         n = select_menu(win, PICK_ONE, &selected);
-                        choice = (n == 1) ? selected[0].item.a_int
-                                          : ROLE_NONE;
+                        if (n > 0) {
+                            choice = selected[0].item.a_int;
+                            if (n > 1 && choice == ROLE_RANDOM)
+                                choice = selected[1].item.a_int;
+                        } else
+                            choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
                         if (selected)
                             free((genericptr_t) selected), selected = 0;
                         destroy_nhwindow(win);
@@ -608,24 +784,28 @@ makepicks:
                         /* populate the menu with gender choices */
                         setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
                         /* add miscellaneous menu entries */
-                        role_menu_extra(ROLE_RANDOM, win);
+                        role_menu_extra(ROLE_RANDOM, win, TRUE);
                         any.a_int = 0; /* separator, not a choice */
-                        add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "",
+                        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
                                  MENU_UNSELECTED);
-                        role_menu_extra(RS_ROLE, win);
-                        role_menu_extra(RS_RACE, win);
-                        role_menu_extra(RS_ALGNMNT, win);
+                        role_menu_extra(RS_ROLE, win, FALSE);
+                        role_menu_extra(RS_RACE, win, FALSE);
+                        role_menu_extra(RS_ALGNMNT, win, FALSE);
                         if (gotrolefilter())
-                            role_menu_extra(RS_filter, win);
-                        role_menu_extra(ROLE_NONE, win); /* quit */
+                            role_menu_extra(RS_filter, win, FALSE);
+                        role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
 /*JP
                         Strcpy(pbuf, "Pick a gender or sex");
 */
                         Strcpy(pbuf, "\90«\95Ê\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
                         end_menu(win, pbuf);
                         n = select_menu(win, PICK_ONE, &selected);
-                        choice = (n == 1) ? selected[0].item.a_int
-                                          : ROLE_NONE;
+                        if (n > 0) {
+                            choice = selected[0].item.a_int;
+                            if (n > 1 && choice == ROLE_RANDOM)
+                                choice = selected[1].item.a_int;
+                        } else
+                            choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
                         if (selected)
                             free((genericptr_t) selected), selected = 0;
                         destroy_nhwindow(win);
@@ -696,24 +876,28 @@ makepicks:
                         start_menu(win);
                         any = zeroany; /* zero out all bits */
                         setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
-                        role_menu_extra(ROLE_RANDOM, win);
+                        role_menu_extra(ROLE_RANDOM, win, TRUE);
                         any.a_int = 0; /* separator, not a choice */
-                        add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "",
+                        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
                                  MENU_UNSELECTED);
-                        role_menu_extra(RS_ROLE, win);
-                        role_menu_extra(RS_RACE, win);
-                        role_menu_extra(RS_GENDER, win);
+                        role_menu_extra(RS_ROLE, win, FALSE);
+                        role_menu_extra(RS_RACE, win, FALSE);
+                        role_menu_extra(RS_GENDER, win, FALSE);
                         if (gotrolefilter())
-                            role_menu_extra(RS_filter, win);
-                        role_menu_extra(ROLE_NONE, win); /* quit */
+                            role_menu_extra(RS_filter, win, FALSE);
+                        role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
 /*JP
                         Strcpy(pbuf, "Pick an alignment or creed");
 */
                         Strcpy(pbuf, "\91®\90«\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
                         end_menu(win, pbuf);
                         n = select_menu(win, PICK_ONE, &selected);
-                        choice = (n == 1) ? selected[0].item.a_int
-                                          : ROLE_NONE;
+                        if (n > 0) {
+                            choice = selected[0].item.a_int;
+                            if (n > 1 && choice == ROLE_RANDOM)
+                                choice = selected[1].item.a_int;
+                        } else
+                            choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
                         if (selected)
                             free((genericptr_t) selected), selected = 0;
                         destroy_nhwindow(win);
@@ -767,7 +951,7 @@ makepicks:
      *           q - quit
      *           (end)
      */
-    getconfirmation = (pick4u != 'a' && !flags.randomall);
+    getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
     while (getconfirmation) {
         tty_clear_nhwindow(BASE_WINDOW);
         role_selection_prolog(ROLE_NONE, BASE_WINDOW);
@@ -795,11 +979,11 @@ makepicks:
                 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
                                                   : roles[ROLE].name.m);
 #endif
-        add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, pbuf,
+        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
                  MENU_UNSELECTED);
         /* blank separator */
         any.a_int = 0;
-        add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "", MENU_UNSELECTED);
+        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
         /* [ynaq] menu choices */
         any.a_int = 1;
 /*JP
@@ -881,7 +1065,7 @@ makepicks:
     tty_display_nhwindow(BASE_WINDOW, FALSE);
     return;
 
-give_up:
+ give_up:
     /* Quit */
     if (selected)
         free((genericptr_t) selected); /* [obsolete] */
@@ -919,7 +1103,7 @@ reset_role_filtering()
 
     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
-             "Uncceptable alignments", MENU_UNSELECTED);
+             "Unacceptable alignments", MENU_UNSELECTED);
     setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
 
     end_menu(win, "Pick all that apply");
@@ -1142,12 +1326,12 @@ tty_askname()
             if (tryct > 10)
                 bail("Giving up after 10 tries.\n");
             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
-/*JP
+#if 0 /*JP*/
             tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
-*/
-           tty_putstr(BASE_WINDOW, 0, "\82 \82È\82½\82Ì\83L\83\83\83\89\83N\83^\82Ì\96¼\91O\82Í\81H");
-            /* erase previous prompt (in case of ESC after partial response)
-             */
+#else
+            tty_putstr(BASE_WINDOW, 0, "\82 \82È\82½\82Ì\83L\83\83\83\89\83N\83^\82Ì\96¼\91O\82Í\81H");
+#endif
+            /* erase previous prompt (in case of ESC after partial response) */
             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
         }
         tty_putstr(BASE_WINDOW, 0, who_are_you);
@@ -1157,6 +1341,8 @@ tty_askname()
         while ((c = tty_nhgetch()) != '\n') {
             if (c == EOF)
                 c = '\033';
+            if (c == '\r')
+                break;
             if (c == '\033') {
                 ct = 0;
                 break;
@@ -1168,7 +1354,7 @@ tty_askname()
             /* some people get confused when their erase char is not ^H */
             if (c == '\b' || c == '\177') {
 #if 1 /*JP*/
-           moreback:
+            moreback:
 #endif
                 if (ct) {
                     ct--;
@@ -1190,19 +1376,19 @@ tty_askname()
 #endif
                 }
 #if 1 /*JP*/
-               if(is_kanji2(ptmpname, ct))
-                 goto moreback;
+                if(is_kanji2(ptmpname, ct))
+                  goto moreback;
 #endif
                 continue;
             }
 #if defined(UNIX) || defined(VMS)
 # if 0 /*JP*/
             if (c != '-' && c != '@')
-                if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') &&
+                if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')
                     /* reject leading digit but allow digits elsewhere
                        (avoids ambiguity when character name gets
                        appended to uid to construct save file name) */
-                    !(c >= '0' && c <= '9' && ct > 0))
+                    && !(c >= '0' && c <= '9' && ct > 0))
                     c = '_';
 # endif /*JP*/
 #endif
@@ -1213,10 +1399,10 @@ tty_askname()
 #  if 0 /*JP*/
                     (void) putchar(c);
 #  else
-                   (void) cputchar(c);
+                    (void) cputchar(c);
 #   ifdef NO_TERMS
-                   ttyDisplay->curx++;
-                   wins[BASE_WINDOW]->curx++;
+                    ttyDisplay->curx++;
+                    wins[BASE_WINDOW]->curx++;
 #   endif
 #  endif /*JP*/
                 } else
@@ -1224,14 +1410,14 @@ tty_askname()
 /*JP
                     msmsg("%c", c);
 */
-                   (void) putchar(c);
+                    (void) putchar(c);
 #else
                 (void) putchar(c);
 #endif
 #if 0 /*JP*/
                 plname[ct++] = c;
 #else
-               ptmpname[ct++] = c;
+                ptmpname[ct++] = c;
 #endif
 #ifdef WIN32CON
                 ttyDisplay->curx++;
@@ -1241,7 +1427,7 @@ tty_askname()
 #if 0 /*JP*/
         plname[ct] = 0;
 #else
-       ptmpname[ct] = 0;
+        ptmpname[ct] = 0;
 #endif
     } while (ct == 0);
 
@@ -1266,8 +1452,9 @@ tty_get_nh_event()
 STATIC_OVL void
 getret()
 {
+    HUPSKIP();
 #if 1 /*JP*/
-       jputchar('\0');
+    jputchar('\0');
 #endif
     xputs("\n");
     if (flags.standout)
@@ -1321,9 +1508,7 @@ const char *str;
         }
     }
     WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
-#ifndef STATUS_VIA_WINDOWPORT
     WIN_STATUS = WIN_ERR;
-#endif
 #ifdef FREE_ALL_MEMORY
     if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
         free_window_info(wins[BASE_WINDOW], TRUE);
@@ -1338,6 +1523,9 @@ const char *str;
 #ifndef NO_TERMS    /*(until this gets added to the window interface)*/
     tty_shutdown(); /* cleanup termcap/terminfo/whatever */
 #endif
+#ifdef WIN32
+    nttty_exit();
+#endif
     iflags.window_inited = 0;
 }
 
@@ -1346,13 +1534,21 @@ tty_create_nhwindow(type)
 int type;
 {
     struct WinDesc *newwin;
-    int i;
+    int i, rowoffset;
     int newid;
 
-    if (maxwin == MAXWIN)
+    for (newid = 0; newid < MAXWIN; ++newid)
+        if (wins[newid] == 0)
+            break;
+    if (newid == MAXWIN) {
+        panic("No window slots!");
+        /*NOTREACHED*/
         return WIN_ERR;
+    }
+
+    newwin = (struct WinDesc *) alloc(sizeof (struct WinDesc));
+    wins[newid] = newwin;
 
-    newwin = (struct WinDesc *) alloc(sizeof(struct WinDesc));
     newwin->type = type;
     newwin->flags = 0;
     newwin->active = FALSE;
@@ -1381,15 +1577,22 @@ int type;
         newwin->maxcol = newwin->cols = 0;
         break;
     case NHW_STATUS:
-        /* status window, 2 lines long, full width, bottom of screen */
+        /* status window, 2 or 3 lines long, full width, bottom of screen */
+        if (iflags.wc2_statuslines < 2
+#ifndef CLIPPING
+            || (LI < 1 + ROWNO + 3)
+#endif
+            || iflags.wc2_statuslines > 3)
+            iflags.wc2_statuslines = 2;
         newwin->offx = 0;
+        rowoffset = ttyDisplay->rows - iflags.wc2_statuslines;
 #if defined(USE_TILES) && defined(MSDOS)
         if (iflags.grmode) {
-            newwin->offy = ttyDisplay->rows - 2;
+            newwin->offy = rowoffset;
         } else
 #endif
-            newwin->offy = min((int) ttyDisplay->rows - 2, ROWNO + 1);
-        newwin->rows = newwin->maxrow = 2;
+            newwin->offy = min(rowoffset, ROWNO + 1);
+        newwin->rows = newwin->maxrow = iflags.wc2_statuslines;
         newwin->cols = newwin->maxcol = ttyDisplay->cols;
         break;
     case NHW_MAP:
@@ -1403,9 +1606,8 @@ int type;
         break;
     case NHW_MENU:
     case NHW_TEXT:
-        /* inventory/menu window, variable length, full width, top of screen
-         */
-        /* help window, the same, different semantics for display, etc */
+        /* inventory/menu window, variable length, full width, top of screen;
+           help window, the same, different semantics for display, etc */
         newwin->offx = newwin->offy = 0;
         newwin->rows = 0;
         newwin->cols = ttyDisplay->cols;
@@ -1413,33 +1615,20 @@ int type;
         break;
     default:
         panic("Tried to create window type %d\n", (int) type);
-        return WIN_ERR;
-    }
-
-    for (newid = 0; newid < MAXWIN; newid++) {
-        if (wins[newid] == 0) {
-            wins[newid] = newwin;
-            break;
-        }
-    }
-    if (newid == MAXWIN) {
-        panic("No window slots!");
+        /*NOTREACHED*/
         return WIN_ERR;
     }
 
     if (newwin->maxrow) {
-        newwin->data =
-            (char **) alloc(sizeof(char *) * (unsigned) newwin->maxrow);
-        newwin->datlen =
-            (short *) alloc(sizeof(short) * (unsigned) newwin->maxrow);
-        if (newwin->maxcol) {
-            /* WIN_STATUS */
-            for (i = 0; i < newwin->maxrow; i++) {
+        newwin->data = (char **) alloc(
+                               (unsigned) (newwin->maxrow * sizeof (char *)));
+        newwin->datlen = (short *) alloc(
+                                (unsigned) (newwin->maxrow * sizeof (short)));
+        for (i = 0; i < newwin->maxrow; i++) {
+            if (newwin->maxcol) { /* WIN_STATUS */
                 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
                 newwin->datlen[i] = (short) newwin->maxcol;
-            }
-        } else {
-            for (i = 0; i < newwin->maxrow; i++) {
+            } else {
                 newwin->data[i] = (char *) 0;
                 newwin->datlen[i] = 0;
             }
@@ -1460,16 +1649,18 @@ winid window;
 struct WinDesc *cw;
 boolean clear;
 {
-    if (cw->offx == 0)
+    if (cw->offx == 0) {
         if (cw->offy) {
             tty_curs(window, 1, 0);
             cl_eos();
-        } else if (clear)
+        } else if (clear) {
             clear_screen();
-        else
+        } else {
             docrt();
-    else
+        }
+    } else {
         docorner((int) cw->offx, cw->maxrow + 1);
+    }
 }
 
 STATIC_OVL void
@@ -1503,7 +1694,7 @@ boolean free_data;
         tty_menu_item *temp;
 
         while ((temp = cw->mlist) != 0) {
-            cw->mlist = cw->mlist->next;
+            cw->mlist = temp->next;
             if (temp->str)
                 free((genericptr_t) temp->str);
             free((genericptr_t) temp);
@@ -1524,12 +1715,16 @@ void
 tty_clear_nhwindow(window)
 winid window;
 {
+    int i, j, m, n;
     register struct WinDesc *cw = 0;
 
+    HUPSKIP();
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
         panic(winpanicstr, window);
     ttyDisplay->lastwin = window;
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
     switch (cw->type) {
     case NHW_MESSAGE:
         if (ttyDisplay->toplin) {
@@ -1541,17 +1736,27 @@ winid window;
         }
         break;
     case NHW_STATUS:
-        tty_curs(window, 1, 0);
-        cl_end();
-        tty_curs(window, 1, 1);
-        cl_end();
+        m = cw->maxrow;
+        n = cw->cols;
+        for (i = 0; i < m; ++i) {
+            tty_curs(window, 1, i);
+            cl_end();
+
+            for (j = 0; j < n - 1; ++j)
+                cw->data[i][j] = ' ';
+            cw->data[i][n - 1] = '\0';
+            /*finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
+        }
+        context.botlx = 1;
         break;
     case NHW_MAP:
         /* cheap -- clear the whole thing and tell nethack to redraw botl */
         context.botlx = 1;
-    /* fall into ... */
+        /*FALLTHRU*/
     case NHW_BASE:
         clear_screen();
+        /*for (i = 0; i < cw->maxrow; ++i)           */
+        /*    finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
         break;
     case NHW_MENU:
     case NHW_TEXT:
@@ -1610,6 +1815,7 @@ const char *s; /* valid responses */
     const char *prompt = cw->morestr ? cw->morestr : defmorestr;
     int offset = (cw->type == NHW_TEXT) ? 1 : 2;
 
+    HUPSKIP();
     tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
              (int) ttyDisplay->cury);
     if (flags.standout)
@@ -1622,10 +1828,10 @@ const char *s; /* valid responses */
     {
       const char *p;
       p = prompt;
-       while(*p){
-           jputchar(*(p++));
-           ttyDisplay->curx++;
-       }
+        while(*p){
+            jputchar(*(p++));
+            ttyDisplay->curx++;
+        }
     }
 /*    jputs(prompt);*/
 #endif
@@ -1643,6 +1849,7 @@ tty_menu_item *item;
 {
     char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
 
+    HUPSKIP();
     tty_curs(window, 4, lineno);
     term_start_attr(item->attr);
 /*JP
@@ -1737,6 +1944,31 @@ char acc; /* group accelerator, 0 => all */
     }
 }
 
+/* support menucolor in addition to caller-supplied attribute */
+STATIC_OVL void
+toggle_menu_attr(on, color, attr)
+boolean on;
+int color, attr;
+{
+    if (on) {
+        term_start_attr(attr);
+#ifdef TEXTCOLOR
+        if (color != NO_COLOR)
+            term_start_color(color);
+#endif
+    } else {
+#ifdef TEXTCOLOR
+        if (color != NO_COLOR)
+            term_end_color();
+#endif
+        term_end_attr(attr);
+    }
+
+#ifndef TEXTCOLOR
+    nhUse(color);
+#endif
+}
+
 STATIC_OVL void
 process_menu_window(window, cw)
 winid window;
@@ -1744,7 +1976,7 @@ struct WinDesc *cw;
 {
     tty_menu_item *page_start, *page_end, *curr;
     long count;
-    int n, curr_page, page_lines, resp_len;
+    int n, attr_n, curr_page, page_lines, resp_len;
     boolean finished, counting, reset_count;
     char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
@@ -1788,6 +2020,7 @@ struct WinDesc *cw;
 
     /* loop until finished */
     while (!finished) {
+        HUPSKIP();
         if (reset_count) {
             counting = FALSE;
             count = 0;
@@ -1815,8 +2048,8 @@ struct WinDesc *cw;
                 page_end = cw->plist[curr_page + 1];
                 for (page_lines = 0, curr = page_start; curr != page_end;
                      page_lines++, curr = curr->next) {
-                    int color = NO_COLOR, attr = ATR_NONE;
-                    boolean menucolr = FALSE;
+                    int attr, color = NO_COLOR;
+
                     if (curr->selector)
                         *rp++ = curr->selector;
 
@@ -1827,9 +2060,26 @@ struct WinDesc *cw;
 #if 0 /*JP*/
                     (void) putchar(' ');
 #else
-                   (void) jputchar(' ');
+                    (void) jputchar(' ');
 #endif
                     ++ttyDisplay->curx;
+
+                    if (!iflags.use_menu_color
+                        || !get_menu_coloring(curr->str, &color, &attr))
+                        attr = curr->attr;
+
+                    /* which character to start attribute highlighting;
+                       whole line for headers and such, after the selector
+                       character and space and selection indicator for menu
+                       lines (including fake ones that simulate grayed-out
+                       entries, so we don't rely on curr->identifier here) */
+                    attr_n = 0; /* whole line */
+                    if (curr->str[0] && curr->str[1] == ' '
+                        && curr->str[2] && index("-+#", curr->str[2])
+                        && curr->str[3] == ' ')
+                        /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
+                        attr_n = 4; /* [4:N]=entry description */
+
                     /*
                      * Don't use xputs() because (1) under unix it calls
                      * tputstr() which will interpret a '*' as some kind
@@ -1837,55 +2087,43 @@ struct WinDesc *cw;
                      * actually output the character.  We're faster doing
                      * this.
                      */
-                    if (iflags.use_menu_color
-                        && (menucolr = get_menu_coloring(curr->str, &color,
-                                                         &attr))) {
-                        term_start_attr(attr);
-#ifdef TEXTCOLOR
-                        if (color != NO_COLOR)
-                            term_start_color(color);
-#endif
-                    } else
-                        term_start_attr(curr->attr);
                     for (n = 0, cp = curr->str;
+                         *cp &&
 #ifndef WIN32CON
-                         *cp
-                         && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
-                         cp++, n++)
+                            (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
 #else
-                         *cp
-                         && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
-                         cp++, n++, ttyDisplay->curx++)
+                            (int) ttyDisplay->curx < (int) ttyDisplay->cols;
+                         ttyDisplay->curx++,
 #endif
-                        if (n == 2 && curr->identifier.a_void != 0
+                         cp++, n++) {
+                        if (n == attr_n && (color != NO_COLOR
+                                            || attr != ATR_NONE))
+                            toggle_menu_attr(TRUE, color, attr);
+                        if (n == 2
+                            && curr->identifier.a_void != 0
                             && curr->selected) {
                             if (curr->count == -1L)
 #if 0 /*JP*/
                                 (void) putchar('+'); /* all selected */
 #else
-                               (void) jputchar('+'); /* all selected */
+                                (void) jputchar('+'); /* all selected */
 #endif
                             else
 #if 0 /*JP*/
                                 (void) putchar('#'); /* count selected */
 #else
-                               (void) jputchar('#'); /* count selected */
+                                (void) jputchar('#'); /* count selected */
 #endif
                         } else
 #if 0 /*JP*/
                             (void) putchar(*cp);
 #else
-                           (void) jputchar(*cp);
-#endif
-                    if (iflags.use_menu_color && menucolr) {
-#ifdef TEXTCOLOR
-                        if (color != NO_COLOR)
-                            term_end_color();
+                            (void) jputchar(*cp);
 #endif
-                        term_end_attr(attr);
-                    } else
-                        term_end_attr(curr->attr);
-                }
+                    } /* for *cp */
+                    if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE))
+                        toggle_menu_attr(FALSE, color, attr);
+                } /* if npages > 0 */
             } else {
                 page_start = 0;
                 page_end = 0;
@@ -1905,6 +2143,7 @@ struct WinDesc *cw;
 
             /* set extra chars.. */
             Strcat(resp, default_menu_cmds);
+            Strcat(resp, " ");                  /* next page or end */
             Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
             Strcat(resp, gacc);                 /* group accelerators */
             Strcat(resp, mapped_menu_cmds);
@@ -1912,10 +2151,11 @@ struct WinDesc *cw;
             if (cw->npages > 1)
 #if 0 /*JP*/
                 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
+                        (int) cw->npages);
 #else
-               Sprintf(cw->morestr, "(%d/%d)", curr_page + 1,
-#endif
+                Sprintf(cw->morestr, "(%d/%d)", curr_page + 1,
                         (int) cw->npages);
+#endif
             else if (msave)
                 Strcpy(cw->morestr, msave);
             else
@@ -1944,7 +2184,8 @@ struct WinDesc *cw;
             /* special case: '0' is also the default ball class */
             if (!counting && index(gacc, morc))
                 goto group_accel;
-        /* fall through to count the zero */
+            /* fall through to count the zero */
+            /*FALLTHRU*/
         case '1':
         case '2':
         case '3':
@@ -1992,12 +2233,15 @@ struct WinDesc *cw;
                 break;
             }
         /* else fall through */
+        case ' ':
         case MENU_NEXT_PAGE:
             if (cw->npages > 0 && curr_page != cw->npages - 1) {
                 curr_page++;
                 page_start = 0;
-            } else
-                finished = TRUE; /* questionable behavior */
+            } else if (morc == ' ') {
+                /* ' ' finishes menus here, but stop '>' doing the same. */
+                finished = TRUE;
+            }
             break;
         case MENU_PREVIOUS_PAGE:
             if (cw->npages > 0 && curr_page != 0) {
@@ -2055,7 +2299,7 @@ struct WinDesc *cw;
                 tty_nhbell();
                 break;
             } else {
-                char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ];
+                char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ] = DUMMY;
                 boolean on_curr_page = FALSE;
                 int lineno = 0;
 
@@ -2092,7 +2336,7 @@ struct WinDesc *cw;
                 tty_nhbell();
                 break;
             } else if (index(gacc, morc)) {
           group_accel:
+ group_accel:
                 /* group accelerator; for the PICK_ONE case, we know that
                    it matches exactly one item in order to be in gacc[] */
                 invert_all(window, page_start, page_end, morc);
@@ -2123,9 +2367,13 @@ winid window;
 struct WinDesc *cw;
 {
     int i, n, attr;
+#if 0 /*JP*/
+    boolean linestart;
+#endif
     register char *cp;
 
     for (n = 0, i = 0; i < cw->maxrow; i++) {
+        HUPSKIP();
         if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
             tty_curs(window, 1, n);
             cl_end();
@@ -2154,24 +2402,38 @@ struct WinDesc *cw;
 #if 0 /*JP*/
                 (void) putchar(' ');
 #else
-               (void) jputchar(' ');
+                (void) jputchar(' ');
 #endif
                 ++ttyDisplay->curx;
             }
             term_start_attr(attr);
+#if 0 /*JP*/
+            for (cp = &cw->data[i][1], linestart = TRUE;
+#else
             for (cp = &cw->data[i][1];
+#endif
 #ifndef WIN32CON
                  *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
-                 cp++)
+                 cp++
 #else
                  *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
-                 cp++, ttyDisplay->curx++)
+                 cp++, ttyDisplay->curx++
 #endif
+                 ) {
+                /* message recall for msg_window:full/combination/reverse
+                   might have output from '/' in it (see redotoplin()) */
 #if 0 /*JP*/
-                (void) putchar(*cp);
+                if (linestart && (*cp & 0x80) != 0) {
+                    g_putch(*cp);
+                    end_glyphout();
+                    linestart = FALSE;
+                } else {
+                    (void) putchar(*cp);
+                }
 #else
-               (void) jputchar(*cp);
+                (void) jputchar(*cp);
 #endif
+            }
             term_end_attr(attr);
         }
     }
@@ -2200,6 +2462,7 @@ boolean blocking; /* with ttys, all windows are blocking */
     register struct WinDesc *cw = 0;
     short s_maxcol;
 
+    HUPSKIP();
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
         panic(winpanicstr, window);
     if (cw->flags & WIN_CANCELLED)
@@ -2207,6 +2470,8 @@ boolean blocking; /* with ttys, all windows are blocking */
     ttyDisplay->lastwin = window;
     ttyDisplay->rawprint = 0;
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
     switch (cw->type) {
     case NHW_MESSAGE:
         if (ttyDisplay->toplin == 1) {
@@ -2227,6 +2492,7 @@ boolean blocking; /* with ttys, all windows are blocking */
             tty_display_nhwindow(WIN_MESSAGE, TRUE);
             return;
         }
+        /*FALLTHRU*/
     case NHW_BASE:
         (void) fflush(stdout);
         break;
@@ -2255,13 +2521,15 @@ boolean blocking; /* with ttys, all windows are blocking */
         if (ttyDisplay->toplin == 1)
             tty_display_nhwindow(WIN_MESSAGE, TRUE);
 #ifdef H2344_BROKEN
-        if (cw->maxrow >= (int) ttyDisplay->rows)
+        if (cw->maxrow >= (int) ttyDisplay->rows
+            || !iflags.menu_overlay)
 #else
-        if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows)
+        if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
+            || !iflags.menu_overlay)
 #endif
         {
             cw->offx = 0;
-            if (cw->offy) {
+            if (cw->offy || iflags.menu_overlay) {
                 tty_curs(window, 1, 0);
                 cl_eos();
             } else
@@ -2287,14 +2555,17 @@ winid window;
 {
     register struct WinDesc *cw = 0;
 
+    HUPSKIP();
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
         panic(winpanicstr, window);
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
     switch (cw->type) {
     case NHW_MESSAGE:
         if (ttyDisplay->toplin)
             tty_display_nhwindow(WIN_MESSAGE, TRUE);
-    /*FALLTHRU*/
+        /*FALLTHRU*/
     case NHW_STATUS:
     case NHW_BASE:
     case NHW_MAP:
@@ -2342,7 +2613,7 @@ winid window;
 
     free_window_info(cw, TRUE);
     free((genericptr_t) cw);
-    wins[window] = 0;
+    wins[window] = 0; /* available for re-use */
 }
 
 void
@@ -2355,10 +2626,13 @@ register int x, y; /* not xchar: perhaps xchar is unsigned and
     int cx = ttyDisplay->curx;
     int cy = ttyDisplay->cury;
 
+    HUPSKIP();
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
         panic(winpanicstr, window);
     ttyDisplay->lastwin = window;
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
 #if defined(USE_TILES) && defined(MSDOS)
     adjust_cursor_flags(cw);
 #endif
@@ -2367,6 +2641,7 @@ register int x, y; /* not xchar: perhaps xchar is unsigned and
 #ifdef DEBUG
     if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
         const char *s = "[unknown type]";
+
         switch (cw->type) {
         case NHW_MESSAGE:
             s = "[topl window]";
@@ -2387,9 +2662,15 @@ register int x, y; /* not xchar: perhaps xchar is unsigned and
             s = "[base window]";
             break;
         }
-        debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x,
-                    y);
-        return;
+        debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
+        /* This return statement caused a functional difference between
+           DEBUG and non-DEBUG operation, so it is being commented
+           out. It caused tty_curs() to fail to move the cursor to the
+           location it needed to be if the x,y range checks failed,
+           leaving the next piece of output to be displayed at whatever
+           random location the cursor happened to be at prior. */
+
+        /* return; */
     }
 #endif
     x += cw->offx;
@@ -2426,7 +2707,7 @@ register int x, y; /* not xchar: perhaps xchar is unsigned and
 # if 0 /*JP*/
         (void) putchar('\r');
 # else
-       (void) cputchar('\r');
+        (void) cputchar('\r');
 # endif
         ttyDisplay->curx = 0;
         nocmov(x, y);
@@ -2447,26 +2728,28 @@ int x, y;
 char ch;
 {
     register struct WinDesc *cw = 0;
-#if 1 /*JP*/
-    static int prev_win;
-#endif
 
+    HUPSKIP();
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
         panic(winpanicstr, window);
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
     switch (cw->type) {
+#ifndef STATUS_HILITES
     case NHW_STATUS:
+#endif
     case NHW_MAP:
     case NHW_BASE:
         tty_curs(window, x, y);
 #if 0 /*JP*/
         (void) putchar(ch);
 #else
-       if(cw->type!=NHW_MAP)
-         (void) jputchar(ch);
-       else {
-         (void) cputchar(ch);
-       }
+        if(cw->type!=NHW_MAP)
+          (void) jputchar(ch);
+        else {
+          (void) cputchar(ch);
+        }
 #endif
         ttyDisplay->curx++;
         cw->curx++;
@@ -2477,9 +2760,6 @@ char ch;
         impossible("Can't putsym to window type %d", cw->type);
         break;
     }
-#if 1 /*JP*/
-    prev_win = cw->type;
-#endif
 }
 
 STATIC_OVL const char *
@@ -2487,22 +2767,30 @@ compress_str(str)
 const char *str;
 {
     static char cbuf[BUFSZ];
-    /* compress in case line too long */
-    if ((int) strlen(str) >= CO) {
-        register const char *bp0 = str;
-        register char *bp1 = cbuf;
 
-        do {
-#ifdef CLIPPING
-            if (*bp0 != ' ' || bp0[1] != ' ')
-#else
-            if (*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
-#endif
-                *bp1++ = *bp0;
-        } while (*bp0++);
-    } else
-        return str;
-    return cbuf;
+    /* compress out consecutive spaces if line is too long;
+       topline wrapping converts space at wrap point into newline,
+       we reverse that here */
+    if ((int) strlen(str) >= CO || index(str, '\n')) {
+        const char *in_str = str;
+        char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
+        boolean was_space = TRUE; /* True discards all leading spaces;
+                                     False would retain one if present */
+
+        while ((c = *in_str++) != '\0' && outstr < outend) {
+            if (c == '\n')
+                c = ' ';
+            if (was_space && c == ' ')
+                continue;
+            *outstr++ = c;
+            was_space = (c == ' ');
+        }
+        if ((was_space && outstr > cbuf) || outstr == outend)
+            --outstr; /* remove trailing space or make room for terminator */
+        *outstr = '\0';
+        str = cbuf;
+    }
+    return str;
 }
 
 void
@@ -2513,17 +2801,21 @@ const char *str;
 {
     register struct WinDesc *cw = 0;
     register char *ob;
+    register long i, n0;
+#ifndef STATUS_HILITES
     register const char *nb;
-    register long i, j, n0;
+    register long j;
 #if 1 /*JP*/
-    int kchar2 = 0;            /* if 1, kanji 2nd byte */
+    int kchar2 = 0;             /* if 1, kanji 2nd byte */
+#endif
 #endif
 
+    HUPSKIP();
     /* Assume there's a real problem if the window is missing --
      * probably a panic message
      */
 #if 1 /*JP*/
-    jputchar('\0');    /* RESET */
+    jputchar('\0');     /* RESET */
 #endif
 
     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
@@ -2539,19 +2831,37 @@ const char *str;
 
     ttyDisplay->lastwin = window;
 
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
+
     switch (cw->type) {
-    case NHW_MESSAGE:
+    case NHW_MESSAGE: {
+        int suppress_history = (attr & ATR_NOHISTORY);
+
+        /* in case we ever support display attributes for topline
+           messages, clear flag mask leaving only display attr */
+        /*attr &= ~(ATR_URGENT | ATR_NOHISTORY);*/
+
         /* really do this later */
 #if defined(USER_SOUNDS) && defined(WIN32CON)
         play_sound_for_message(str);
 #endif
-        update_topl(str);
+        if (!suppress_history) {
+            /* normal output; add to current top line if room, else flush
+               whatever is there to history and then write this */
+            update_topl(str);
+        } else {
+            /* put anything already on top line into history */
+            remember_topl();
+            /* write to top line without remembering what we're writing */
+            show_topl(str);
+        }
         break;
-
+    }
+#ifndef STATUS_HILITES
     case NHW_STATUS:
         ob = &cw->data[cw->cury][j = cw->curx];
         if (context.botlx)
-            *ob = 0;
+            *ob = '\0';
         if (!cw->cury && (int) strlen(str) >= CO) {
             /* the characters before "St:" are unnecessary */
             nb = index(str, ':');
@@ -2569,46 +2879,46 @@ const char *str;
                 break;
             }
 #if 0 /*JP*/
-            if (*ob != *nb)
 #else /* this code updates all status line at any time */
 # if 0
-/* check 2-bytes character for Japanese        */
-/* by issei 93/12/2                    */
-           uc = *((unsigned char *)nb);
-           if((!(uc & 0x80) && *ob != *nb) || kflg){
-             tty_putsym(WIN_STATUS, i, cw->cury, *nb);
-           }
-           else{
-             if(*ob != *nb || *(ob+1)!= *(nb+1)){
-               tty_putsym(WIN_STATUS, i, cw->cury, *nb);
-               kflg = 1;
-             }
-           }
-# endif        /* 0 */
-#define ismbchar(c)    (((unsigned char)(c)) & 0x80)
-#define KANJI2 1
-#define KUPDATE        2
-           if (kchar2)                 /* kanji 2nd byte */
-           {
-               if (kchar2 & KUPDATE)
-                   tty_putsym(WIN_STATUS, i, cw->cury, *nb);
-               kchar2 = 0;
-           }
-           else if (ismbchar(*nb))     /* kanji 1st byte */
-           {
-               kchar2 = KANJI2;
-               /* Kanji char must be checked as 2-bytes pair. */
-               /* check i to prevent putting only kanji 1st byte at last. */
-               if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
-               {
-                   tty_putsym(WIN_STATUS, i, cw->cury, *nb);
-                   kchar2 |= KUPDATE;  /* must do update */
-               }
-               /* else nb is the same char as old, so need not to update */
-           }
-           /* not kanji char */
-           else if (*nb != *ob)
+/* check 2-bytes character for Japanese */
+/* by issei 93/12/2                     */
+            uc = *((unsigned char *)nb);
+            if((!(uc & 0x80) && *ob != *nb) || kflg){
+              tty_putsym(WIN_STATUS, i, cw->cury, *nb);
+            }
+            else{
+              if(*ob != *nb || *(ob+1)!= *(nb+1)){
+                tty_putsym(WIN_STATUS, i, cw->cury, *nb);
+                kflg = 1;
+              }
+            }
+# endif /* 0 */
+#define ismbchar(c)     (((unsigned char)(c)) & 0x80)
+#define KANJI2  1
+#define KUPDATE 2
+            if (kchar2)                 /* kanji 2nd byte */
+            {
+                if (kchar2 & KUPDATE)
+                    tty_putsym(WIN_STATUS, i, cw->cury, *nb);
+                kchar2 = 0;
+            }
+            else if (ismbchar(*nb))     /* kanji 1st byte */
+            {
+                kchar2 = KANJI2;
+                /* Kanji char must be checked as 2-bytes pair. */
+                /* check i to prevent putting only kanji 1st byte at last. */
+                if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
+                {
+                    tty_putsym(WIN_STATUS, i, cw->cury, *nb);
+                    kchar2 |= KUPDATE;  /* must do update */
+                }
+                /* else nb is the same char as old, so need not to update */
+            }
+            /* not kanji char */
+            else
 #endif
+            if (*ob != *nb)
                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
             if (*ob)
                 ob++;
@@ -2616,15 +2926,10 @@ const char *str;
 
         (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
         cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
-#ifdef STATUS_VIA_WINDOWPORT
-        if (!iflags.use_status_hilites) {
-#endif
-            cw->cury = (cw->cury + 1) % 2;
-            cw->curx = 0;
-#ifdef STATUS_VIA_WINDOWPORT
-        }
-#endif
+        cw->cury = (cw->cury + 1) % cw->maxrow;
+        cw->curx = 0;
         break;
+#endif /* STATUS_HILITES */
     case NHW_MAP:
         tty_curs(window, cw->curx + 1, cw->cury);
         term_start_attr(attr);
@@ -2632,7 +2937,7 @@ const char *str;
 #if 0 /*JP*/
             (void) putchar(*str);
 #else
-           (void) cputchar(*str);
+            (void) cputchar(*str);
 #endif
             str++;
             ttyDisplay->curx++;
@@ -2653,7 +2958,7 @@ const char *str;
 #if 0 /*JP*/
             (void) putchar(*str);
 #else
-           (void) jputchar(*str);
+            (void) jputchar(*str);
 #endif
             str++;
             ttyDisplay->curx++;
@@ -2698,7 +3003,7 @@ const char *str;
         }
         if (cw->data[cw->cury])
             free((genericptr_t) cw->data[cw->cury]);
-        n0 = strlen(str) + 1;
+        n0 = (long) strlen(str) + 1L;
         ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
         *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
         Strcpy(ob, str);
@@ -2719,7 +3024,7 @@ const char *str;
         break;
     }
 #if 1 /*JP*/
-    jputchar('\0');    /* RESET */
+    jputchar('\0');     /* RESET */
 #endif
 }
 
@@ -2755,7 +3060,7 @@ boolean complain;
             }
             if (complain)
                 sleep(10); /* want to wait_synch() but stdin is gone */
-            terminate(EXIT_FAILURE);
+            nh_terminate(EXIT_FAILURE);
         }
         (void) close(fd);
 #ifdef notyet
@@ -2846,6 +3151,7 @@ boolean preselected;        /* item is marked as selected */
     const char *newstr;
     char buf[4 + BUFSZ];
 
+    HUPSKIP();
     if (str == (const char *) 0)
         return;
 
@@ -2856,7 +3162,7 @@ boolean preselected;        /* item is marked as selected */
 
     cw->nitems++;
     if (identifier->a_void) {
-        int len = strlen(str);
+        int len = (int) strlen(str);
 
         if (len >= BUFSZ) {
             /* We *think* everything's coming in off at most BUFSZ bufs... */
@@ -2870,7 +3176,7 @@ boolean preselected;        /* item is marked as selected */
     } else
         newstr = str;
 
-    item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
+    item = (tty_menu_item *) alloc(sizeof *item);
     item->identifier = *identifier;
     item->count = -1L;
     item->selected = preselected;
@@ -2934,17 +3240,26 @@ const char *prompt; /* prompt to for menu */
                      MENU_UNSELECTED);
     }
 
-    /* XXX another magic number? 52 */
+    /* 52: 'a'..'z' and 'A'..'Z'; avoids selector duplication within a page */
     lmax = min(52, (int) ttyDisplay->rows - 1);    /* # lines per page */
     cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
+    /*
+     * TODO?
+     *  For really tall page, allow 53 if '$' or '#' is present and
+     *  54 if both are.  [Only for single page menu, otherwise pages
+     *  without those could try to use too many letters.]
+     *  Probably not worth bothering with; anyone with a display big
+     *  for this to matter will likely switch from tty to curses for
+     *  multi-line message window and/or persistent inventory window.
+     */
 
     /* make sure page list is large enough */
-    if (cw->plist_size < cw->npages + 1 /*need 1 slot beyond last*/) {
+    if (cw->plist_size < cw->npages + 1) { /* +1: need one slot beyond last */
         if (cw->plist)
             free((genericptr_t) cw->plist);
         cw->plist_size = cw->npages + 1;
         cw->plist = (tty_menu_item **) alloc(cw->plist_size
-                                             * sizeof(tty_menu_item *));
+                                             * sizeof (tty_menu_item *));
     }
 
     cw->cols = 0;  /* cols is set when the win is initialized... (why?) */
@@ -3057,6 +3372,7 @@ char let;
 int how;
 const char *mesg;
 {
+    HUPSKIP();
     /* "menu" without selection; use ordinary pline, no more() */
     if (how == PICK_NONE) {
         pline("%s", mesg);
@@ -3092,12 +3408,14 @@ tty_update_inventory()
 void
 tty_mark_synch()
 {
+    HUPSKIP();
     (void) fflush(stdout);
 }
 
 void
 tty_wait_synch()
 {
+    HUPSKIP();
     /* we just need to make sure all windows are synch'd */
     if (!ttyDisplay || ttyDisplay->rawprint) {
         getret();
@@ -3127,10 +3445,18 @@ register int xmin, ymax;
     register int y;
     register struct WinDesc *cw = wins[WIN_MAP];
 
+    HUPSKIP();
+#if 0   /* this optimization is not valuable enough to justify
+           abusing core internals... */
     if (u.uswallow) { /* Can be done more efficiently */
         swallowed(1);
+        /* without this flush, if we happen to follow --More-- displayed in
+           leftmost column, the cursor gets left in the wrong place after
+           <docorner<more<update_topl<tty_putstr calls unwind back to core */
+        flush_screen(0);
         return;
     }
+#endif /*0*/
 
 #if defined(SIGWINCH) && defined(CLIPPING)
     if (ymax > LI)
@@ -3168,6 +3494,7 @@ register int xmin, ymax;
 void
 end_glyphout()
 {
+    HUPSKIP();
 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
     if (GFlag) {
         GFlag = FALSE;
@@ -3189,13 +3516,19 @@ int in_ch;
 {
     register char ch = (char) in_ch;
 
+    HUPSKIP();
 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
-    if (SYMHANDLING(H_IBM) || iflags.eight_bit_tty) {
+    if (SYMHANDLING(H_IBM)
+        /* for DECgraphics, lower-case letters with high bit set mean
+           switch character set and render with high bit clear;
+           user might want 8-bits for other characters */
+        || (iflags.eight_bit_tty && (!SYMHANDLING(H_DEC)
+                                     || (in_ch & 0x7f) < 0x60))) {
         /* IBM-compatible displays don't need other stuff */
 #   if 0 /*JP*/
         (void) putchar(ch);
 #   else
-       (void) cputchar(ch);
+        (void) cputchar(ch);
 #   endif
     } else if (ch & 0x80) {
         if (!GFlag || HE_resets_AS) {
@@ -3205,7 +3538,7 @@ int in_ch;
 # if 0 /*JP*/
         (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
 # else
-       (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
+        (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
 # endif
     } else {
         if (GFlag) {
@@ -3215,7 +3548,7 @@ int in_ch;
 # if 0 /*JP*/
         (void) putchar(ch);
 # else
-       (void) jputchar(ch);
+        (void) jputchar(ch);
 # endif
     }
 
@@ -3235,16 +3568,16 @@ setclipped()
     clipping = TRUE;
     clipx = clipy = 0;
     clipxmax = CO;
-    clipymax = LI - 3;
+    clipymax = LI - 1 - iflags.wc2_statuslines;
 }
 
 void
 tty_cliparound(x, y)
 int x, y;
 {
-    extern boolean restoring;
     int oldx = clipx, oldy = clipy;
 
+    HUPSKIP();
     if (!clipping)
         return;
     if (x < clipx + 5) {
@@ -3256,14 +3589,13 @@ int x, y;
     }
     if (y < clipy + 2) {
         clipy = max(0, y - (clipymax - clipy) / 2);
-        clipymax = clipy + (LI - 3);
+        clipymax = clipy + (LI - 1 - iflags.wc2_statuslines);
     } else if (y > clipymax - 2) {
         clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
-        clipy = clipymax - (LI - 3);
+        clipy = clipymax - (LI - 1 - iflags.wc2_statuslines);
     }
     if (clipx != oldx || clipy != oldy) {
-        if (on_level(&u.uz0, &u.uz) && !restoring)
-            (void) doredraw();
+        redraw_map(); /* ask the core to resend the map window's data */
     }
 }
 #endif /* CLIPPING */
@@ -3289,6 +3621,7 @@ int bkglyph UNUSED;
     int color;
     unsigned special;
 
+    HUPSKIP();
 #ifdef CLIPPING
     if (clipping) {
         if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
@@ -3296,17 +3629,21 @@ int bkglyph UNUSED;
     }
 #endif
     /* map glyph to character and color */
-    (void) mapglyph(glyph, &ch, &color, &special, x, y);
+    (void) mapglyph(glyph, &ch, &color, &special, x, y, 0);
+
+    print_vt_code2(AVTC_SELECT_WINDOW, window);
 
     /* Move the cursor. */
     tty_curs(window, x, y);
 
+    print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
+
 #ifndef NO_TERMS
     if (ul_hack && ch == '_') { /* non-destructive underscore */
 # if 0 /*JP*/
         (void) putchar((char) ' ');
 # else
-       (void) cputchar((char) ' ');
+        (void) cputchar((char) ' ');
 # endif
         backsp();
     }
@@ -3325,7 +3662,8 @@ int bkglyph UNUSED;
     /* must be after color check; term_end_color may turn off inverse too */
     if (((special & MG_PET) && iflags.hilite_pet)
         || ((special & MG_OBJPILE) && iflags.hilite_pile)
-        || ((special & MG_DETECT) && iflags.use_inverse)) {
+        || ((special & MG_DETECT) && iflags.use_inverse)
+        || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
         term_start_attr(ATR_INVERSE);
         reverse_on = TRUE;
     }
@@ -3348,6 +3686,8 @@ int bkglyph UNUSED;
 #endif
     }
 
+    print_vt_code1(AVTC_GLYPH_END);
+
     wins[window]->curx++; /* one character over */
     ttyDisplay->curx++;   /* the real cursor moved too */
 }
@@ -3356,8 +3696,10 @@ void
 tty_raw_print(str)
 const char *str;
 {
+    HUPSKIP();
     if (ttyDisplay)
         ttyDisplay->rawprint++;
+    print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
 #if defined(MICRO) || defined(WIN32CON)
     msmsg("%s\n", str);
 #else
@@ -3374,8 +3716,10 @@ void
 tty_raw_print_bold(str)
 const char *str;
 {
+    HUPSKIP();
     if (ttyDisplay)
         ttyDisplay->rawprint++;
+    print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
     term_start_raw_bold();
 #if defined(MICRO) || defined(WIN32CON)
     msmsg("%s", str);
@@ -3411,6 +3755,8 @@ tty_nhgetch()
     char nestbuf;
 #endif
 
+    HUPSKIP_RESULT('\033');
+    print_vt_code1(AVTC_INLINE_SYNC);
     (void) fflush(stdout);
     /* Note: if raw_print() and wait_synch() get called to report terminal
      * initialization problems, then wins[] and ttyDisplay might not be
@@ -3419,20 +3765,34 @@ tty_nhgetch()
      */
     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
         wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
+    if (iflags.debug_fuzzer) {
+        i = randomkey();
+    } else {
 #ifdef UNIX
-    i = (++nesting == 1) ? tgetch()
-                         : (read(fileno(stdin), (genericptr_t) &nestbuf, 1)
-                            == 1) ? (int) nestbuf : EOF;
-    --nesting;
+        i = (++nesting == 1)
+              ? tgetch()
+              : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1)
+                  ? (int) nestbuf : EOF;
+        --nesting;
 #else
-    i = tgetch();
+        i = tgetch();
 #endif
+    }
     if (!i)
         i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
     else if (i == EOF)
         i = '\033'; /* same for EOF */
     if (ttyDisplay && ttyDisplay->toplin == 1)
         ttyDisplay->toplin = 2;
+#ifdef TTY_TILES_ESCCODES
+    {
+        /* hack to force output of the window select code */
+        int tmp = vt_tile_current_window;
+
+        vt_tile_current_window++;
+        print_vt_code2(AVTC_SELECT_WINDOW, tmp);
+    }
+#endif /* TTY_TILES_ESCCODES */
     return i;
 }
 
@@ -3446,8 +3806,10 @@ int
 tty_nh_poskey(x, y, mod)
 int *x, *y, *mod;
 {
-#if defined(WIN32CON)
     int i;
+
+    HUPSKIP_RESULT('\033');
+#if defined(WIN32CON)
     (void) fflush(stdout);
     /* Note: if raw_print() and wait_synch() get called to report terminal
      * initialization problems, then wins[] and ttyDisplay might not be
@@ -3461,14 +3823,14 @@ int *x, *y, *mod;
         i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
     if (ttyDisplay && ttyDisplay->toplin == 1)
         ttyDisplay->toplin = 2;
-    return i;
 #else /* !WIN32CON */
     nhUse(x);
     nhUse(y);
     nhUse(mod);
 
-    return tty_nhgetch();
+    i = tty_nhgetch();
 #endif /* ?WIN32CON */
+    return i;
 }
 
 void
@@ -3477,10 +3839,6 @@ int dir;
 {
     if (dir != WININIT)
         return;
-#if defined(WIN32CON)
-    if (!strncmpi(windowprocs.name, "tty", 3))
-        nttty_open(0);
-#endif
     return;
 }
 
@@ -3493,62 +3851,202 @@ char *posbar;
     video_update_positionbar(posbar);
 #endif
 }
-#endif
+#endif /* POSITIONBAR */
+
+
+/*
+ * +------------------+
+ * |  STATUS CODE     |
+ * +------------------+
+ */
+
+/*
+ * ----------------------------------------------------------------
+ * tty_status_update
+ *
+ *      Called from the NetHack core and receives info hand-offs
+ *      from the core, processes them, storing some information
+ *      for rendering to the tty.
+ *          -> make_things_fit()
+ *          -> render_status()
+ *
+ * make_things_fit
+ *
+ *      Called from tty_status_update(). It calls check_fields()
+ *      (described next) to get the required number of columns
+ *      and tries a few different ways to squish a representation
+ *      of the status window values onto the 80 column tty display.
+ *          ->check_fields()
+ *          ->set_condition_length()  - update the width of conditions
+ *          ->shrink_enc()      - shrink encumbrance message word
+ *          ->shrink_dlvl()     - reduce the width of Dlvl:42
+ *
+ *  check_fields
+ *
+ *      Verifies that all the fields are ready for display, and
+ *      returns FALSE if called too early in the startup
+ *      processing sequence. It also figures out where everything
+ *      needs to go, taking the current shrinking attempts into
+ *      account. It returns number of columns needed back to
+ *      make_things_fit(), so make_things_fit() can make attempt
+ *      to make adjustments.
+ *
+ *  render_status
+ *
+ *      Goes through each of the status row's fields and
+ *      calls tty_putstatusfield() to place them on the display.
+ *          ->tty_putstatusfield()
+ *      At the end of the for-loop, the NOW values get copied
+ *      to BEFORE values.
+ *
+ *  tty_putstatusfield()
+ *
+ *      Move the cursor to the target spot, and output the field
+ *      asked for by render_status().
+ *
+ * ----------------------------------------------------------------
+ */
 
-#ifdef STATUS_VIA_WINDOWPORT
 /*
  * The following data structures come from the genl_ routines in
  * src/windows.c and as such are considered to be on the window-port
  * "side" of things, rather than the NetHack-core "side" of things.
  */
-
-extern const char *status_fieldnm[MAXBLSTATS];
 extern const char *status_fieldfmt[MAXBLSTATS];
 extern char *status_vals[MAXBLSTATS];
 extern boolean status_activefields[MAXBLSTATS];
 extern winid WIN_STATUS;
 
 #ifdef STATUS_HILITES
-typedef struct hilite_data_struct {
-    int thresholdtype;
-    anything threshold;
-    int behavior;
-    int under;
-    int over;
-} hilite_data_t;
-static hilite_data_t tty_status_hilites[MAXBLSTATS];
-static int tty_status_colors[MAXBLSTATS];
-
-struct color_option {
-    int color;
-    int attr_bits;
+#ifdef TEXTCOLOR
+STATIC_DCL int FDECL(condcolor, (long, unsigned long *));
+#endif
+STATIC_DCL int FDECL(condattr, (long, unsigned long *));
+static unsigned long *tty_colormasks;
+static long tty_condition_bits;
+static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */
+static int hpbar_percent, hpbar_color;
+static struct condition_t {
+    long mask;
+    const char *text[3]; /* 3: potential display vals, progressively shorter */
+} conditions[] = {
+    /* The sequence order of these matters */
+    { BL_MASK_STONE,    { "Stone",    "Ston",  "Sto" } },
+    { BL_MASK_SLIME,    { "Slime",    "Slim",  "Slm" } },
+    { BL_MASK_STRNGL,   { "Strngl",   "Stngl", "Str" } },
+    { BL_MASK_FOODPOIS, { "FoodPois", "Fpois", "Poi" } },
+    { BL_MASK_TERMILL,  { "TermIll" , "Ill",   "Ill" } },
+    { BL_MASK_BLIND,    { "Blind",    "Blnd",  "Bl"  } },
+    { BL_MASK_DEAF,     { "Deaf",     "Def",   "Df"  } },
+    { BL_MASK_STUN,     { "Stun",     "Stun",  "St"  } },
+    { BL_MASK_CONF,     { "Conf",     "Cnf",   "Cf"  } },
+    { BL_MASK_HALLU,    { "Hallu",    "Hal",   "Hl"  } },
+    { BL_MASK_LEV,      { "Lev",      "Lev",   "Lv"  } },
+    { BL_MASK_FLY,      { "Fly",      "Fly",   "Fl"  } },
+    { BL_MASK_RIDE,     { "Ride",     "Rid",   "Rd"  } },
 };
-
-static void FDECL(start_color_option, (struct color_option));
-static void FDECL(end_color_option, (struct color_option));
-static void FDECL(apply_color_option, (struct color_option, const char *));
-static void FDECL(add_colored_text, (const char *, char *));
+static const char *encvals[3][6] = {
+    { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" },
+    { "", "Burden",   "Stress",   "Strain",   "Overtax",   "Overload"   },
+    { "", "Brd",      "Strs",     "Strn",     "Ovtx",      "Ovld"       }
+};
+#define blPAD BL_FLUSH
+#define MAX_PER_ROW 15
+/* 2 or 3 status lines */
+static const enum statusfields
+    twolineorder[3][MAX_PER_ROW] = {
+    { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
+      BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD },
+    { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
+      BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
+      BL_CAP, BL_CONDITION, BL_FLUSH },
+    /* third row of array isn't used for twolineorder */
+    { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
+      blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
+},
+    /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */
+    threelineorder[3][MAX_PER_ROW] = {
+    { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
+      BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
+    { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
+      BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER,
+      BL_CAP, BL_FLUSH, blPAD, blPAD },
+    { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD,
+      blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
+};
+static const enum statusfields (*fieldorder)[MAX_PER_ROW];
+
+static int finalx[3][2];    /* [rows][NOW or BEFORE] */
+static boolean windowdata_init = FALSE;
+static int cond_shrinklvl = 0;
+static int enclev = 0, enc_shrinklvl = 0;
+static int dlvl_shrinklvl = 0;
+static boolean truncation_expected = FALSE;
+#define FORCE_RESET TRUE
+#define NO_RESET FALSE
+
+/* This controls whether to skip fields that aren't
+ * flagged as requiring updating during the current
+ * render_status().
+ *
+ * Hopefully that can be confirmed as working correctly
+ * for all platforms eventually and the conditional
+ * setting below can be removed.
+ */
+static int do_field_opt =
+#if defined(DISABLE_TTY_FIELD_OPT)
+    0;
+#else
+    1;
 #endif
 
+#endif  /* STATUS_HILITES */
+
+/*
+ *  tty_status_init()
+ *      -- initialize the tty-specific data structures.
+ *      -- call genl_status_init() to initialize the general data.
+ */
 void
 tty_status_init()
 {
-    int i;
+#ifdef STATUS_HILITES
+    int i, num_rows;
 
-    /* let genl_status_init do most of the initialization */
-    genl_status_init();
+    num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
+    fieldorder = (num_rows != 3) ? twolineorder : threelineorder;
 
     for (i = 0; i < MAXBLSTATS; ++i) {
-#ifdef STATUS_HILITES
-        tty_status_colors[i] = NO_COLOR; /* no color */
-        tty_status_hilites[i].thresholdtype = 0;
-        tty_status_hilites[i].behavior = BL_TH_NONE;
-        tty_status_hilites[i].under = BL_HILITE_NONE;
-        tty_status_hilites[i].over = BL_HILITE_NONE;
-#endif /* STATUS_HILITES */
+        tty_status[NOW][i].idx = BL_FLUSH;
+        tty_status[NOW][i].color = NO_COLOR; /* no color */
+        tty_status[NOW][i].attr = ATR_NONE;
+        tty_status[NOW][i].x = tty_status[NOW][i].y = 0;
+        tty_status[NOW][i].valid  = FALSE;
+        tty_status[NOW][i].dirty  = FALSE;
+        tty_status[NOW][i].redraw = FALSE;
+        tty_status[NOW][i].sanitycheck = FALSE;
+        tty_status[BEFORE][i] = tty_status[NOW][i];
     }
+    tty_condition_bits = 0L;
+    hpbar_percent = 0, hpbar_color = NO_COLOR;
+#endif /* STATUS_HILITES */
+
+    /* let genl_status_init do most of the initialization */
+    genl_status_init();
+}
+
+void
+tty_status_enablefield(fieldidx, nm, fmt, enable)
+int fieldidx;
+const char *nm;
+const char *fmt;
+boolean enable;
+{
+    genl_status_enablefield(fieldidx, nm, fmt, enable);
 }
 
+#ifdef STATUS_HILITES
+
 /*
  *  *_status_update()
  *      -- update the value of a status field.
@@ -3561,286 +4059,813 @@ tty_status_init()
  *         BL_LEVELDESC, BL_EXP, BL_CONDITION
  *      -- fldindex could also be BL_FLUSH (-1), which is not really
  *         a field index, but is a special trigger to tell the
+ *         windowport that it should output all changes received
+ *         to this point. It marks the end of a bot() cycle.
+ *      -- fldindex could also be BL_RESET (-3), which is not really
+ *         a field index, but is a special advisory to to tell the
  *         windowport that it should redisplay all its status fields,
  *         even if no changes have been presented to it.
  *      -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
  *         If fldindex is BL_CONDITION, then ptr is a long value with
  *         any or none of the following bits set (from botl.h):
- *              BL_MASK_BLIND           0x00000001L
- *              BL_MASK_CONF            0x00000002L
- *              BL_MASK_FOODPOIS        0x00000004L
- *              BL_MASK_ILL             0x00000008L
- *              BL_MASK_HALLU           0x00000010L
- *              BL_MASK_STUNNED         0x00000020L
- *              BL_MASK_SLIMED          0x00000040L
- *      -- The value passed for BL_GOLD includes a leading
- *         symbol for GOLD "$:nnn". If the window port needs to use
- *         the textual gold amount without the leading "$:" the port
- *         will have to add 2 to the passed "ptr" for the BL_GOLD case.
+ *              BL_MASK_STONE           0x00000001L
+ *              BL_MASK_SLIME           0x00000002L
+ *              BL_MASK_STRNGL          0x00000004L
+ *              BL_MASK_FOODPOIS        0x00000008L
+ *              BL_MASK_TERMILL         0x00000010L
+ *              BL_MASK_BLIND           0x00000020L
+ *              BL_MASK_DEAF            0x00000040L
+ *              BL_MASK_STUN            0x00000080L
+ *              BL_MASK_CONF            0x00000100L
+ *              BL_MASK_HALLU           0x00000200L
+ *              BL_MASK_LEV             0x00000400L
+ *              BL_MASK_FLY             0x00000800L
+ *              BL_MASK_RIDE            0x00001000L
+ *      -- The value passed for BL_GOLD usually includes an encoded leading
+ *         symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
+ *         the textual gold amount without the leading "$:" the port will
+ *         have to skip past ':' in the passed "ptr" for the BL_GOLD case.
+ *      -- color is an unsigned int.
+ *               color_index = color & 0x00FF;         CLR_* value
+ *               attribute   = (color >> 8) & 0x00FF;  HL_ATTCLR_* mask
+ *         This holds the color and attribute that the field should
+ *         be displayed in.
+ *         This is relevant for everything except BL_CONDITION fldindex.
+ *         If fldindex is BL_CONDITION, this parameter should be ignored,
+ *         as condition highlighting is done via the next colormasks
+ *         parameter instead.
+ *
+ *      -- colormasks - pointer to cond_hilites[] array of colormasks.
+ *         Only relevant for BL_CONDITION fldindex. The window port
+ *         should ignore this parameter for other fldindex values.
+ *         Each condition bit must only ever appear in one of the
+ *         CLR_ array members, but can appear in multiple HL_ATTCLR_
+ *         offsets (because more than one attribute can co-exist).
+ *         See doc/window.doc for more details.
  */
+
 void
-tty_status_update(fldidx, ptr, chg, percent)
-int fldidx, chg, percent;
+tty_status_update(fldidx, ptr, chg, percent, color, colormasks)
+int fldidx, chg UNUSED, percent, color;
 genericptr_t ptr;
+unsigned long *colormasks;
 {
-    long cond, *condptr = (long *) ptr;
-    register int i;
+    int attrmask;
+    long *condptr = (long *) ptr;
     char *text = (char *) ptr;
-    /* Mapping BL attributes to tty attributes
-     * BL_HILITE_NONE     -1 + 3 = 2 (statusattr[2])
-     * BL_HILITE_INVERSE  -2 + 3 = 1 (statusattr[1])
-     * BL_HILITE_BOLD     -3 + 3 = 0 (statusattr[0])
-     */
-    int statusattr[] = { ATR_BOLD, ATR_INVERSE, ATR_NONE };
-    int attridx = 0;
-    long value = -1L;
-    static boolean beenhere = FALSE;
-    enum statusfields fieldorder[2][15] = {
-        { 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 },
-        { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
-          BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
-          BL_CAP, BL_CONDITION, BL_FLUSH }
-    };
+    char goldbuf[40], *lastchar, *p;
+    const char *fmt;
+    boolean reset_state = NO_RESET;
 
-    if (fldidx != BL_FLUSH) {
-        if (!status_activefields[fldidx])
-            return;
-        switch (fldidx) {
-        case BL_CONDITION:
-            cond = *condptr;
-            *status_vals[fldidx] = '\0';
-            if (cond & BL_MASK_BLIND)
-                Strcat(status_vals[fldidx], " Blind");
-            if (cond & BL_MASK_CONF)
-                Strcat(status_vals[fldidx], " Conf");
-            if (cond & BL_MASK_FOODPOIS)
-                Strcat(status_vals[fldidx], " FoodPois");
-            if (cond & BL_MASK_ILL)
-                Strcat(status_vals[fldidx], " Ill");
-            if (cond & BL_MASK_STUNNED)
-                Strcat(status_vals[fldidx], " Stun");
-            if (cond & BL_MASK_HALLU)
-                Strcat(status_vals[fldidx], " Hallu");
-            if (cond & BL_MASK_SLIMED)
-                Strcat(status_vals[fldidx], " Slime");
-            value = cond;
-            break;
-        default:
-            value = atol(text);
-            Sprintf(status_vals[fldidx],
-                    status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s",
-                    text);
-            break;
+    if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS))
+        return;
+
+    if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx])
+        return;
+
+    switch (fldidx) {
+    case BL_RESET:
+        reset_state = FORCE_RESET;
+        /*FALLTHRU*/
+    case BL_FLUSH:
+        if (make_things_fit(reset_state) || truncation_expected) {
+            render_status();
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
+            status_sanity_check();
+#endif
         }
+        return;
+    case BL_CONDITION:
+        tty_status[NOW][fldidx].idx = fldidx;
+        tty_condition_bits = *condptr;
+        tty_colormasks = colormasks;
+        tty_status[NOW][fldidx].valid = TRUE;
+        tty_status[NOW][fldidx].dirty = TRUE;
+        tty_status[NOW][fldidx].sanitycheck = TRUE;
+        truncation_expected = FALSE;
+        break;
+    case BL_GOLD:
+        text = decode_mixed(goldbuf, text);
+        /*FALLTHRU*/
+    default:
+        attrmask = (color >> 8) & 0x00FF;
+#ifndef TEXTCOLOR
+        color = NO_COLOR;
+#endif
+        fmt = status_fieldfmt[fldidx];
+        if (!fmt)
+            fmt = "%s";
+        /* should be checking for first enabled field here rather than
+           just first field, but 'fieldorder' doesn't start any rows
+           with fields which can be disabled so [any_row][0] suffices */
+        if (*fmt == ' ' && (fldidx == fieldorder[0][0]
+                            || fldidx == fieldorder[1][0]
+                            || fldidx == fieldorder[2][0]))
+            ++fmt; /* skip leading space for first field on line */
+        Sprintf(status_vals[fldidx], fmt, text);
+        tty_status[NOW][fldidx].idx = fldidx;
+        tty_status[NOW][fldidx].color = (color & 0x00FF);
+        tty_status[NOW][fldidx].attr = term_attr_fixup(attrmask);
+        tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]);
+        tty_status[NOW][fldidx].valid = TRUE;
+        tty_status[NOW][fldidx].dirty = TRUE;
+        tty_status[NOW][fldidx].sanitycheck = TRUE;
+        break;
+    }
 
-#ifdef STATUS_HILITES
-        switch (tty_status_hilites[fldidx].behavior) {
-        case BL_TH_NONE:
-            tty_status_colors[fldidx] = NO_COLOR;
-            break;
-        case BL_TH_UPDOWN:
-            if (chg > 0)
-                tty_status_colors[fldidx] = tty_status_hilites[fldidx].over;
-            else if (chg < 0)
-                tty_status_colors[fldidx] = tty_status_hilites[fldidx].under;
-            else
-                tty_status_colors[fldidx] = NO_COLOR;
-            break;
-        case BL_TH_VAL_PERCENTAGE:  {
-            int pct_th = 0;
+    /* The core botl engine sends a single blank to the window port
+       for carrying-capacity when its unused. Let's suppress that */
+    if (fldidx >= 0 && fldidx < MAXBLSTATS
+        && tty_status[NOW][fldidx].lth == 1
+        && status_vals[fldidx][0] == ' ') {
+        status_vals[fldidx][0] = '\0';
+        tty_status[NOW][fldidx].lth = 0;
+    }
 
-            if (tty_status_hilites[fldidx].thresholdtype != ANY_INT) {
-                impossible(
-                "tty_status_update: unsupported percentage threshold type %d",
-                           tty_status_hilites[fldidx].thresholdtype);
-            } else {
-                pct_th = tty_status_hilites[fldidx].threshold.a_int;
-                tty_status_colors[fldidx] = (percent >= pct_th)
-                                           ? tty_status_hilites[fldidx].over
-                                           : tty_status_hilites[fldidx].under;
+    /* default processing above was required before these */
+    switch (fldidx) {
+    case BL_HP:
+        if (iflags.wc2_hitpointbar) {
+            /* Special additional processing for hitpointbar */
+            hpbar_percent = percent;
+            hpbar_color = (color & 0x00FF);
+            tty_status[NOW][BL_TITLE].color = hpbar_color;
+            tty_status[NOW][BL_TITLE].dirty = TRUE;
+        }
+        break;
+    case BL_LEVELDESC:
+        dlvl_shrinklvl = 0; /* caller is passing full length string */
+        /*FALLTHRU*/
+    case BL_HUNGER:
+        /* The core sends trailing blanks for some fields.
+           Let's suppress the trailing blanks */
+        if (tty_status[NOW][fldidx].lth > 0) {
+            p = status_vals[fldidx];
+            for (lastchar = eos(p); lastchar > p && *--lastchar == ' '; ) {
+                *lastchar = '\0';
+                tty_status[NOW][fldidx].lth--;
             }
+        }
+        break;
+    case BL_TITLE:
+        /* when hitpointbar is enabled, rendering will enforce a length
+           of 30 on title, padding with spaces or truncating if necessary */
+        if (iflags.wc2_hitpointbar)
+            tty_status[NOW][fldidx].lth = 30 + 2; /* '[' and ']' */
+        break;
+    case BL_GOLD:
+        /* \GXXXXNNNN counts as 1 [moot since we use decode_mixed() above] */
+        if ((p = index(status_vals[fldidx], '\\')) != 0 && p[1] == 'G')
+            tty_status[NOW][fldidx].lth -= (10 - 1);
+        break;
+    case BL_CAP:
+        enc_shrinklvl = 0; /* caller is passing full length string */
+        enclev = stat_cap_indx();
+        break;
+    }
+    /* As of 3.6.2 we only render on BL_FLUSH (or BL_RESET) */
+    return;
+}
+
+STATIC_OVL int
+make_things_fit(force_update)
+boolean force_update;
+{
+    int trycnt, fitting = 0, requirement;
+    int rowsz[3], num_rows, condrow, otheroptions = 0;
+
+    num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
+    condrow = num_rows - 1; /* always last row, 1 for 0..1 or 2 for 0..2 */
+    cond_shrinklvl = 0;
+    if (enc_shrinklvl > 0 && num_rows == 2)
+        shrink_enc(0);
+    if (dlvl_shrinklvl > 0)
+        shrink_dlvl(0);
+    set_condition_length();
+    for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) {
+        /* FIXME: this remeasures each line every time even though it
+           is only attempting to shrink one of them and the other one
+           (or two) remains the same */
+        if (!check_fields(force_update, rowsz)) {
+            fitting = 0;
             break;
         }
-        case BL_TH_VAL_ABSOLUTE: {
-            int c = NO_COLOR;
-            int o = tty_status_hilites[fldidx].over;
-            int u = tty_status_hilites[fldidx].under;
-            anything *t = &tty_status_hilites[fldidx].threshold;
 
-            switch (tty_status_hilites[fldidx].thresholdtype) {
-            case ANY_LONG:
-                c = (value >= t->a_long) ? o : u;
-                break;
-            case ANY_INT:
-                c = (value >= t->a_int) ? o : u;
-                break;
-            case ANY_UINT:
-                c = ((unsigned long) value >= t->a_uint) ? o : u;
-                break;
-            case ANY_ULONG:
-                c = ((unsigned long) value >= t->a_ulong) ? o : u;
-                break;
-            case ANY_MASK32:
-                c = (value & t->a_ulong) ? o : u;
-                break;
-            default:
-                impossible(
-                "tty_status_update: unsupported absolute threshold type %d\n",
-                           tty_status_hilites[fldidx].thresholdtype);
+        requirement = rowsz[condrow] - 1;
+        if (requirement <= wins[WIN_STATUS]->cols - 1) {
+            fitting = requirement;
+            break;  /* we're good */
+        }
+        if (trycnt < 2) {
+            if (cond_shrinklvl < trycnt + 1) {
+                cond_shrinklvl = trycnt + 1;
+                set_condition_length();
+            }
+            continue;
+        }
+        if (cond_shrinklvl >= 2) {
+            /* We've exhausted the condition identifiers shrinkage,
+             * so let's try shrinking other things...
+             */
+            if (otheroptions < 2) {
+                /* try shrinking the encumbrance word, but
+                   only when it's on the same line as conditions */
+                if (num_rows == 2)
+                    shrink_enc(otheroptions + 1);
+            } else if (otheroptions == 2) {
+                shrink_dlvl(1);
+            } else {
+                /* Last resort - turn on trunction */
+                truncation_expected = TRUE;
                 break;
             }
-            tty_status_colors[fldidx] = c;
-            break;
-        } /* case */
-        } /* switch */
-#endif /* STATUS_HILITES */
+            ++otheroptions;
+        }
     }
+    return fitting;
+}
 
-    /* For now, this version copied from the genl_ version currently
-     * updates everything on the display, everytime
-     */
+/*
+ * This is the routine where we figure out where each field
+ * should be placed, and flag whether the on-screen details
+ * must be updated because they need to change.
+ * This is now done at an individual field case-by-case level.
+ */
+STATIC_OVL boolean
+check_fields(forcefields, sz)
+boolean forcefields;
+int sz[3];
+{
+    int c, i, row, col, num_rows, idx;
+    boolean valid = TRUE, matchprev, update_right;
 
-    if (!beenhere || !iflags.use_status_hilites) {
-        char newbot1[MAXCO], newbot2[MAXCO];
+    if (!windowdata_init && !check_windowdata())
+        return FALSE;
 
-        newbot1[0] = '\0';
-        for (i = 0; fieldorder[0][i] >= 0; ++i) {
-            int idx1 = fieldorder[0][i];
+    num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
 
-            if (status_activefields[idx1])
-                Strcat(newbot1, status_vals[idx1]);
-        }
-        newbot2[0] = '\0';
-        for (i = 0; fieldorder[1][i] >= 0; ++i) {
-            int idx2 = fieldorder[1][i];
+    for (row = 0; row < num_rows; ++row) {
+        sz[row] = 0;
+        col = 1;
+        update_right = FALSE;
+        for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
+            if (!status_activefields[idx])
+                continue;
+            if (!tty_status[NOW][idx].valid)
+                valid = FALSE;
+            /* might be called more than once for shrink tests, so need
+               to reset these (redraw and x at any rate) each time */
+            tty_status[NOW][idx].redraw = FALSE;
+            tty_status[NOW][idx].y = row;
+            tty_status[NOW][idx].x = col;
+
+            /* On a change to the field location, everything further
+               to the right must be updated as well.  (Not necessarily
+               everything; it's possible for complementary changes across
+               multiple fields to put stuff further right back in sync.) */
+            if (tty_status[NOW][idx].x + tty_status[NOW][idx].lth
+                != tty_status[BEFORE][idx].x + tty_status[BEFORE][idx].lth)
+                update_right = TRUE;
+            else if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth
+                     || tty_status[NOW][idx].x != tty_status[BEFORE][idx].x)
+                tty_status[NOW][idx].redraw = TRUE;
+            else /* in case update_right is set, we're back in sync now */
+                update_right = FALSE;
+
+            matchprev = FALSE; /* assume failure */
+            if (valid && !update_right && !forcefields
+                && !tty_status[NOW][idx].redraw) {
+                /*
+                 * Check values against those already on the display.
+                 *  - Is the additional processing time for this worth it?
+                 */
+                if (do_field_opt
+                    /* color/attr checks aren't right for 'condition'
+                       and neither is examining status_vals[BL_CONDITION]
+                       so skip same-contents optimization for conditions */
+                    && idx != BL_CONDITION
+                    && (tty_status[NOW][idx].color
+                        == tty_status[BEFORE][idx].color)
+                    && (tty_status[NOW][idx].attr
+                        == tty_status[BEFORE][idx].attr)) {
+                    matchprev = TRUE; /* assume success */
+                    if (tty_status[NOW][idx].dirty) {
+                        /* compare values */
+                        const char *ob, *nb; /* old byte, new byte */
+                        struct WinDesc *cw = wins[WIN_STATUS];
+
+                        c = col - 1;
+                        ob = &cw->data[row][c];
+                        nb = status_vals[idx];
+                        while (*nb && c < cw->cols) {
+                            if (*nb != *ob)
+                                break;
+                            nb++;
+                            ob++;
+                            c++;
+                        }
+                        /* if we're not at the end of new string, no match;
+                           we don't need to worry about whether there might
+                           be leftover old string; that could only happen
+                           if they have different lengths, in which case
+                           'update_right' will be set and we won't get here */
+                        if (*nb)
+                            matchprev = FALSE;
+#if 0
+                        if (*nb || (*ob && *ob != ' '
+                                    && (*ob != '/' || idx != BL_XP)
+                                    && (*ob != '(' || (idx != BL_HP
+                                                       && idx != BL_ENE))))
+                            matchprev = FALSE;
+#endif
+                    }
+                }
+            }
+
+            if (forcefields || update_right
+                || (tty_status[NOW][idx].dirty && !matchprev))
+                tty_status[NOW][idx].redraw = TRUE;
 
-            if (status_activefields[idx2])
-                Strcat(newbot2, status_vals[idx2]);
+            col += tty_status[NOW][idx].lth;
         }
+        sz[row] = col;
+    }
+    return valid;
+}
 
-        curs(WIN_STATUS, 1, 0);
-        putstr(WIN_STATUS, 0, newbot1);
-        curs(WIN_STATUS, 1, 1);
-        putmixed(WIN_STATUS, 0, newbot2); /* putmixed() due to GOLD glyph */
-        beenhere = TRUE;
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
+STATIC_OVL void
+status_sanity_check(VOID_ARGS)
+{
+    int i;
+    static boolean in_sanity_check = FALSE;
+    static const char *const idxtext[] = {
+        "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5   */
+        "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD",     /* 6.. 10  */
+        "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD",       /* 11.. 15 */
+        "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX",           /* 16.. 19 */
+        "BL_LEVELDESC", "BL_EXP", "BL_CONDITION"              /* 20.. 22 */
+    };
+   
+    if (in_sanity_check)
         return;
+    in_sanity_check = TRUE;
+    /*
+     * Make sure that every field made it down to the
+     * bottom of the render_status() for-loop.
+     */
+    for (i = 0; i < MAXBLSTATS; ++i) {
+        if (tty_status[NOW][i].sanitycheck) {
+            char panicmsg[BUFSZ];
+
+            Sprintf(panicmsg, "failed on tty_status[NOW][%s].", idxtext[i]);
+            paniclog("status_sanity_check", panicmsg);
+            tty_status[NOW][i].sanitycheck = FALSE;
+            /*
+             * Attention developers: If you encounter the above
+             * message in paniclog, it almost certainly means that
+             * a recent code change has caused a failure to reach
+             * the bottom of render_status(), at least for the BL_
+             * field identified in the impossible() message.
+             *
+             * That could be because of the addition of a continue
+             * statement within the render_status() for-loop, or a
+             * premature return from render_status() before it finished
+             * its housekeeping chores.
+             */
+        }
     }
+    in_sanity_check = FALSE;
+}
+#endif /* NHDEVEL_STATUS */
+
+/*
+ * This is what places a field on the tty display.
+ */
+STATIC_OVL void
+tty_putstatusfield(text, x, y)
+const char *text;
+int x, y;
+{
+    int i, n, ncols, nrows, lth = 0;
+    struct WinDesc *cw = 0;
 
-    curs(WIN_STATUS, 1, 0);
-    for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) {
-        int fldidx1 = fieldorder[0][i];
+    if (WIN_STATUS == WIN_ERR
+        || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0)
+        panic("tty_putstatusfield: Invalid WinDesc\n");
 
-        if (status_activefields[fldidx1]) {
-            if (tty_status_colors[fldidx1] < 0 &&
-                    tty_status_colors[fldidx1] >= -3) {
-                /* attribute, not a color */
-                attridx = tty_status_colors[fldidx1] + 3;
-                term_start_attr(statusattr[attridx]);
-                putstr(WIN_STATUS, 0, status_vals[fldidx1]);
-                term_end_attr(statusattr[attridx]);
-#ifdef TEXTCOLOR
-            } else if (tty_status_colors[fldidx1] != CLR_MAX) {
-                if (tty_status_colors[fldidx1] != NO_COLOR)
-                    term_start_color(tty_status_colors[fldidx1]);
-                putstr(WIN_STATUS, 0, status_vals[fldidx1]);
-                if (tty_status_colors[fldidx1] != NO_COLOR)
-                    term_end_color();
+    ncols = cw->cols;
+    nrows = cw->maxrow;
+    lth = (int) strlen(text);
+
+    print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS);
+
+    if (x < ncols && y < nrows) {
+        if (x != cw->curx || y != cw->cury)
+            tty_curs(NHW_STATUS, x, y);
+        for (i = 0; i < lth; ++i) {
+            n = i + x;
+            if (n < ncols && *text) {
+#if 0 /*JP*/
+                (void) putchar(*text);
+#else
+                (void) jputchar(*(unsigned char *)text);
 #endif
-            } else
-                putstr(WIN_STATUS, 0, status_vals[fldidx1]);
+                ttyDisplay->curx++;
+                cw->curx++;
+                cw->data[y][n - 1] = *text;
+                text++;
+            }
+        }
+    }
+#if 0
+    else {
+        if (truncation_expected) {
+        /* Now we're truncating */
+            ; /* but we knew in advance */
         }
     }
-    curs(WIN_STATUS, 1, 1);
-    for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) {
-        int fldidx2 = fieldorder[1][i];
-
-        if (status_activefields[fldidx2]) {
-            if (tty_status_colors[fldidx2] < 0 &&
-                    tty_status_colors[fldidx2] >= -3) {
-                /* attribute, not a color */
-                attridx = tty_status_colors[fldidx2] + 3;
-                term_start_attr(statusattr[attridx]);
-                putstr(WIN_STATUS, 0, status_vals[fldidx2]);
-                term_end_attr(statusattr[attridx]);
-#ifdef TEXTCOLOR
-            } else if (tty_status_colors[fldidx2] != CLR_MAX) {
-                if (tty_status_colors[fldidx2] != NO_COLOR)
-                    term_start_color(tty_status_colors[fldidx2]);
-                if (fldidx2 == BL_GOLD) {
-                    /* putmixed() due to GOLD glyph */
-                   putmixed(WIN_STATUS, 0, status_vals[fldidx2]);
-                } else {
-                   putstr(WIN_STATUS, 0, status_vals[fldidx2]);
-                }
-                if (tty_status_colors[fldidx2] != NO_COLOR)
-                    term_end_color();
 #endif
-            } else
-                putstr(WIN_STATUS, 0, status_vals[fldidx2]);
+}
+
+/* caller must set cond_shrinklvl (0..2) before calling us */
+STATIC_OVL void
+set_condition_length()
+{
+    long mask;
+    int c, lth = 0;
+
+    if (tty_condition_bits) {
+        for (c = 0; c < SIZE(conditions); ++c) {
+            mask = conditions[c].mask;
+            if ((tty_condition_bits & mask) == mask)
+                lth += 1 + (int) strlen(conditions[c].text[cond_shrinklvl]);
         }
     }
-    return;
+    tty_status[NOW][BL_CONDITION].lth = lth;
+}
+
+STATIC_OVL void
+shrink_enc(lvl)
+int lvl;
+{
+    /* shrink or restore the encumbrance word */
+    if (lvl <= 2) {
+        enc_shrinklvl = lvl;
+        Sprintf(status_vals[BL_CAP], " %s", encvals[lvl][enclev]);
+    }
+    tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]);
+}
+
+STATIC_OVL void
+shrink_dlvl(lvl)
+int lvl;
+{
+    /* try changing Dlvl: to Dl: */
+    char buf[BUFSZ];
+    char *levval = index(status_vals[BL_LEVELDESC], ':');
+
+    if (levval) {
+        dlvl_shrinklvl = lvl;
+        Strcpy(buf, (lvl == 0) ? "Dlvl" : "Dl");
+        Strcat(buf, levval);
+        Strcpy(status_vals[BL_LEVELDESC], buf);
+        tty_status[NOW][BL_LEVELDESC].lth = strlen(status_vals[BL_LEVELDESC]);
+    }
 }
 
-#ifdef STATUS_HILITES
 /*
- *  status_threshold(int fldidx, int threshholdtype, anything threshold,
- *                   int behavior, int under, int over)
- *
- *        -- called when a hiliting preference is added, changed, or
- *           removed.
- *        -- the fldindex identifies which field is having its hiliting
- *           preference set. It is an integer index value from botl.h
- *        -- fldindex could be any one of the following from botl.h:
- *           BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
- *           BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
- *           BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
- *           BL_LEVELDESC, BL_EXP, BL_CONDITION
- *        -- datatype is P_INT, P_UINT, P_LONG, or P_MASK.
- *        -- threshold is an "anything" union which can contain the
- *           datatype value.
- *        -- behavior is used to define how threshold is used and can
- *           be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE,
- *           or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above
- *           or below the threshold.  BL_TH_VAL_PERCENTAGE treats the
- *           threshold value as a precentage of the maximum possible
- *           value. BL_TH_VAL_ABSOLUTE means that the threshold is an
- *           actual value. BL_TH_UPDOWN means that threshold is not
- *           used, and the two below/above hilite values indicate how
- *           to display something going down (under) or rising (over).
- *        -- under is the hilite attribute used if value is below the
- *           threshold. The attribute can be BL_HILITE_NONE,
- *           BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
- *           of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
- *           CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
- *           CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
- *           CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
- *        -- over is the hilite attribute used if value is at or above
- *           the threshold. The attribute can be BL_HILITE_NONE,
- *           BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
- *           of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
- *           CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
- *           CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
- *           CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
+ * Ensure the underlying status window data start out
+ * blank and null-terminated.
  */
-void
-tty_status_threshold(fldidx, thresholdtype, threshold, behavior, under, over)
-int fldidx, thresholdtype;
-int behavior, under, over;
-anything threshold;
-{
-    tty_status_hilites[fldidx].thresholdtype = thresholdtype;
-    tty_status_hilites[fldidx].threshold = threshold;
-    tty_status_hilites[fldidx].behavior = behavior;
-    tty_status_hilites[fldidx].under = under;
-    tty_status_hilites[fldidx].over = over;
+STATIC_OVL boolean
+check_windowdata(VOID_ARGS)
+{
+    if (WIN_STATUS == WIN_ERR || wins[WIN_STATUS] == (struct WinDesc *) 0) {
+        paniclog("check_windowdata", " null status window.");
+        return FALSE;
+    } else if (!windowdata_init) {
+        tty_clear_nhwindow(WIN_STATUS); /* also sets cw->data[] to spaces */
+        windowdata_init = TRUE;
+    }
+    return TRUE;
+}
+
+#ifdef TEXTCOLOR
+/*
+ * Return what color this condition should
+ * be displayed in based on user settings.
+ */
+STATIC_OVL int
+condcolor(bm, bmarray)
+long bm;
+unsigned long *bmarray;
+{
+    int i;
+
+    if (bm && bmarray)
+        for (i = 0; i < CLR_MAX; ++i) {
+            if ((bm & bmarray[i]) != 0)
+                return i;
+        }
+    return NO_COLOR;
+}
+#else
+/* might need something more elaborate if some compiler complains that
+   the condition where this gets used always has the same value */
+#define condcolor(bm,bmarray) NO_COLOR
+#define term_start_color(color) /*empty*/
+#define term_end_color() /*empty*/
+#endif /* TEXTCOLOR */
+
+STATIC_OVL int
+condattr(bm, bmarray)
+long bm;
+unsigned long *bmarray;
+{
+    int attr = 0;
+    int i;
+
+    if (bm && bmarray) {
+        for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
+            if ((bm & bmarray[i]) != 0) {
+                switch (i) {
+                case HL_ATTCLR_DIM:
+                    attr |= HL_DIM;
+                    break;
+                case HL_ATTCLR_BLINK:
+                    attr |= HL_BLINK;
+                    break;
+                case HL_ATTCLR_ULINE:
+                    attr |= HL_ULINE;
+                    break;
+                case HL_ATTCLR_INVERSE:
+                    attr |= HL_INVERSE;
+                    break;
+                case HL_ATTCLR_BOLD:
+                    attr |= HL_BOLD;
+                    break;
+                }
+            }
+        }
+    }
+    return attr;
+}
+
+#define Begin_Attr(m) \
+    do {                                        \
+        if (m) {                                \
+            if ((m) & HL_BOLD)                  \
+                term_start_attr(ATR_BOLD);      \
+            if ((m) & HL_INVERSE)               \
+                term_start_attr(ATR_INVERSE);   \
+            if ((m) & HL_ULINE)                 \
+                term_start_attr(ATR_ULINE);     \
+            if ((m) & HL_BLINK)                 \
+                term_start_attr(ATR_BLINK);     \
+            if ((m) & HL_DIM)                   \
+                term_start_attr(ATR_DIM);       \
+        }                                       \
+    } while (0)
+
+#define End_Attr(m) \
+    do {                                        \
+        if (m) {                                \
+            if ((m) & HL_DIM)                   \
+                term_end_attr(ATR_DIM);         \
+            if ((m) & HL_BLINK)                 \
+                term_end_attr(ATR_BLINK);       \
+            if ((m) & HL_ULINE)                 \
+                term_end_attr(ATR_ULINE);       \
+            if ((m) & HL_INVERSE)               \
+                term_end_attr(ATR_INVERSE);     \
+            if ((m) & HL_BOLD)                  \
+                term_end_attr(ATR_BOLD);        \
+        }                                       \
+    } while (0)
+
+STATIC_OVL void
+render_status(VOID_ARGS)
+{
+    long mask, bits;
+    int i, x, y, idx, c, row, tlth, num_rows, coloridx = 0, attrmask = 0;
+    char *text;
+    struct WinDesc *cw = 0;
+
+    if (WIN_STATUS == WIN_ERR
+        || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) {
+        paniclog("render_status", "WIN_ERR on status window.");
+        return;
+    }
+
+    num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
+    for (row = 0; row < num_rows; ++row) {
+        HUPSKIP();
+        y = row;
+        tty_curs(WIN_STATUS, 1, y);
+        for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
+            if (!status_activefields[idx])
+                continue;
+            x = tty_status[NOW][idx].x;
+            text = status_vals[idx]; /* always "" for BL_CONDITION */
+            tlth = (int) tty_status[NOW][idx].lth; /* valid for BL_CONDITION */
+
+            if (tty_status[NOW][idx].redraw || !do_field_opt) {
+                boolean hitpointbar = (idx == BL_TITLE
+                                       && iflags.wc2_hitpointbar);
+
+                if (idx == BL_CONDITION) {
+                    /*
+                     * +-----------------+
+                     * | Condition Codes |
+                     * +-----------------+
+                     */
+                    bits = tty_condition_bits;
+                    /* if no bits are set, we can fall through condition
+                       rendering code to finalx[] handling (and subsequent
+                       rest-of-line erasure if line is shorter than before) */
+                    if (num_rows == 3 && bits != 0L) {
+                        int k;
+                        char *dat = &cw->data[y][0];
+
+                        /* line up with hunger (or where it would have
+                           been when currently omitted); if there isn't
+                           enough room for that, right justify; or place
+                           as-is if not even enough room for /that/; we
+                           expect hunger to be on preceding row, in which
+                           case its current data has been moved to [BEFORE] */
+                        if (tty_status[BEFORE][BL_HUNGER].y < row
+                            && x < tty_status[BEFORE][BL_HUNGER].x
+                            && (tty_status[BEFORE][BL_HUNGER].x + tlth
+                                < cw->cols - 1))
+                            k = tty_status[BEFORE][BL_HUNGER].x;
+                        else if (x + tlth < cw->cols - 1)
+                            k = cw->cols - tlth;
+                        else
+                            k = x;
+                        while (x < k) {
+                            if (dat[x - 1] != ' ')
+                                tty_putstatusfield(" ", x, y);
+                            ++x;
+                        }
+                        tty_status[NOW][BL_CONDITION].x = x;
+                        tty_curs(WIN_STATUS, x, y);
+                    }
+                    for (c = 0; c < SIZE(conditions) && bits != 0L; ++c) {
+                        mask = conditions[c].mask;
+                        if (bits & mask) {
+                            const char *condtext;
+
+                            tty_putstatusfield(" ", x++, y);
+                            if (iflags.hilite_delta) {
+                                attrmask = condattr(mask, tty_colormasks);
+                                Begin_Attr(attrmask);
+                                coloridx = condcolor(mask, tty_colormasks);
+                                if (coloridx != NO_COLOR)
+                                    term_start_color(coloridx);
+                            }
+                            condtext = conditions[c].text[cond_shrinklvl];
+                            if (x >= cw->cols && !truncation_expected) {
+                                impossible(
+                         "Unexpected condition placement overflow for \"%s\"",
+                                           condtext);
+                                condtext = "";
+                                bits = 0L; /* skip any remaining conditions */
+                            }
+                            tty_putstatusfield(condtext, x, y);
+                            x += (int) strlen(condtext);
+                            if (iflags.hilite_delta) {
+                                if (coloridx != NO_COLOR)
+                                    term_end_color();
+                                End_Attr(attrmask);
+                            }
+                            bits &= ~mask;
+                        }
+                    }
+                    /* 'x' is 1-based and 'cols' and 'data' are 0-based,
+                       so x==cols means we just stored in data[N-2] and
+                       are now positioned at data[N-1], the terminator;
+                       that's ok as long as we don't write there */
+                    if (x > cw->cols) {
+                        static unsigned once_only = 0;
+
+                        if (!truncation_expected && !once_only++)
+                            paniclog("render_status()",
+                                     " unexpected truncation.");
+                        x = cw->cols;
+                    }
+                } else if (hitpointbar) {
+                    /*
+                     * +-------------------------+
+                     * | Title with Hitpoint Bar |
+                     * +-------------------------+
+                     */
+                    /* hitpointbar using hp percent calculation */
+                    int bar_len, bar_pos = 0;
+                    char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0';
+                    boolean twoparts = (hpbar_percent < 100);
+
+                    /* force exactly 30 characters, padded with spaces
+                       if shorter or truncated if longer */
+                    if (strlen(text) != 30) {
+                        Sprintf(bar, "%-30.30s", text);
+                        Strcpy(status_vals[BL_TITLE], bar);
+                    } else
+                        Strcpy(bar, text);
+                    bar_len = (int) strlen(bar); /* always 30 */
+                    tlth = bar_len + 2;
+                    /* when at full HP, the whole title will be highlighted;
+                       when injured or dead, there will be a second portion
+                       which is not highlighted */
+                    if (twoparts) {
+                        /* figure out where to separate the two parts */
+                        bar_pos = (bar_len * hpbar_percent) / 100;
+                        if (bar_pos < 1 && hpbar_percent > 0)
+                            bar_pos = 1;
+                        if (bar_pos >= bar_len && hpbar_percent < 100)
+                            bar_pos = bar_len - 1;
+                        bar2 = &bar[bar_pos];
+                        savedch = *bar2;
+                        *bar2 = '\0';
+                    }
+                    tty_putstatusfield("[", x++, y);
+                    if (*bar) { /* always True, unless twoparts+dead (0 HP) */
+                        term_start_attr(ATR_INVERSE);
+                        if (iflags.hilite_delta && hpbar_color != NO_COLOR)
+                            term_start_color(hpbar_color);
+                        tty_putstatusfield(bar, x, y);
+                        x += (int) strlen(bar);
+                        if (iflags.hilite_delta && hpbar_color != NO_COLOR)
+                            term_end_color();
+                        term_end_attr(ATR_INVERSE);
+                    }
+                    if (twoparts) { /* no highlighting for second part */
+                        *bar2 = savedch;
+                        tty_putstatusfield(bar2, x, y);
+                        x += (int) strlen(bar2);
+                    }
+                    tty_putstatusfield("]", x++, y);
+                } else {
+                    /*
+                     * +-----------------------------+
+                     * | Everything else that is not |
+                     * |   in a special case above   |
+                     * +-----------------------------+
+                     */
+                    if (iflags.hilite_delta) {
+                        while (*text == ' ') {
+                            tty_putstatusfield(" ", x++, y);
+                            text++;
+                        }
+                        if (*text == '/' && idx == BL_EXP) {
+                            tty_putstatusfield("/", x++, y);
+                            text++;
+                        }
+                        attrmask = tty_status[NOW][idx].attr;
+                        Begin_Attr(attrmask);
+                        coloridx = tty_status[NOW][idx].color;
+                        if (coloridx != NO_COLOR)
+                            term_start_color(coloridx);
+                    }
+                    tty_putstatusfield(text, x, y);
+                    x += (int) strlen(text);
+                    if (iflags.hilite_delta) {
+                        if (coloridx != NO_COLOR)
+                            term_end_color();
+                        End_Attr(attrmask);
+                    }
+                }
+            } else {
+                /* not rendered => same text as before */
+                x += tlth;
+            }
+            finalx[row][NOW] = x - 1;
+            /* reset .redraw and .dirty now that field has been rendered */
+            tty_status[NOW][idx].dirty  = FALSE;
+            tty_status[NOW][idx].redraw = FALSE;
+            tty_status[NOW][idx].sanitycheck = FALSE;
+            /*
+             * For comparison of current and previous:
+             * - Copy the entire tty_status struct.
+             */
+            tty_status[BEFORE][idx] = tty_status[NOW][idx];
+        }
+        x = finalx[row][NOW];
+        if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE])
+            && x + 1 < cw->cols) {
+            tty_curs(WIN_STATUS, x + 1, y);
+            cl_end();
+        }
+        /*
+         * For comparison of current and previous:
+         * - Copy the last written column number on the row.
+         */
+        finalx[row][BEFORE] = finalx[row][NOW];
+    }
     return;
 }
 
 #endif /* STATUS_HILITES */
-#endif /*STATUS_VIA_WINDOWPORT*/
 
 #endif /* TTY_GRAPHICS */