]> granicus.if.org Git - nethack/commitdiff
move some parts of TTY_PERM_INVENT into core
authornhmall <nhmall@nethack.org>
Fri, 24 Jun 2022 19:01:38 +0000 (15:01 -0400)
committernhmall <nhmall@nethack.org>
Fri, 24 Jun 2022 19:01:38 +0000 (15:01 -0400)
This starts the tty perm_invent just in time later in the
startup rather than initializing it with the other
game windows.

This also splits the duties:

The core will inquire from the window port about how many
inventory slots it can fill.

The core will handle figuring out the inventory text and
inventory letters, and will do the traversing of internal
data structures like obj chains, and passing customization
options on to the window port.

The window port will look after placing each inventory slot's
text at an appropriate location on the screen.

This, in theory, makes the core-portion available for
window ports other than tty to use, though none currently do.
The decision of what goes in an inventory slot is all left up
to the core with the update_invent_slot interface.

Documentation updates will come later, not at this time.

include/decl.h
include/extern.h
include/wintty.h
include/wintype.h
src/allmain.c
src/decl.c
src/invent.c
src/options.c
win/tty/wintty.c

index 67598d852fe431ed8528addcd72492258a61dae4..c66a98c117424cd384c0327a23553430201860dd 100644 (file)
@@ -972,9 +972,9 @@ struct instance_globals {
        persistent one doesn't get shrunk during filtering for item selection
        then regrown to full inventory, possibly being resized in the process */
     winid cached_pickinv_win;
-#ifdef TTY_PERM_INVENT
-    winid tty_invent_win;
-#endif
+    winid perm_invent_win;
+    int core_invent_state;
+
     /* query objlist callback: return TRUE if obj type matches "this_type" */
     int this_type;
     const char *this_title; /* title for inventory list of specific type */
index f9525d6fdf471d0e7f50a17abe218cfb3685a255..e3ebc56efdd84e8d4a5ef0a956c3fe1c2d477257 100644 (file)
@@ -1174,6 +1174,7 @@ extern long count_contents(struct obj *, boolean, boolean, boolean, boolean);
 extern void carry_obj_effects(struct obj *);
 extern const char *currency(long);
 extern void silly_thing(const char *, struct obj *);
+extern void core_update_invent_slot(void);
 
 /* ### ioctl.c ### */
 
index 552684d4c9f82052ba4ca46e99cc3a123832487a..39f8dddd9e2a9f99e44526cdf02150fb06dc10d2 100644 (file)
@@ -11,6 +11,7 @@
 #define WINDOW_STRUCTS
 
 #ifdef TTY_PERM_INVENT
+
 enum { tty_pi_minrow = 28, tty_pi_mincol = 79 };
 /* for static init of zerottycell, put pointer first */
 union ttycellcontent {
@@ -123,11 +124,7 @@ struct tty_status_fields {
 #ifdef NHW_BASE
 #undef NHW_BASE
 #endif
-#define NHW_BASE (NHW_TEXT + 1)
-
-#ifdef TTY_PERM_INVENT
-#define NHW_TTYINVENT (NHW_BASE + 1)
-#endif
+#define NHW_BASE (NHW_LAST_TYPE + 1)
 
 extern struct window_procs tty_procs;
 
index e262b1aa97e17b3f0d94d3bca8e3d34f844047f5..010ea2dbb77f3bbcd3bbe5040b719a4f206b08ba 100644 (file)
@@ -111,6 +111,8 @@ typedef struct gi {
 #define NHW_MAP 3
 #define NHW_MENU 4
 #define NHW_TEXT 5
+#define NHW_PERMINVENT 6
+#define NHW_LAST_TYPE NHW_PERMINVENT
 
 /* attribute types for putstr; the same as the ANSI value, for convenience */
 #define ATR_NONE       0
@@ -162,19 +164,37 @@ typedef struct gi {
 
 #define MENU_BEHAVE_STANDARD      0x0000000U
 
+/* inventory modes */
+enum inv_modes { InvNormal = 0, InvShowGold = 1, InvSparse = 2, InvInUse = 4 };
+
+enum to_core_flags {
+    active           = 0x001,
+    prohibited       = 0x002,
+    no_init_done     = 0x004
+};
+
+enum from_core_requests {
+    request_settings = 1,
+    update_slot      = 2,
+    render           = 3
+};
+
 struct to_core {
+    long tocore_flags;
     boolean active;
     boolean use_update_inventory;    /* disable the newer slot interface */
-    int slotcount;
-    int low_slot_num, high_slot_num;
-    int max_slot_text;
+    int maxslot;
+    int needrows, needcols;
+    int haverows, havecols;
 };
 
 struct from_core {
-    long piflags;
-    int slot;           /* which inventory slot; 0 means info exchange only */
+    enum from_core_requests core_request;
+    enum inv_modes invmode;
+    boolean force_redraw;
+    int slot;           /* which inventory slot; 0 indicates request */
     int invlet;
-    const char *text;   /* the text to display */
+    char text[BUFSZ];
 };
 
 struct perminvent_info_t {
@@ -184,6 +204,8 @@ struct perminvent_info_t {
 
 typedef struct perminvent_info_t perminvent_info;
 
+#define CORE_INVENT
+
 /* clang-format on */
 
 #endif /* WINTYPE_H */
index a39f5d21638bdda4d73d8850561121922a1433e8..c255ba18a018a3b5ac1920192437b12403e8ef13 100644 (file)
 #include <signal.h>
 #endif
 
-#ifdef TTY_PERM_INVENT
-#include "wintty.h"     /* just for define of NHW_TTYINVENT */
-#endif
-
 static void moveloop_preamble(boolean);
 static void u_calc_moveamt(int);
 #ifdef POSITIONBAR
@@ -635,11 +631,6 @@ display_gamewindows(void)
        ever having been used, use it here to pacify the Qt interface */
     start_menu(WIN_INVEN, 0U), end_menu(WIN_INVEN, (char *) 0);
 
-#ifdef TTY_PERM_INVENT
-    if (WINDOWPORT("tty") && iflags.perm_invent) {
-        g.tty_invent_win = create_nhwindow(NHW_TTYINVENT);
-    }
-#endif
 #ifdef MAC
     /* This _is_ the right place for this - maybe we will
      * have to split display_gamewindows into create_gamewindows
@@ -659,11 +650,7 @@ display_gamewindows(void)
     display_nhwindow(WIN_MESSAGE, FALSE);
     clear_glyph_buffer();
     display_nhwindow(WIN_MAP, FALSE);
-#ifdef TTY_PERM_INVENT
-    if (g.tty_invent_win != WIN_ERR)
-        display_nhwindow(g.tty_invent_win, FALSE);
-#endif
-}
+ }
 
 void
 newgame(void)
index f0c032d05a1f1e2e0af5e1437684d5035dedf77c..f724786325472795379e87dd80fb051fcea1bbb2 100644 (file)
@@ -435,9 +435,8 @@ const struct instance_globals g_init = {
     NULL, /* invbuf */
     0, /* inbufsize */
     WIN_ERR, /* cached_pickinv_win */
-#ifdef TTY_PERM_INVENT
-    WIN_ERR, /* tty_invent_win */
-#endif
+    WIN_ERR, /* perm_invent_win */
+    0,       /* core_invent_state */
     0, /* this_type */
     NULL, /* this_title */
     UNDEFINED_VALUES, /* only (coord) */
index 7d3677b410e2e8147f9a848559b311f2d4bf6c87..44090afe01169d397735ab473c5a2ffe2f2295ff 100644 (file)
@@ -2429,9 +2429,13 @@ update_inventory(void)
      */
     save_suppress_price = iflags.suppress_price;
     iflags.suppress_price = 0;
-
-    (*windowprocs.win_update_inventory)(0);
-
+#if defined(TTY_PERM_INVENT) && defined(CORE_INVENT)
+    if (WINDOWPORT("tty"))
+        core_update_invent_slot();
+    else
+#else
+        (*windowprocs.win_update_inventory)(0);
+#endif
     iflags.suppress_price = save_suppress_price;
 }
 
@@ -5333,4 +5337,188 @@ display_binventory(int x, int y, boolean as_if_seen)
     return n;
 }
 
+#if defined(CORE_INVENT)
+/* enum and structs are defined in wintype.h */
+static perminvent_info zeropi = { 0 };
+static perminvent_info pi_info;
+static int invmode = InvNormal;
+static char Empty[1] = { '\0' };
+#ifdef TTY_PERM_INVENT
+extern void tty_perm_invent_toggled(boolean negated);
+#endif
+
+void
+core_update_invent_slot()
+{
+    static perminvent_info *pi = 0;
+    char *text, nxtlet;
+    int slot;
+    boolean show_gold = (invmode & InvShowGold) != 0,
+            inuse_only = (invmode & InvInUse) != 0,
+            sparse = (invmode & InvSparse) != 0;
+    const char *wport_id;
+    struct obj *obj;
+
+    if (g.perm_invent_win == WIN_ERR && g.core_invent_state)
+            return;
+
+    if (!iflags.perm_invent && g.core_invent_state) {
+        /* Odd - but this could be end-of-game disclosure
+         * which just sets boolean iflag.perm_invent to
+         * FALSE without actually doing anything else.
+         */
+#ifdef TTY_PERM_INVENT
+        if (WINDOWPORT("tty"))
+            tty_perm_invent_toggled(TRUE); /* TRUE means negated */
+#endif
+        (void) doredraw();
+        return;
+    }
+
+    /*
+     * The core looks after what content goes into the
+     * inventory slots, and deals with things like obj
+     * chains etc, so the window port doesn't have to.
+     *
+     * The window port informs the core of the number of
+     * slots that it will process.
+     *
+     * The core tells the window port what the contents of the
+     * inventory slots should be.
+     *
+     * The core requests the window port when to render, after
+     * all the content has been updated.
+     *
+     * The window port looks after the placement of an inventory
+     * slot's contents onto the display in an appropriate fashion,
+     * The core doesn't care, and leaves that up to the window port.
+     *
+     * The core slot handling is no longer tied to TTY_PERM_INVENT,
+     * although at this point that's the only window port to utilize
+     * it. The rest are still rolling their own via the basic
+     * [port]_update_inventory() mechanism.
+     */
+
+    if (WINDOWPORT("tty") && iflags.perm_invent)
+        wport_id = "tty perm_invent";
+    else
+        wport_id = "perm_invent";
+
+    pi_info.fromcore.core_request = 0;
+    if (!g.core_invent_state) {
+        {
+            /*TEMPORARY*/
+            char *envtmp = nh_getenv("TTYINV");
+            invmode = envtmp ? atoi(envtmp) : InvNormal;
+        }
+        pi_info.fromcore.invmode = invmode;
+        pi_info = zeropi;
+        /* Send the wport a request to get the related settings. */
+        pi_info.fromcore.core_request = request_settings;
+        if ((pi = update_invent_slot(g.perm_invent_win, (slot = 0), &pi_info))) {
+            if ((pi->tocore.tocore_flags & prohibited) != 0) {
+                /* sizes aren't good enough */
+                set_option_mod_status("perm_invent", set_gameview);
+                iflags.perm_invent = FALSE;
+                pline("%s could not be enabled.", wport_id); 
+                pline("%s needs a terminal that is at least %dx%d, yours is %dx%d.",
+                      wport_id,
+                      pi->tocore.needrows, pi->tocore.needcols,
+                      pi->tocore.haverows, pi->tocore.havecols);
+                wait_synch();
+                return;
+            }
+        }
+        g.perm_invent_win = create_nhwindow(NHW_PERMINVENT);
+        if (g.perm_invent_win == WIN_ERR)
+            return;
+        display_nhwindow(g.perm_invent_win, FALSE);
+        g.core_invent_state++;
+    }
+    text = Empty; /* lint suppression */
+    pi_info.fromcore.core_request = update_slot;
+    pi_info.fromcore.force_redraw = g.program_state.in_docrt ? TRUE : FALSE,
+
+    obj = g.invent;
+    for (slot = 0; slot < pi->tocore.maxslot; ++slot) {
+        nxtlet = '?'; /* always gets set to something else if actually used */
+        if (!sparse) {
+            while (obj && ((obj->invlet == GOLD_SYM && !show_gold)
+                           || (!obj->owornmask && inuse_only)))
+                obj = obj->nobj;
+        } else {
+            if (!show_gold)
+                nxtlet = (slot < 26) ? ('a' + slot) : ('A' + slot - 26);
+            else
+                nxtlet = (slot == 0) ? GOLD_SYM
+                         : (slot < 27) ? ('a' + slot - 1)
+                           : (slot < 53) ? ('A' + slot - 27)
+                             : NOINVSYM;
+            for (obj = g.invent; obj; obj = obj->nobj)
+                if (obj->invlet == nxtlet)
+                    break;
+        }
+        if (obj) {
+            /* TODO: check for MENUCOLORS match */
+            text = doname(obj); /* 'text' will switch to fromcore.text below */
+            /* strip away "a"/"an"/"the" prefix to show a bit more of the
+               interesting part of the object's description;
+               this is inline version of pi_article_skip() from cursinvt.c;
+               should move that to hacklib.c and use it here */
+            if (text[0] == 'a') {
+                if (text[1] == ' ')
+                    text += 2;
+                else if (text[1] == 'n' && text[2] == ' ')
+                    text += 3;
+            } else if (text[0] == 't') {
+                if (text[1] == 'h' && text[2] == 'e' && text[3] == ' ')
+                    text += 4;
+            }
+            Snprintf(pi_info.fromcore.text,
+                     sizeof pi_info.fromcore.text,
+                     "%c - %s", obj->invlet, text);
+            text = pi_info.fromcore.text;
+            obj = obj->nobj; /* set up for next iteration */
+        } else if (sparse) {
+            Sprintf(pi_info.fromcore.text, "%c", nxtlet); /* empty slot */
+            text = pi_info.fromcore.text;
+        } else {
+            if (slot == 0) {
+                Sprintf(pi_info.fromcore.text, "%-4s[%s]", "",
+                        !g.invent ? "empty"
+                        : inuse_only ? "no items are in use"
+                          : "only gold");
+                text = pi_info.fromcore.text;
+            } else {
+                text = Empty; /* "" => fill slot with spaces */
+            }
+        }
+        if (!*text)
+            pi_info.fromcore.text[0] = Empty[0];
+        pi = update_invent_slot(g.perm_invent_win, slot + 1, &pi_info);
+    }
+    pi_info.fromcore.force_redraw = g.program_state.in_docrt ? TRUE : FALSE,
+    pi_info.fromcore.core_request = render;
+    pi = update_invent_slot(g.perm_invent_win, (slot = 0), &pi_info);
+}
+
+#if 0
+RESTORE_WARNING_FORMAT_NONLITERAL
+
+void
+tty_perm_invent_toggled(boolean negated)
+{
+    if (negated) {
+        if (g.perm_invent_win != WIN_ERR)
+            destroy_nhwindow(g.perm_invent_win), g.perm_invent_win = WIN_ERR;
+        done_tty_perm_invent_init = FALSE;
+    } else {
+        g.perm_invent_win = create_nhwindow(NHW_PERMINVENT);
+        if (g.perm_invent_win != WIN_ERR)
+            display_nhwindow(g.perm_invent_win, FALSE);
+    }
+}
+#endif
+#endif  /* CORE_INVENT */
+
 /*invent.c*/
index edcc6b8bc52551a05f97b6a7889d38c51a689155..71dd7258dbaaa6424366c43f1e984a10ddede5be 100644 (file)
@@ -4417,7 +4417,7 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op)
                    -> tty_create_nhwindow(WIN_TTYINVENT)
                       -> tty_create_invent()
                    gives feedback for failure (terminal too small) */
-                if (g.tty_invent_win == WIN_ERR)
+                if (g.perm_invent_win == WIN_ERR)
                     return optn_silenterr;
             }
 #endif
index 193aada84689e1256ce279f198b1529c66427dcb..89ad5baeb437021b29a20b252e91310412defa5b 100644 (file)
@@ -255,15 +255,25 @@ void g_pututf8(uint8 *utf8str);
 
 #ifdef TTY_PERM_INVENT
 void tty_perm_invent_toggled(boolean negated);
-static int ttyinv_create_window(int, struct WinDesc *);
-static void tty_invent_box_glyph_init(struct WinDesc *cw);
-static boolean calling_from_update_inventory = FALSE;
 static struct tty_perminvent_cell zerottycell = { 0, 0, 0, 0, { 0 } };
 static glyph_info zerogi = { 0 };
+#ifdef CORE_INVENT
+static struct to_core zero_tocore = { 0 };
+#endif
 enum { border_left, border_middle, border_right, border_elements };
 static int bordercol[border_elements] = { 0, 0, 0 }; /* left, middle, right */
-enum { InvNormal = 0, InvShowGold = 1, InvSparse = 2, InvInUse = 4 };
-static int ttyinvmode = InvNormal;
+static int ttyinvmode = InvNormal; /* enum is in wintype.h */
+static boolean done_tty_perm_invent_init = FALSE;
+#ifndef NOINVSYM /* invent.c */
+#define NOINVSYM '#'
+#endif
+static int ttyinv_create_window(int, struct WinDesc *);
+
+static void tty_invent_box_glyph_init(struct WinDesc *cw);
+static boolean calling_from_update_inventory = FALSE;
+static boolean assesstty(enum inv_modes, short *, short *,
+        long *, long *, long *, long *, long *);
+static void ttyinv_populate_slot(struct WinDesc *, int, int, const char *);
 #endif
 
 /*
@@ -551,8 +561,8 @@ tty_preference_update(const char *pref)
        only might individual symbols change (punctuation vs line drawing),
        the way to render them might change too (Handling: DEC/UTF8/&c) */
     if (!strcmp(pref, "symset") && iflags.window_inited) {
-       if (g.tty_invent_win != WIN_ERR)
-           tty_invent_box_glyph_init(wins[g.tty_invent_win]);
+       if (g.perm_invent_win != WIN_ERR)
+           tty_invent_box_glyph_init(wins[g.perm_invent_win]);
     }
 #endif
     return;
@@ -1597,12 +1607,7 @@ tty_create_nhwindow(int type)
         newwin->maxrow = newwin->maxcol = 0;
         break;
 #ifdef TTY_PERM_INVENT
-    case NHW_TTYINVENT:
-        {
-            /*TEMPORARY*/
-            char *envtmp = nh_getenv("TTYINV");
-            ttyinvmode = envtmp ? atoi(envtmp) : InvNormal;
-        }
+    case NHW_PERMINVENT:
         return ttyinv_create_window(newid, newwin);
 #endif
     default:
@@ -1640,7 +1645,8 @@ tty_create_nhwindow(int type)
 static int
 ttyinv_create_window(int newid, struct WinDesc *newwin)
 {
-    int i, r, c, minrow;
+    int i, r, c;
+    long minrow;    /* long to match maxrow declaration */
     unsigned n;
 
     /* Is there enough real estate to do this beyond the status line?
@@ -1668,37 +1674,20 @@ ttyinv_create_window(int newid, struct WinDesc *newwin)
      * them would reduce the number of rows needed by 2.]
      *
      */
-    newwin->offx = 0;
-    /* topline + map rows + status lines */
-    newwin->offy = 1 + ROWNO + 3; /* 3: + 2 + (iflags.wc2_statuslines > 2) */
-    newwin->rows = (ttyDisplay->rows - newwin->offy);
-    newwin->cols = ttyDisplay->cols;
-    newwin->maxrow = 0;
-    newwin->maxcol = 79;  /* bhaak */
+
     /* preliminary init in case tty_desctroy_nhwindow() gets called */
     newwin->data = (char **) 0;
     newwin->datlen = (short *) 0;
     newwin->cells = (struct tty_perminvent_cell **) 0;
 
-    minrow = tty_pi_minrow;
-    if ((ttyinvmode & InvShowGold) != 0)
-        minrow += 1;
-    /* "normal" max for items in use would be 3 weapon + 7 armor + 4
-       accessories == 14, but being punished and picking up the ball will
-       add 1, and some quest artifacts have an an #invoke property that's
-       tracked via obj->owornmask so could add more; if hero ends up with
-       more than 15 in-use items, some will be left out;
-       Qt's "paper doll" adds first lit lamp/candle and first active
-       leash; those aren't tracked via owornmask so we don't notice them */
-    if ((ttyinvmode & InvInUse) != 0)
-        minrow = 1 + 15 + 1; /* top border + 15 lines + bottom border */
-
-    if (newwin->rows < minrow || newwin->cols < tty_pi_mincol) {
-        tty_destroy_nhwindow(newid); /* sets g.tty_invent_win to WIN_ERR */
+    if (!assesstty(ttyinvmode,
+                  &newwin->offx, &newwin->offy, &newwin->rows, &newwin->cols,
+                  &newwin->maxcol, &minrow, &newwin->maxrow)) {
+        tty_destroy_nhwindow(newid); /* sets g.perm_invent_win to WIN_ERR */
         pline("%s.", "tty perm_invent could not be enabled");
         pline(
    "tty perm_invent needs a terminal that is at least %dx%d, yours is %dx%d.",
-              minrow + 1 + ROWNO + 3, tty_pi_mincol,
+              (int) (minrow + 1 + ROWNO + 3), tty_pi_mincol,
               ttyDisplay->rows, ttyDisplay->cols);
         tty_wait_synch();
         set_option_mod_status("perm_invent", set_gameview);
@@ -1739,10 +1728,11 @@ ttyinv_create_window(int newid, struct WinDesc *newwin)
                 newwin->cells[r][c].glyph = 1;
             }
         }
-
+    if (!done_tty_perm_invent_init)
+        tty_invent_box_glyph_init(newwin);
     return newid;
 }
-#endif
+#endif  /* TTY_PERM_INVENT */
 
 static void
 erase_menu_or_text(winid window, struct WinDesc *cw, boolean clear)
@@ -2739,7 +2729,7 @@ tty_destroy_nhwindow(winid window)
     if (cw->type == NHW_MAP)
         clear_screen();
 #ifdef TTY_PERM_INVENT
-    if (cw->type == NHW_TTYINVENT) {
+    if (cw->type == NHW_PERMINVENT) {
         int r, c;
 
         if (cw->cells) {
@@ -2762,7 +2752,7 @@ tty_destroy_nhwindow(winid window)
             cw->rows = cw->cols = 0;
         }
         cw->maxrow = cw->maxcol = 0;
-        g.tty_invent_win = WIN_ERR;
+        g.perm_invent_win = WIN_ERR;
     }
 #endif
     free_window_info(cw, TRUE);
@@ -2887,7 +2877,7 @@ tty_putsym(winid window, int x, int y, char ch)
     case NHW_STATUS:
 #endif
 #ifdef TTY_PERM_INVENT
-    case NHW_TTYINVENT:
+    case NHW_PERMINVENT:
 #endif
     case NHW_MAP:
     case NHW_BASE:
@@ -2959,7 +2949,7 @@ tty_putstr(winid window, int attr, const char *str)
         return;
     if (cw->type != NHW_MESSAGE
 #ifdef TTY_PERM_INVENT
-        && window != g.tty_invent_win
+        && window != g.perm_invent_win
 #endif
        )
         str = compress_str(str);
@@ -3457,8 +3447,6 @@ tty_select_menu(winid window, int how, menu_item **menu_list)
     return n;
 }
 
-RESTORE_WARNING_FORMAT_NONLITERAL
-
 /* special hack for treating top line --More-- as a one item menu */
 char
 tty_message_menu(char let, int how, const char *mesg)
@@ -3491,32 +3479,34 @@ tty_message_menu(char let, int how, const char *mesg)
     return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
 }
 
-#ifdef TTY_PERM_INVENT
-static boolean done_tty_perm_invent_init = FALSE;
-static void ttyinv_populate_slot(struct WinDesc *, int, int, const char *);
-#ifndef NOINVSYM /* invent.c */
-#define NOINVSYM '#'
-#endif
-DISABLE_WARNING_FORMAT_NONLITERAL
-#endif
-
 /* update persistent inventory window */
 void
 tty_update_inventory(int arg UNUSED)
 {
 #ifdef TTY_PERM_INVENT
+#ifndef CORE_INVENT
     static char Empty[1] = { '\0' };
     struct WinDesc *cw;
     struct tty_perminvent_cell *cell;
     struct obj *obj;
     char invbuf[BUFSZ], *text, nxtlet;
     int row, col, side, slot, maxslot;
-    winid window = g.tty_invent_win;
+    winid window = g.perm_invent_win;
     boolean force_redraw = g.program_state.in_docrt ? TRUE : FALSE,
             show_gold = (ttyinvmode & InvShowGold) != 0,
             inuse_only = (ttyinvmode & InvInUse) != 0,
             sparse = (ttyinvmode & InvSparse) != 0;
 
+    if (g.perm_invent_win == WIN_ERR
+        && !done_tty_perm_invent_init && iflags.perm_invent) {
+        g.perm_invent_win = create_nhwindow(NHW_PERMINVENT);
+        if (g.perm_invent_win == WIN_ERR) {
+            tty_perm_invent_toggled(TRUE); /* TRUE means negated */
+            return;
+        }
+        display_nhwindow(g.perm_invent_win, FALSE);
+        window = g.perm_invent_win;
+    }
     /* we just return if the window creation failed, probably due to
        not meeting size requirements */
     if (window == WIN_ERR)
@@ -3624,29 +3614,155 @@ tty_update_inventory(int arg UNUSED)
             }
         }
     calling_from_update_inventory = FALSE;
-#endif
+#endif  /* CORE_INVENT */
+#endif  /* TTY_PERM_INVENT */
     return;
 }
 
 perminvent_info *
 tty_update_invent_slot(
-    winid window UNUSED,  /* window to use, must be of type NHW_MENU */
-    int inventory_slot UNUSED,          /* slot id: 0 - info return to core */
-                                        /*          1 - gold slot */
-                                        /*          2 - 29 obj slots */
-    perminvent_info *pi UNUSED)
+    winid window,  /* window to use, must be of type NHW_MENU */
+    int slot,
+    perminvent_info *pi)
 {
+#if !defined(TTY_PERM_INVENT) || !defined(CORE_INVENT)
     return (perminvent_info *) 0;
+    nhUse(window);
+    nhUse(slot);
+    nhUse(pi);
+#else
+    boolean force_redraw, tty_ok, show_gold, inuse_only;
+    int row, col, side, maxslot;
+    /* winid window = g.perm_invent_win; */
+    struct WinDesc *cw;
+    struct tty_perminvent_cell *cell;
+    /* these types are set match the wintty.h field declarations */
+    long minrow;    /* long to match maxrow declaration in wintty.h */
+    short offx, offy;
+    long rows, cols, maxrow, maxcol;
+
+    if (!pi)
+        return (perminvent_info *) 0;
+    if (!done_tty_perm_invent_init
+        && pi->fromcore.core_request != request_settings) {
+        pi->tocore.tocore_flags |= no_init_done;
+        return pi;
+    }
+
+    switch(pi->fromcore.core_request) {
+    case request_settings: {
+        pi->tocore = zero_tocore;
+        ttyinvmode = pi->fromcore.invmode;
+        inuse_only = (ttyinvmode & InvInUse) != 0;
+        tty_ok = assesstty(pi->fromcore.invmode,
+                           &offx, &offy, &rows, &cols,
+                           &maxcol, &minrow, &maxrow);
+        pi->tocore.needrows = (int) (minrow + 1 + ROWNO + 3);
+        pi->tocore.needcols = (int) tty_pi_mincol;
+        pi->tocore.haverows = (int) ttyDisplay->rows;
+        pi->tocore.havecols = (int) ttyDisplay->cols;
+        if (!tty_ok) {
+            pi->tocore.tocore_flags |= prohibited;  /* prohibited */
+            return pi;
+        }
+        maxslot = (maxrow - 2) * (!inuse_only ? 2 : 1);
+        pi->tocore.maxslot = maxslot;
+        return pi;
+        break;
+    }
+    case update_slot:
+        if ((cw = wins[window]) == (struct WinDesc *) 0)
+            panic(winpanicstr, window);
+        slot -= 1;  /* 0 is used for commands */
+        show_gold = (ttyinvmode & InvShowGold) != 0;
+        row = (slot % (!show_gold ? 26 : 27)) + 1; /* +1: top border */
+        /* side: left side panel or right side panel, not a window column */
+        side = slot < (!show_gold ? 26 : 27) ? 0 : 1;
+        ttyinv_populate_slot(cw, row, side, pi->fromcore.text);
+        break;
+    case render:
+        if ((cw = wins[window]) == (struct WinDesc *) 0)
+            panic(winpanicstr, window);
+        /* render to the display */
+        force_redraw = pi->fromcore.force_redraw;
+//        show_gold = (ttyinvmode & InvShowGold) != 0;
+//        inuse_only = (ttyinvmode & InvInUse) != 0;
+//        sparse = (ttyinvmode & InvSparse) != 0;
+//        if (!done_tty_perm_invent_init)
+        calling_from_update_inventory = TRUE;
+        for (row = 0; row < cw->maxrow; ++row)
+            for (col = 0; col < cw->maxcol; ++col) {
+                cell = &cw->cells[row][col];
+                if (cell->refresh || force_redraw) {
+                    if (cell->glyph) {
+                        tty_print_glyph(window, col + 1, row,
+                                    cell->content.gi, &nul_glyphinfo);
+                        end_glyphout();
+                    } else {
+                        if (col != cw->curx || row != cw->cury)
+                        tty_curs(window, col + 1, row);
+                        (void) putchar(cell->content.ttychar);
+                        ttyDisplay->curx++;
+                        cw->curx++;
+                    }
+                    cell->refresh = 0;
+                }
+            }
+        calling_from_update_inventory = FALSE;
+        break;
+    default:
+        impossible("invalid request to tty_update_invent_slot %u",
+                   pi->fromcore.core_request);
+    }
+    return pi;
+#endif
 }
 
+RESTORE_WARNING_FORMAT_NONLITERAL
+
 #ifdef TTY_PERM_INVENT
+/*
+ * returns TRUE if things are ok
+ */
+static boolean
+assesstty(
+    enum inv_modes invmode,
+    short *offx, short *offy, long *rows, long *cols,
+    long *maxcol, long *minrow, long *maxrow)
+{
+    boolean show_gold, inuse_only;
+
+    show_gold = (invmode & InvShowGold) != 0;
+    inuse_only = (invmode & InvInUse) != 0;
+
+    *offx = 0;
+    /* topline + map rows + status lines */
+    *offy = 1 + ROWNO + 3; /* 3: + 2 + (iflags.wc2_statuslines > 2) */
+    *rows = (ttyDisplay->rows - (*offy));
+    *cols = ttyDisplay->cols;
+    *minrow = tty_pi_minrow;
+    if (show_gold)
+        *minrow += 1;
+    /* "normal" max for items in use would be 3 weapon + 7 armor + 4
+       accessories == 14, but being punished and picking up the ball will
+       add 1, and some quest artifacts have an an #invoke property that's
+       tracked via obj->owornmask so could add more; if hero ends up with
+       more than 15 in-use items, some will be left out;
+       Qt's "paper doll" adds first lit lamp/candle and first active
+       leash; those aren't tracked via owornmask so we don't notice them */
+    if (inuse_only)
+        *minrow = 1 + 15 + 1; /* top border + 15 lines + bottom border */
+    *maxrow = *minrow;
+    *maxcol = *cols;
+    return !(*rows < *minrow || *cols < tty_pi_mincol);
+}
 
 /* put the formatted object description for one item into a particular row
    and left/right panel, truncating if long or padding with spaces if short */
 static void
 ttyinv_populate_slot(
     struct WinDesc *cw,
-    int row, /* 'row' within the window, not within screen */
+    int row,  /* 'row' within the window, not within screen */
     int side, /* 'side'==0 is left panel or ==1 is right panel */
     const char *text)
 {
@@ -3680,12 +3796,14 @@ ttyinv_populate_slot(
     }
 }
 
+DISABLE_WARNING_FORMAT_NONLITERAL
+
 void
 tty_refresh_inventory(int start, int stop, int y)
 {
     int row = y, col, col_limit = stop;
     struct WinDesc *cw = 0;
-    winid window = g.tty_invent_win;
+    winid window = g.perm_invent_win;
     struct tty_perminvent_cell *cell;
 
     if (window == WIN_ERR || !iflags.perm_invent || y < 0)
@@ -3718,6 +3836,8 @@ tty_refresh_inventory(int start, int stop, int y)
     }
 }
 
+RESTORE_WARNING_FORMAT_NONLITERAL
+
 static void
 tty_invent_box_glyph_init(struct WinDesc *cw)
 {
@@ -3789,22 +3909,20 @@ tty_invent_box_glyph_init(struct WinDesc *cw)
     done_tty_perm_invent_init = TRUE;
 }
 
-RESTORE_WARNING_FORMAT_NONLITERAL
-
 void
 tty_perm_invent_toggled(boolean negated)
 {
     if (negated) {
-        if (g.tty_invent_win != WIN_ERR)
-            destroy_nhwindow(g.tty_invent_win), g.tty_invent_win = WIN_ERR;
+        if (g.perm_invent_win != WIN_ERR)
+            destroy_nhwindow(g.perm_invent_win), g.perm_invent_win = WIN_ERR;
         done_tty_perm_invent_init = FALSE;
     } else {
-        g.tty_invent_win = create_nhwindow(NHW_TTYINVENT);
-        if (g.tty_invent_win != WIN_ERR)
-            display_nhwindow(g.tty_invent_win, FALSE);
+        g.perm_invent_win = create_nhwindow(NHW_PERMINVENT);
+        if (g.perm_invent_win != WIN_ERR)
+            display_nhwindow(g.perm_invent_win, FALSE);
     }
 }
-#endif
+#endif  /* TTY_PERM_INVENT */
 
 void
 tty_mark_synch(void)
@@ -3848,8 +3966,8 @@ docorner(register int xmin, register int ymax, int ystart_between_menu_pages)
 #ifdef TTY_PERM_INVENT
     struct WinDesc *icw = 0;
 
-    if (g.tty_invent_win != WIN_ERR)
-        icw = wins[g.tty_invent_win];
+    if (g.perm_invent_win != WIN_ERR)
+        icw = wins[g.perm_invent_win];
 #endif
 
     HUPSKIP();
@@ -3902,7 +4020,8 @@ docorner(register int xmin, register int ymax, int ystart_between_menu_pages)
     }
 
     end_glyphout();
-    if (ymax >= (int) wins[WIN_STATUS]->offy) {
+    if (ymax >= (int) wins[WIN_STATUS]->offy
+        && !ystart_between_menu_pages) {
         /* we have wrecked the bottom line */
         g.context.botlx = 1;
         bot();