From 1b1c45d3cee0677bf00a2771baaa8756b4fb9dd9 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 6 Dec 2023 02:19:41 -0800 Subject: [PATCH] TTY_PERM_INVENT perminv_mode=inuse Revise the tty permanent inventory window's in-use mode to be able to switch back and forth between one full width panel and two side-by-side half width panels on the fly as needs dictate. A lot of trial and error involved but I think it has reached a state where it is reliable, and the smaller number of lines required at present can probably be viable for actual usage. At the moment it requires at least 10 extra lines below the standard tty mesg+map+stat display, 34 lines total for statuslines:2 or 35 for statuslines:3. The top and bottom of the 10 or more extra lines get drawn as boundary lines using wall characters, leaving 8 lines for full width or 16 half lines when 8 isn't enough. If more that 16 items are in use (maybe lots of lit candles forced into separate inventory slots or a menagerie on leashes), the excess get skipped. However, it will use more lines if they are available. Prior to a few days ago it was requiring 17 extra lines and able to show 15 items as full lines only, unable to take advantage of more lines when available. --- win/tty/wintty.c | 199 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 150 insertions(+), 49 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index e054d90f5..2037c780b 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -263,6 +263,7 @@ static int inuse_only_start = 0; /* next slot to use for in-use-only mode */ static boolean done_tty_perm_invent_init = FALSE; enum { tty_slots = invlet_basic + invlet_gold + invlet_overflow }; /* 54 */ static boolean slot_tracker[tty_slots]; +static int ttyinv_slots_used = 0; /* 1-based, slot_trackter[0..slots-1] */ static long last_glyph_reset_when; #ifndef NOINVSYM /* invent.c */ #define NOINVSYM '#' @@ -274,13 +275,16 @@ static void ttyinv_add_menu(winid, struct WinDesc *, char ch, int attr, int clr, const char *str); static int selector_to_slot(char ch, const int invflags, boolean *ignore); static char slot_to_invlet(int, boolean); +static void ttyinv_inuse_fulllines(struct WinDesc *, int); +static void ttyinv_inuse_twosides(struct WinDesc *, int); +static void ttyinv_end_menu(int, struct WinDesc *); static void ttyinv_render(winid window, struct WinDesc *cw); static void tty_invent_box_glyph_init(struct WinDesc *cw); static boolean assesstty(enum inv_modes, short *, short *, long *, long *, long *, long *, long *); static void ttyinv_populate_slot(struct WinDesc *, int, int, const char *, int32_t); -#endif +#endif /* TTY_PERM_INVENT */ /* * A string containing all the default commands -- to add to a list @@ -2615,12 +2619,9 @@ tty_end_menu( ttywindowpanic(); } #ifdef TTY_PERM_INVENT - if (cw->mbehavior == MENU_BEHAVE_PERMINV - && (iflags.perm_invent - || gp.perm_invent_toggling_direction == toggling_on) - && window == WIN_INVEN) { - if (gp.program_state.in_moveloop) - ttyinv_render(window, cw); + /* (probably don't need to check both of these conditions) */ + if (cw->mbehavior == MENU_BEHAVE_PERMINV && window == WIN_INVEN) { + ttyinv_end_menu(window, cw); return; } #endif @@ -2917,18 +2918,19 @@ ttyinv_create_window(int newid, struct WinDesc *newwin) bordercol[border_left] = 0; bordercol[border_middle] = (newwin->maxcol + 1) / 2; bordercol[border_right] = newwin->maxcol - 1; - /* for in-use mode, use full lines */ + /* for in-use mode, use full lines; it will switch to two panels if + there are more items than the number of full lines */ if ((ttyinvmode & InvInUse) != 0) bordercol[border_middle] = bordercol[border_right]; - n = (unsigned) (newwin->maxrow * sizeof(struct tty_perminvent_cell *)); + n = (unsigned) (newwin->maxrow * sizeof (struct tty_perminvent_cell *)); newwin->cells = (struct tty_perminvent_cell **) alloc(n); - n = (unsigned) (newwin->maxcol * sizeof(struct tty_perminvent_cell)); + n = (unsigned) (newwin->maxcol * sizeof (struct tty_perminvent_cell)); for (i = 0; i < newwin->maxrow; i++) newwin->cells[i] = (struct tty_perminvent_cell *) alloc(n); - n = (unsigned) sizeof(glyph_info); + n = (unsigned) sizeof (glyph_info); for (r = 0; r < newwin->maxrow; r++) for (c = 0; c < newwin->maxcol; c++) { newwin->cells[r][c] = zerottycell; @@ -2942,6 +2944,7 @@ ttyinv_create_window(int newid, struct WinDesc *newwin) } } newwin->active = 1; + ttyinv_slots_used = 0; tty_invent_box_glyph_init(newwin); return newid; } @@ -3015,9 +3018,20 @@ ttyinv_add_menu( if (!gp.program_state.in_moveloop) return; slot = selector_to_slot(ch, ttyinvmode, &ignore); + if (inuse_only && slot > 2 * rows_per_side) + ignore = TRUE; /* left & right sides full; no 3rd 'side' available */ if (!ignore) { slot_tracker[slot] = TRUE; - /* maxslot = ((int) cw->maxrow - 2) * (!inuse_only ? 2 : 1); */ + /* if we need to expand inuse_only from one side to two, do so now; + entries are shown in row 1 thru rows_per_side (with rows 0 and + rows_per_side+1 containing boundary lines) but stored in + slots 0 thru rows_per_side-1, so zero-based slot==rows_per_side + is the entry that will be shown on first row of the second side + (and one-base ttyinv_slots_used==rows_per_side means that on the + previous inventory_update(), full lines filled all the rows) */ + if (inuse_only && slot == rows_per_side + && ttyinv_slots_used == rows_per_side) + ttyinv_inuse_twosides(cw, rows_per_side); /* TODO: check for MENUCOLORS match */ text = str; /* 'text' will switch to invbuf[] below */ @@ -3138,6 +3152,64 @@ slot_to_invlet(int slot, boolean incl_gold) return res; } +/* called if inuse_only contracts from two sides to one; there won't be + full lines shown until the next inventory update because the data past + the middle boundary was clipped rather than saved; caller or caller's + caller or somewhere up the call chain will call update_inventory() to + redraw it all and rectify that */ +static void +ttyinv_inuse_fulllines( + struct WinDesc *cw, + int rows_per_side UNUSED) +{ + bordercol[border_middle] = bordercol[border_right]; + tty_invent_box_glyph_init(cw); +} + +/* called when inuse_only expands from full lines (one side) to two sides */ +static void +ttyinv_inuse_twosides( + struct WinDesc *cw, + int rows_per_side) +{ + int row, col; + + bordercol[border_middle] = (cw->maxcol + 1) / 2; + tty_invent_box_glyph_init(cw); + /* draw the middle boundary */ + col = bordercol[border_middle]; + for (row = 0; row <= rows_per_side; ++row) + tty_refresh_inventory(col, col, row); +} + +/* split out of tty_end_menu(); persistent inventory is ready to display */ +static void +ttyinv_end_menu(int window, struct WinDesc *cw) +{ + if (iflags.perm_invent + || gp.perm_invent_toggling_direction == toggling_on) { + if (gp.program_state.in_moveloop) { + boolean inuse_only = ((ttyinvmode & InvInUse) != 0); + int rows_per_side = inuse_only ? cw->maxrow - 2 : 0; + int old_slots_used = ttyinv_slots_used; /* value before render */ + + ttyinv_render(window, cw); + + /* if inuse_only was using two sides and has just shrunk to one, + it will switch to full rows instead of side-by-side panels + but current data only holds the left-hand panel's portion; + rerun the whole thing to regenerate previously clipped data; + fortunately this should be a fairly rare occurrence */ + if (inuse_only && old_slots_used > rows_per_side + /* 'ttyinv_slots_used' was just updated by ttyinv_render() + to the number of entries currently shown */ + && ttyinv_slots_used <= rows_per_side) + tty_update_inventory(0); /* will call back to core for data */ + } + } +} + +/* display persistent inventory */ static void ttyinv_render(winid window, struct WinDesc *cw) { @@ -3145,15 +3217,18 @@ ttyinv_render(winid window, struct WinDesc *cw) struct tty_perminvent_cell *cell; char invbuf[BUFSZ]; boolean force_redraw = gp.program_state.in_docrt ? TRUE : FALSE, - show_gold = (ttyinvmode & InvShowGold) != 0, inuse_only = (ttyinvmode & InvInUse) != 0, + show_gold = (ttyinvmode & InvShowGold) != 0 && !inuse_only, sparse = (ttyinvmode & InvSparse) != 0 && !inuse_only; - int rows_per_side = (!show_gold ? 26 : 27); + int rows_per_side = (inuse_only ? (cw->maxrow - 2) + : !show_gold ? 26 + : 27); slot_limit = SIZE(slot_tracker); if (inuse_only) { - rows_per_side = cw->maxrow - 2; /* -2 top and bottom borders */ - slot_limit = rows_per_side; + slot_limit = rows_per_side; /* assume one side */ + if (ttyinv_slots_used >= rows_per_side) + slot_limit *= 2; /* second side is populated */ } else if (!show_gold) { slot_limit -= 2; /* there are two extra slots for gold and overflow; * blanking them for !show_gold would wrap back to @@ -3163,6 +3238,7 @@ ttyinv_render(winid window, struct WinDesc *cw) for (slot = 0; slot < slot_limit; ++slot) if (slot_tracker[slot]) filled_count++; + /* clear unused slots */ for (slot = 0; slot < slot_limit; ++slot) { if (slot_tracker[slot]) continue; @@ -3181,6 +3257,23 @@ ttyinv_render(winid window, struct WinDesc *cw) side = slot / rows_per_side; ttyinv_populate_slot(cw, row, side, invbuf, 0); } + + /* inuse_only might switch from one panel to two or vice versa */ + if (inuse_only && filled_count != ttyinv_slots_used) { + if (filled_count > rows_per_side + && ttyinv_slots_used <= rows_per_side) { + /* need second side; set up the middle border */ + /* done earlier, in add_menu(): + ttyinv_inuse_twosides(cw, rows_per_side); + */ + } else if (filled_count <= rows_per_side + && ttyinv_slots_used > rows_per_side) { + /* have second side but don't need/want it anymore */ + ttyinv_inuse_fulllines(cw, rows_per_side); + } + ttyinv_slots_used = filled_count; + } + /* has there been a glyph reset since we last got here? */ if (gg.glyph_reset_timestamp > last_glyph_reset_when) { /* // tty_invent_box_glyph_init(wins[WIN_INVEN]); */ @@ -3265,12 +3358,13 @@ ttyinv_populate_slot( struct tty_perminvent_cell *cell; char c; int ccnt, col, endcol; - boolean inuse_only = (ttyinvmode & InvInUse) != 0; + boolean oops, inuse_only = (ttyinvmode & InvInUse) != 0; - if (inuse_only && side == 1) /* there might be more in use than fits */ - return; - if (row < 0 || (long) row >= cw->maxrow || side < 0 || side > 1) - panic("ttyinv_populate_slot row=%d, size=%d", row, side); + oops = (row < 0 || (long) row >= cw->maxrow || side < 0); + if (inuse_only && side > 1 && !oops) + return; /* there might be more in-use than fits; ignore excess */ + if (oops || side > 1) + panic("ttyinv_populate_slot row=%d, side=%d", row, side); col = bordercol[side] + 1; endcol = bordercol[side + 1] - 1; @@ -3283,8 +3377,8 @@ ttyinv_populate_slot( memory allocated for it; gi pointer and ttychar character overlay each other in a union, so clear gi before assigning ttychar */ if (cell->glyph) { - free((genericptr_t) cell->content.gi), cell->content.gi = 0; - cell->glyph = 0; /* cell->content.gi is gone */ + free((genericptr_t) cell->content.gi); + *cell = zerottycell; /* clears cell->glyph and cell->content */ } if ((c = *text) != '\0') @@ -3343,8 +3437,8 @@ RESTORE_WARNING_FORMAT_NONLITERAL static void tty_invent_box_glyph_init(struct WinDesc *cw) - { - int row, col; +{ + int row, col, glyph; uchar sym; struct tty_perminvent_cell *cell; @@ -3354,13 +3448,7 @@ tty_invent_box_glyph_init(struct WinDesc *cw) for (row = 0; row < cw->maxrow; ++row) for (col = 0; col < cw->maxcol; ++col) { cell = &cw->cells[row][col]; - /* cell->glyph is a flag for whether the content union contains - a glyph_info structure rather than just a char */ - if (!cell->glyph) - continue; - /* sym will always get another value; if for some reason it - doesn't, this default is valid for cmap_walls_to_glyph() */ - sym = S_crwall; + sym = S_crwall; /* placeholder */ /* note: for top and bottom, check [border_right] before [border_middle] because they could be the same column (for InvInUse) and if so we want corner rather than tee there */ @@ -3395,25 +3483,38 @@ tty_invent_box_glyph_init(struct WinDesc *cw) sym = S_vwall; } - /* to get here, cell->glyph is 1 and cell->content union has gi */ - { - int oldsymidx = cell->content.gi->gm.sym.symidx; -#ifdef ENHANCED_SYMBOLS - struct unicode_representation * - oldgmu = cell->content.gi->gm.u; -#endif - int glyph = cmap_D0walls_to_glyph(sym); - - map_glyphinfo(0, 0, glyph, 0, cell->content.gi); - if ( -#ifdef ENHANCED_SYMBOLS - cell->content.gi->gm.u != oldgmu || -#endif - cell->content.gi->gm.sym.symidx != oldsymidx) + if (sym == S_crwall) { /* not a boundary (but might have been) */ + if (cell->glyph) { + /* presumably is no longer wanted middle vertical line */ + free((genericptr_t) cell->content.gi); + *cell = zerottycell; /* clears cell->glyph */ + cell->content.ttychar = ' '; + cell->text = 1; cell->refresh = 1; - cell->glyph = 1; /* (redundant) */ - cell->text = 0; + } + continue; } + /* a boundary, but might not have been if this is the middle + and we were showing full lines and are now going to show two + sides; allocate glyph_info for middle vertical line */ + if (!cell->glyph) { + cell->content.gi = (glyph_info *) alloc(sizeof (glyph_info)); + *(cell->content.gi) = zerogi; + cell->glyph = 1, cell->text = 0; + } + + /* to get here, cell->glyph is 1 and cell->content union has gi */ + glyph = cmap_D0walls_to_glyph(sym); + map_glyphinfo(0, 0, glyph, 0, cell->content.gi); + cell->glyph = 1; /* (redundant) */ + cell->text = 0; + /* originaly this conditionally set refresh depending upon + whether the glyph was already shown, but that optimization + is for something that rarely happens (boundary lines aren't + redrawn very often, and most of the time when they are it's + because they were erased or overwritten by something so + won't match the prior value anymore) so skip it */ + cell->refresh = 1; } done_tty_perm_invent_init = TRUE; }