From 3b2f7e86e0330f6515781f52d81713471cd02542 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 13 May 2018 15:19:39 -0400 Subject: [PATCH 1/8] more status updates - prevent an overflow - add make_things_fit() --- win/tty/wintty.c | 490 +++++++++++++++++++++++++++++------------------ 1 file changed, 303 insertions(+), 187 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index dbfc6d863..25df05d5e 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -177,12 +177,15 @@ 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)); +STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *, int *)); STATIC_DCL void NDECL(render_status); STATIC_DCL void FDECL(tty_putstatusfield, (struct tty_status_fields *, const char *, int, int)); -STATIC_DCL int FDECL(set_cond_shrinklvl, (int, int)); STATIC_DCL boolean NDECL(check_windowdata); +STATIC_DCL int NDECL(condition_size); +STATIC_DCL int FDECL(make_things_fit, (BOOLEAN_P)); +STATIC_DCL void FDECL(shrink_enc, (int)); +STATIC_DCL void FDECL(shrink_dlvl, (int)); #endif /* @@ -3444,8 +3447,9 @@ const char *fieldnames[] = { #ifdef STATUS_HILITES static int FDECL(condcolor, (long, unsigned long *)); static int FDECL(condattr, (long, unsigned long *)); -static long tty_condition_bits; static unsigned long *tty_colormasks; +static long tty_condition_bits; +int cond_disp_width[2]; /* 2: current and previous */ static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: first index is for current and previous */ @@ -3470,6 +3474,11 @@ static struct condition_t { { BL_MASK_FLY, {"Fly", "Fly", "Fl"}}, { BL_MASK_RIDE, {"Ride", "Rid", "Ri"}}, }; +static const char *encvals[3][6] = { + { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded"}, + { "", "Burden", "Stress", "Strain", "Overtax", "Overload" }, + { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" } +}; static enum statusfields fieldorder[2][15] = { /* 2: two status lines */ { 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, @@ -3478,7 +3487,13 @@ static enum statusfields fieldorder[2][15] = { /* 2: two status lines */ BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_CAP, BL_CONDITION, BL_FLUSH } }; + static boolean windowdata_init = FALSE; +static int cond_shrinklvl = 0, cond_width_at_shrink = 0; +static int enclev = 0, enc_shrinklvl = 0; +static dl_shrinklvl = 0; +static boolean truncation_expected = FALSE; + /* This controls whether to skip fields that aren't * flagged as requiring updating during the current * render_status(). @@ -3585,6 +3600,7 @@ int fldidx, chg UNUSED, percent, color; genericptr_t ptr; unsigned long *colormasks; { + int i; long *condptr = (long *) ptr; char *text = (char *) ptr; boolean do_color = FALSE; @@ -3607,7 +3623,17 @@ unsigned long *colormasks; tty_colormasks = colormasks; tty_status[NOW][fldidx].valid = TRUE; tty_status[NOW][fldidx].dirty = TRUE; + truncation_expected = FALSE; break; + case BL_CAP: + for (i = 0; i < SIZE(encvals); ++i) { + if (!strcmp(encvals[enc_shrinklvl][i], + status_vals[BL_CAP])) { + enclev = i; + break; + } + } + /*FALLTHRU*/ default: tty_status[NOW][fldidx].idx = fldidx; Sprintf(status_vals[fldidx], @@ -3637,9 +3663,9 @@ unsigned long *colormasks; tty_status[NOW][fldidx].lth = 0; } - /* The core botl engine sends BL_LEVELDESC with trailing blanks - included. Let's suppress one of the trailing blanks */ - if (fldidx == BL_LEVELDESC) { + /* The core botl engine sends trailing blanks for some fields + Let's suppress the trailing blanks */ + if (fldidx == BL_LEVELDESC || fldidx == BL_HUNGER) { char *lastchar = eos(status_vals[fldidx]); lastchar--; while (lastchar && *lastchar == ' ' @@ -3655,12 +3681,58 @@ unsigned long *colormasks; if (fldidx == BL_GOLD) tty_status[NOW][fldidx].lth -= 9; /* \GXXXXNNNN counts as 1 */ - - if (check_fields(force_update)) + if (make_things_fit(force_update) || truncation_expected) render_status(); return; } +int +make_things_fit(force_update) +boolean force_update; +{ + int trycnt, fitting = 0, condsz = 0, requirement = 0; + int rowsz[2], otheroptions = 0; + boolean check = FALSE; + + condsz = condition_size(); + for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) { + check = check_fields(force_update, &rowsz[0], &rowsz[1]); + if (!check) return 0; + + requirement = rowsz[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; + condsz = condition_size(); + cond_width_at_shrink = cond_disp_width[NOW]; + } + continue; + } + if (cond_shrinklvl >= 2) { + /* We've exhausted the condition identifiers shrinkage, + * so let's try something other things... + */ + if (otheroptions == 0 || otheroptions == 1) { + /* try shrinking the encumbrance word */ + shrink_enc(otheroptions + 1); + otheroptions++; + } else if (otheroptions == 2) { + shrink_dlvl(1); + otheroptions++; + } else { + /* Last resort - turn on trunction */ + truncation_expected = TRUE; + otheroptions++; + } + } + } + return fitting; +} + /* * This is the routine where we figure out where each field * should be placed, and flag whether the on-screen details @@ -3668,10 +3740,11 @@ unsigned long *colormasks; * This is now done at an individual field case-by-case level. */ boolean -check_fields(forcefields) +check_fields(forcefields, topsz, bottomsz) boolean forcefields; +int *topsz, *bottomsz; { - int c, i, row, col; + int c, i, row, col, trackx, idx; boolean valid = TRUE, matchprev = FALSE, update_right; if (!windowdata_init && !check_windowdata()) @@ -3679,13 +3752,12 @@ boolean forcefields; for (row = 0; row < 2; ++row) { col = 1; + trackx = 1; update_right = FALSE; for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { - int idx = fieldorder[row][i]; - + idx = fieldorder[row][i]; if (!status_activefields[idx]) continue; - if (!tty_status[NOW][idx].valid) valid = FALSE; @@ -3696,29 +3768,30 @@ boolean forcefields; if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth) update_right = TRUE; - /* - * Check values against those already on the dislay. - * - Is the additional processing time for this worth it? - */ - matchprev = FALSE; - if (do_field_opt && tty_status[NOW][idx].dirty) { - /* compare values */ - const char *ob, *nb; /* old byte, new byte */ + if (!update_right && !forcefields) { + /* + * Check values against those already on the dislay. + * - Is the additional processing time for this worth it? + */ + matchprev = FALSE; + if (do_field_opt && tty_status[NOW][idx].dirty) { + /* compare values */ + const char *ob, *nb; /* old byte, new byte */ - c = col - 1; - ob = &wins[WIN_STATUS]->data[row][c]; - nb = status_vals[idx]; - while (*nb && c < wins[WIN_STATUS]->cols) { - if (*nb != *ob) - break; - nb++; - ob++; - c++; + c = col - 1; + ob = &wins[WIN_STATUS]->data[row][c]; + nb = status_vals[idx]; + while (*nb && c < wins[WIN_STATUS]->cols) { + if (*nb != *ob) + break; + nb++; + ob++; + c++; + } + if (!*nb && c > col - 1) + matchprev = TRUE; } - if (!*nb && c > col - 1) - matchprev = TRUE; - } - + } /* * With STATUS_HILITES, it is possible that the color * needs to change even if the text is the same, so @@ -3726,18 +3799,201 @@ boolean forcefields; * Then, render_status() will see that flag setting * and ensure that the tty cell content is updated. * After the field has been updated, render_status() - * will also clear .redraw. + * will also clear .redraw and .dirty. */ if (forcefields || update_right || !matchprev || tty_status[NOW][idx].color != tty_status[BEFORE][idx].color || tty_status[NOW][idx].attr != tty_status[BEFORE][idx].attr) - tty_status[NOW][idx].redraw = TRUE; + tty_status[NOW][idx].redraw = TRUE; col += tty_status[NOW][idx].lth; } + if (row && bottomsz) + *bottomsz = col + tty_status[NOW][idx].lth; + else if (topsz) + *topsz = col + tty_status[NOW][idx].lth; } return valid; } +/* + * This is what places a field on the tty display. + * If val isn't null, it will be used rather than + * fld (it takes precedence). + */ +void +tty_putstatusfield(fld, val, x, y) +struct tty_status_fields *fld; +const char *val; +int x,y; +{ + int i, n, ncols, lth; + struct WinDesc *cw = 0; + const char *text = (char *)0; + + if ((cw = wins[NHW_STATUS]) == (struct WinDesc *) 0) + panic("Invalid WinDesc\n"); + + ncols = cw->cols; + if (val) { + text = val; + lth = strlen(text); + } else if (fld) { + text = status_vals[fld->idx]; + lth = fld->lth; + } + if (!text) return; + + print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS); + + if (x <= ncols) { + tty_curs(NHW_STATUS, x, y); + for (i = 0; i < lth; ++i) { + n = i + x; + if (n < ncols && *text) { + (void) putchar(*text); + ttyDisplay->curx++; + cw->curx++; + cw->data[y][n-1] = *text; + text++; + } + } + } else { + /* Now we're truncating */ + if (truncation_expected) + ; /* but we new in advance */ + } +} + +int +condition_size() +{ + long mask = 0L; + int c, x; + boolean fitting = FALSE; + + x = 0; + for (c = 0; c < SIZE(conditions); ++c) { + mask = conditions[c].mask; + if ((tty_condition_bits & mask) == mask) { + x++; /* for spacer */ + x += (int) strlen(conditions[c].text[cond_shrinklvl]); + } + } + tty_status[NOW][BL_CONDITION].lth = x; + cond_disp_width[NOW] = x; + return x; +} + +void +shrink_enc(lvl) +int lvl; +{ + /* shrink or restore the encumbrance word */ + if (lvl == 0 || lvl <= 2) { + enc_shrinklvl = lvl; + Strcpy(status_vals[BL_CAP], encvals[lvl][enclev]); + } + tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]); +} + +void +shrink_dlvl(lvl) +int lvl; +{ + /* try changing Dlvl: to Dl: */ + char buf[BUFSZ]; + char *levval =index(status_vals[BL_LEVELDESC], ':'); + + if (levval) { + if (lvl == 0) + Strcpy(buf, "Dlvl"); + else + Strcpy(buf, "Dl"); + + Strcat(buf, levval); + Strcpy(status_vals[BL_LEVELDESC], buf); + tty_status[NOW][BL_LEVELDESC].lth = + strlen(status_vals[BL_LEVELDESC]); + } +} + +/* + * Ensure the underlying status window data start out + * blank and null-terminated. + */ +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) { + int i, n = wins[WIN_STATUS]->cols; + + for (i = 0; i < wins[WIN_STATUS]->cols; ++i) { + wins[WIN_STATUS]->data[0][i] = ' '; + wins[WIN_STATUS]->data[1][i] = ' '; + } + wins[WIN_STATUS]->data[0][n - 1] = '\0'; /* null terminate */ + wins[WIN_STATUS]->data[1][n - 1] = '\0'; /* null terminate */ + windowdata_init = TRUE; + } + return TRUE; +} + +#ifdef TEXTCOLOR +/* + * Return what color this condition should + * be displayed in based on user settings. + */ +int condcolor(bm, bmarray) +long bm; +unsigned long *bmarray; +{ + int i; + + if (bm && bmarray) + for (i = 0; i < CLR_MAX; ++i) { + if (bmarray[i] && (bm & bmarray[i])) + return i; + } + return NO_COLOR; +} +#endif /* TEXTCOLOR */ + +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 (bmarray[i] && (bm & bmarray[i])) { + 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) \ if (m) { \ if ((m) & HL_BOLD) \ @@ -3770,7 +4026,7 @@ void render_status(VOID_ARGS) { long mask = 0L; - int i, c, row, shrinklvl = 0, attrmask = 0; + int i, c, row, attrmask = 0; struct WinDesc *cw = 0; boolean do_color = FALSE, fit = FALSE; struct tty_status_fields *nullfield = (struct tty_status_fields *)0; @@ -3814,7 +4070,6 @@ render_status(VOID_ARGS) * | Condition Codes | * +-----------------+ */ - shrinklvl = set_cond_shrinklvl(x, cw->cols); for (c = 0; c < SIZE(conditions); ++c) { mask = conditions[c].mask; if ((tty_condition_bits & mask) == mask) { @@ -3828,9 +4083,11 @@ render_status(VOID_ARGS) term_start_color(coloridx); } } + if (x >= cw->cols && !truncation_expected) + impossible("Unexpected condition placement overflow"); tty_putstatusfield(nullfield, - conditions[c].text[shrinklvl], x, y); - x += (int) strlen(conditions[c].text[shrinklvl]); + conditions[c].text[cond_shrinklvl], x, y); + x += (int) strlen(conditions[c].text[cond_shrinklvl]); if (iflags.hilite_delta) { if (do_color && coloridx != NO_COLOR) term_end_color(); @@ -3935,156 +4192,15 @@ render_status(VOID_ARGS) } } } + if (cond_disp_width[NOW] < cond_width_at_shrink) { + cond_shrinklvl = 0; /* reset */ + cond_width_at_shrink = condition_size(); + shrink_enc(0); + shrink_dlvl(0); + } return; } -/* - * This is what places a field on the tty display. - * If val isn't null, it will be used rather than - * fld (it takes precedence). - */ -void -tty_putstatusfield(fld, val, x, y) -struct tty_status_fields *fld; -const char *val; -int x,y; -{ - int i, n, ncols, lth; - struct WinDesc *cw = 0; - const char *text = (char *)0; - - if ((cw = wins[NHW_STATUS]) == (struct WinDesc *) 0) - panic("Invalid WinDesc\n"); - - ncols = cw->cols; - if (val) { - text = val; - lth = strlen(text); - } else if (fld) { - text = status_vals[fld->idx]; - lth = fld->lth; - } - if (!text) return; - - print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS); - - tty_curs(NHW_STATUS, x, y); - for (i = 0; i < lth; ++i) { - n = i + x; - if (n < ncols && *text) { - (void) putchar(*text); - ttyDisplay->curx++; - cw->curx++; - cw->data[y][n-1] = *text; - text++; - } - } -} - -int -set_cond_shrinklvl(col, ncols) -int col, ncols; -{ - long mask = 0L; - int j, c, x, avail, shrinklvl = 0; - boolean fitting = FALSE; - - avail = ncols - col; - /* determine appropriate shrinklvl required */ - for (j = 0; j < 3 && !fitting; ++j) { - x = 0; - for (c = 0; c < SIZE(conditions); ++c) { - mask = conditions[c].mask; - if ((tty_condition_bits & mask) == mask) { - x++; /* for spacer */ - x += (int) strlen(conditions[c].text[shrinklvl]); - } - } - if (x < avail) - fitting = TRUE; - else if (shrinklvl < 2) - shrinklvl++; - } - return shrinklvl; -} - -/* - * Ensure the underlying status window data start out - * blank and null-terminated. - */ -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) { - int i, n = wins[WIN_STATUS]->cols; - - for (i = 0; i < wins[WIN_STATUS]->cols; ++i) - wins[WIN_STATUS]->data[0][i] = ' '; - wins[WIN_STATUS]->data[0][n - 1] = '\0'; /* null terminate */ - for (i = 0; i < wins[WIN_STATUS]->cols; ++i) - wins[WIN_STATUS]->data[1][i] = ' '; - wins[WIN_STATUS]->data[0][n - 1] = '\0'; /* null terminate */ - windowdata_init = TRUE; - } - return TRUE; -} - -#ifdef TEXTCOLOR -/* - * Return what color this condition should - * be displayed in based on user settings. - */ -int condcolor(bm, bmarray) -long bm; -unsigned long *bmarray; -{ - int i; - - if (bm && bmarray) - for (i = 0; i < CLR_MAX; ++i) { - if (bmarray[i] && (bm & bmarray[i])) - return i; - } - return NO_COLOR; -} -#endif /* TEXTCOLOR */ - -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 (bmarray[i] && (bm & bmarray[i])) { - 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; -} - #endif /* STATUS_HILITES */ #endif /* TTY_GRAPHICS */ From 2b8ac8af9eaa8940b8dc0b7c73929336b5149bb4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 13 May 2018 16:42:11 -0400 Subject: [PATCH 2/8] missing stubs for NetHackW.exe --- sys/winnt/stubs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index 61e031c7f..469a0bdb2 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -170,4 +170,14 @@ more() return; } +void +nethack_enter_nttty() +{ + return; +} + +void +set_altkeyhandler(const char *inName) +{ +} #endif /* TTYSTUBS */ From 860b9c35c13b291a451c782395f272416fcc6204 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 13 May 2018 21:24:14 -0400 Subject: [PATCH 3/8] testing build with STATUS_HILITES --- win/tty/wintty.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 7b1802176..77835a5ec 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -2445,7 +2445,9 @@ char ch; 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); @@ -2501,6 +2503,10 @@ const char *str; register struct WinDesc *cw = 0; register char *ob; register long i, n0; +#ifndef STATUS_HILITES + register const char *nb; + register long j; +#endif /* Assume there's a real problem if the window is missing -- * probably a panic message @@ -3421,7 +3427,52 @@ char *posbar; * | 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() + * ->condition_size() - get the width of all 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 two status row's fields and + * calls tty_putstatusfield() to place them on the display. + * ->tty_putstatusfield() + * + * tty_putstatusfield() + * + * Move the cursor to the target spot, and output the field + * asked for by render_status(). + * + * ---------------------------------------------------------------- + */ + /* * The following data structures come from the genl_ routines in * src/windows.c and as such are considered to be on the window-port @@ -3841,7 +3892,7 @@ int x,y; print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS); - if (x <= ncols) { + if (x <= ncols && y < 2) { tty_curs(NHW_STATUS, x, y); for (i = 0; i < lth; ++i) { n = i + x; From 53c7cb8c78c2de69deec251f56bd28f6e9b08a72 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sun, 13 May 2018 18:54:20 -0700 Subject: [PATCH 4/8] Changes to xputc_core to handle cursor correctly. --- sys/winnt/nttty.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index d641b2981..61bd36f40 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -542,7 +542,8 @@ char ch; switch (ch) { case '\n': - console.cursor.Y++; + if (console.cursor.Y < console.height - 1) + console.cursor.Y++; /* fall through */ case '\r': console.cursor.X = 1; @@ -565,6 +566,12 @@ char ch; buffer_write(console.back_buffer, &cell, console.cursor); console.cursor.X++; + + if (console.cursor.X == console.width) { + console.cursor.X = 0; + if (console.cursor.Y < console.height - 1) + console.cursor.Y++; + } } } From 1f877dbca1ed0e92b61ef7a84b7eb5980f18e812 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sun, 13 May 2018 20:46:43 -0700 Subject: [PATCH 5/8] Additional changes to xputc_core() and early_raw_print() to manage the cursor position correctly. This is needed to handle raw printing correctly. Added check for when we might be running off the bottom of the screen when handling msmsg(). Added runtime checks to keep cursor always within bounds. --- sys/winnt/nttty.c | 117 ++++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 61bd36f40..6dfb77471 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -458,6 +458,15 @@ int *x, *y, *mod; return ch; } +static void set_console_cursor(int x, int y) +{ + ntassert(x >= 0 && x < console.width); + ntassert(y >= 0 && y < console.height); + + console.cursor.X = max(0, min(console.width - 1, x)); + console.cursor.Y = max(0, min(console.height - 1, y)); +} + static void really_move_cursor() { @@ -474,10 +483,9 @@ really_move_cursor() (void) SetConsoleTitle(newtitle); } #endif - if (ttyDisplay) { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; - } + if (ttyDisplay) + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); + back_buffer_flip(); SetConsoleCursorPosition(console.hConOut, console.cursor); } @@ -488,26 +496,25 @@ register int x, y; { ttyDisplay->cury = y; ttyDisplay->curx = x; - console.cursor.X = x; - console.cursor.Y = y; + + set_console_cursor(x, y); } void nocmov(x, y) int x, y; { - console.cursor.X = x; - console.cursor.Y = y; ttyDisplay->curx = x; ttyDisplay->cury = y; + + set_console_cursor(x, y); } void xputc(ch) char ch; { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); xputc_core(ch); } @@ -518,10 +525,8 @@ const char *s; int k; int slen = strlen(s); - if (ttyDisplay) { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; - } + if (ttyDisplay) + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); if (s) { for (k = 0; k < slen && s[k]; ++k) @@ -537,6 +542,9 @@ void xputc_core(ch) char ch; { + ntassert(console.cursor.X >= 0 && console.cursor.X < console.width); + ntassert(console.cursor.Y >= 0 && console.cursor.Y < console.height); + boolean inverse = FALSE; cell_t cell; @@ -549,7 +557,12 @@ char ch; console.cursor.X = 1; break; case '\b': - console.cursor.X--; + if (console.cursor.X > 1) { + console.cursor.X--; + } else if(console.cursor.Y > 0) { + console.cursor.X = console.width - 1; + console.cursor.Y--; + } break; default: inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse); @@ -565,14 +578,18 @@ char ch; buffer_write(console.back_buffer, &cell, console.cursor); - console.cursor.X++; - - if (console.cursor.X == console.width) { - console.cursor.X = 0; - if (console.cursor.Y < console.height - 1) + if (console.cursor.X == console.width - 1) { + if (console.cursor.Y < console.height - 1) { + console.cursor.X = 1; console.cursor.Y++; + } + } else { + console.cursor.X++; } } + + ntassert(console.cursor.X >= 0 && console.cursor.X < console.width); + ntassert(console.cursor.Y >= 0 && console.cursor.Y < console.height); } /* @@ -623,8 +640,7 @@ int in_ch; boolean inverse = FALSE; unsigned char ch = (unsigned char) in_ch; - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse); console.attr = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse) ? @@ -644,8 +660,7 @@ int in_ch; void cl_end() { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); buffer_clear_to_end_of_line(console.back_buffer, console.cursor.X, console.cursor.Y); tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury); @@ -667,15 +682,14 @@ clear_screen() void home() { - console.cursor.X = console.cursor.Y = 0; ttyDisplay->curx = ttyDisplay->cury = 0; + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); } void backsp() { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); xputc_core('\b'); } @@ -1082,6 +1096,17 @@ VA_DECL(const char *, fmt) if(!init_ttycolor_completed) init_ttycolor(); + /* if we have generated too many messages ... ask the user to + * confirm and then clear. + */ + if (console.cursor.Y > console.height - 4) { + xputs("Hit to continue."); + while (pgetchar() != '\n') + ; + raw_clear_screen(); + set_console_cursor(1, 0); + } + xputs(buf); if (ttyDisplay) curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); @@ -1714,35 +1739,49 @@ void early_raw_print(const char *s) if (console.hConOut == NULL) return; + ntassert(console.cursor.X >= 0 && console.cursor.X < console.width); + ntassert(console.cursor.Y >= 0 && console.cursor.Y < console.height); + WORD attribute = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; DWORD unused; while (*s != '\0') { switch (*s) { case '\n': - console.cursor.Y++; + if (console.cursor.Y < console.height - 1) + console.cursor.Y++; /* fall through */ case '\r': - console.cursor.X = 0; + console.cursor.X = 1; break; case '\b': - console.cursor.X--; + if (console.cursor.X > 1) { + console.cursor.X--; + } else if(console.cursor.Y > 0) { + console.cursor.X = console.width - 1; + console.cursor.Y--; + } break; default: WriteConsoleOutputAttribute(console.hConOut, &attribute, 1, console.cursor, &unused); WriteConsoleOutputCharacterA(console.hConOut, s, 1, console.cursor, &unused); - console.cursor.X++; - - if (console.cursor.X == console.width) { - console.cursor.Y++; - console.cursor.X = 0; + if (console.cursor.X == console.width - 1) { + if (console.cursor.Y < console.height - 1) { + console.cursor.X = 1; + console.cursor.Y++; + } + } else { + console.cursor.X++; } } s++; } + ntassert(console.cursor.X >= 0 && console.cursor.X < console.width); + ntassert(console.cursor.Y >= 0 && console.cursor.Y < console.height); + SetConsoleCursorPosition(console.hConOut, console.cursor); } @@ -1776,9 +1815,6 @@ void nethack_enter_nttty() /* set up state needed by early_raw_print() */ windowprocs.win_raw_print = early_raw_print; - console.cursor.X = 0; - console.cursor.Y = 0; - console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); ntassert(console.hConOut != NULL); // NOTE: this assert will not print @@ -1802,9 +1838,11 @@ void nethack_enter_nttty() console.buffer_size = console.width * console.height; + /* clear the entire console buffer */ int size = console.origcsbi.dwSize.X * console.origcsbi.dwSize.Y; DWORD unused; + set_console_cursor(0, 0); FillConsoleOutputAttribute( console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, size, console.cursor, &unused); @@ -1813,9 +1851,10 @@ void nethack_enter_nttty() console.hConOut, CONSOLE_CLEAR_CHARACTER, size, console.cursor, &unused); + set_console_cursor(1, 0); SetConsoleCursorPosition(console.hConOut, console.cursor); - /* At this point early_raw_print will output */ + /* At this point early_raw_print will work */ console.hConIn = GetStdHandle(STD_INPUT_HANDLE); ntassert(console.hConIn != NULL); From fba7c06fd63cfde58acbaf3f901f8f8582ffb5e0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 14 May 2018 21:09:01 -0400 Subject: [PATCH 6/8] put back the functionality of commandline --debug:immediateflips overwritten recently --- sys/winnt/nttty.c | 8 +++----- sys/winnt/stubs.c | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 6dfb77471..9b26a5e3f 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -206,8 +206,6 @@ keyboard_handler_t keyboard_handler; /* Console buffer flipping support */ -boolean do_immediate_flips = FALSE; - static void back_buffer_flip() { cell_t * back = console.back_buffer; @@ -250,7 +248,7 @@ void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y) while (dst != sentinel) *dst++ = *fill; - if (do_immediate_flips && buffer == console.back_buffer) + if (iflags.debug.immediateflips && buffer == console.back_buffer) back_buffer_flip(); } @@ -265,7 +263,7 @@ static void buffer_clear_to_end_of_line(cell_t * buffer, int x, int y) while (dst != sentinel) *dst++ = clear_cell; - if (do_immediate_flips) + if (iflags.debug.immediateflips) back_buffer_flip(); } @@ -277,7 +275,7 @@ void buffer_write(cell_t * buffer, cell_t * cell, COORD pos) cell_t * dst = buffer + (console.width * pos.Y) + pos.X; *dst = *cell; - if (do_immediate_flips && buffer == console.back_buffer) + if (iflags.debug.immediateflips && buffer == console.back_buffer) back_buffer_flip(); } diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index 469a0bdb2..ef161ae9b 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -179,5 +179,6 @@ nethack_enter_nttty() void set_altkeyhandler(const char *inName) { + return; } #endif /* TTYSTUBS */ From 2e8b69d5ff2d896c9768455c105ad428c566538e Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 14 May 2018 21:13:37 -0400 Subject: [PATCH 7/8] some tty per field rendering and optimization --- doc/fixes36.2 | 1 + win/tty/wintty.c | 77 +++++++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ca45e5826..35d75d637 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -17,6 +17,7 @@ Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository Platform- and/or Interface-Specific Fixes ----------------------------------------- windows-tty: Specify both width and height when creating font for width testing +tty: some optimizations for performance and per field rendering Code Cleanup and Reorganization diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 77835a5ec..b9ac757c0 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3650,6 +3650,8 @@ unsigned long *colormasks; int i; long *condptr = (long *) ptr; char *text = (char *) ptr; + char *lastchar = (char *) 0; + char *fval = (char *) 0; boolean do_color = FALSE; boolean force_update = FALSE; @@ -3672,15 +3674,6 @@ unsigned long *colormasks; tty_status[NOW][fldidx].dirty = TRUE; truncation_expected = FALSE; break; - case BL_CAP: - for (i = 0; i < SIZE(encvals); ++i) { - if (!strcmp(encvals[enc_shrinklvl][i], - status_vals[BL_CAP])) { - enclev = i; - break; - } - } - /*FALLTHRU*/ default: tty_status[NOW][fldidx].idx = fldidx; Sprintf(status_vals[fldidx], @@ -3692,16 +3685,9 @@ unsigned long *colormasks; tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]); tty_status[NOW][fldidx].valid = TRUE; tty_status[NOW][fldidx].dirty = TRUE; - break; } - /* Special additional processing for hitpointbar */ - if (iflags.wc2_hitpointbar && fldidx == BL_HP) { - hpbar_percent = percent; - hpbar_color = do_color ? (color & 0x00FF) : NO_COLOR; - } - /* The core botl engine sends a single blank to the window port for carrying-capacity when its unused. Let's suppress that */ if (tty_status[NOW][fldidx].lth == 1 @@ -3710,24 +3696,49 @@ unsigned long *colormasks; tty_status[NOW][fldidx].lth = 0; } - /* The core botl engine sends trailing blanks for some fields - Let's suppress the trailing blanks */ - if (fldidx == BL_LEVELDESC || fldidx == BL_HUNGER) { - char *lastchar = eos(status_vals[fldidx]); - lastchar--; - while (lastchar && *lastchar == ' ' - && lastchar >= status_vals[fldidx]) { - *lastchar-- = '\0'; - tty_status[NOW][fldidx].lth--; - } + /* 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 = do_color ? (color & 0x00FF) : NO_COLOR; + } + break; + case BL_LEVELDESC: + case BL_HUNGER: + /* The core sends trailing blanks for some fields + Let's suppress the trailing blanks */ + char *lastchar = eos(status_vals[fldidx]); + lastchar = eos(status_vals[fldidx]); + lastchar--; + while (lastchar && *lastchar == ' ' + && lastchar >= status_vals[fldidx]) { + *lastchar-- = '\0'; + tty_status[NOW][fldidx].lth--; + } + break; + case BL_TITLE: + if (iflags.wc2_hitpointbar) + tty_status[NOW][fldidx].lth += 2; /* '[' and ']' */ + break; + case BL_GOLD: + tty_status[NOW][fldidx].lth -= 9; /* \GXXXXNNNN counts as 1 */ + break; + case BL_CAP: + fval = status_vals[fldidx]; + if (fval) { + if (*fval == ' ') + fval++; + for (i = 0; i < SIZE(encvals); ++i) { + if (!strcmp(encvals[enc_shrinklvl][i], fval)) { + enclev = i; + break; /* for */ + } + } + } + break; } - - /* Some other special case fixups */ - if (iflags.wc2_hitpointbar && fldidx == BL_TITLE) - tty_status[NOW][fldidx].lth += 2; /* [ and ] */ - if (fldidx == BL_GOLD) - tty_status[NOW][fldidx].lth -= 9; /* \GXXXXNNNN counts as 1 */ - if (make_things_fit(force_update) || truncation_expected) render_status(); return; From 26654ef0797db5ba68c4ffd5ac4c3b55a5ecfdd1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 14 May 2018 22:25:25 -0400 Subject: [PATCH 8/8] a few cut-and-paste errors --- win/tty/wintty.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index b9ac757c0..b13318cc2 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3538,7 +3538,7 @@ static enum statusfields fieldorder[2][15] = { /* 2: two status lines */ static boolean windowdata_init = FALSE; static int cond_shrinklvl = 0, cond_width_at_shrink = 0; static int enclev = 0, enc_shrinklvl = 0; -static dl_shrinklvl = 0; +static int dl_shrinklvl = 0; static boolean truncation_expected = FALSE; /* This controls whether to skip fields that aren't @@ -3709,7 +3709,6 @@ unsigned long *colormasks; case BL_HUNGER: /* The core sends trailing blanks for some fields Let's suppress the trailing blanks */ - char *lastchar = eos(status_vals[fldidx]); lastchar = eos(status_vals[fldidx]); lastchar--; while (lastchar && *lastchar == ' '