diff --git a/include/extern.h b/include/extern.h index b6f482515..96a5ade8b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -26,7 +26,7 @@ E void NDECL(display_gamewindows); E void NDECL(newgame); E void FDECL(welcome, (BOOLEAN_P)); E time_t NDECL(get_realtime); -E boolean FDECL(argcheck, (int, char **, enum earlyarg)); +E int FDECL(argcheck, (int, char **, enum earlyarg)); /* ### apply.c ### */ @@ -786,6 +786,7 @@ E void NDECL(read_wizkit); E int FDECL(read_sym_file, (int)); E int FDECL(parse_sym_line, (char *, int)); E void FDECL(paniclog, (const char *, const char *)); +E void FDECL(testinglog, (const char *, const char *, const char *)); E int FDECL(validate_prefix_locations, (char *)); #ifdef SELECTSAVED E char *FDECL(plname_from_file, (const char *)); @@ -1157,6 +1158,7 @@ E boolean FDECL(usmellmon, (struct permonst *)); E int FDECL(mapglyph, (int, int *, int *, unsigned *, int, int)); E char *FDECL(encglyph, (int)); +E const char *FDECL(decode_mixed, (char *,const char *)); E void FDECL(genl_putmixed, (winid, int, const char *)); /* ### mcastu.c ### */ diff --git a/include/flag.h b/include/flag.h index 5dd13b2ec..277b42cfe 100644 --- a/include/flag.h +++ b/include/flag.h @@ -221,6 +221,16 @@ enum getloc_filters { NUM_GFILTER }; +struct debug_flags { + boolean test; +#ifdef TTY_GRAPHICS + boolean ttystatus; +#endif +#ifdef WIN32 + boolean immediateflips; +#endif +}; + struct instance_flags { /* stuff that really isn't option or platform related. They are * set and cleared during the game to control the internal @@ -423,6 +433,7 @@ struct instance_flags { short mines_prize_type; /* luckstone */ short soko_prize_type1; /* bag of holding or */ short soko_prize_type2; /* amulet of reflection */ + struct debug_flags debug; }; /* diff --git a/include/global.h b/include/global.h index 420cd8491..831717c36 100644 --- a/include/global.h +++ b/include/global.h @@ -8,7 +8,7 @@ #include -/* #define BETA */ /* development or beta testing [MRS] */ +#define BETA /* development or beta testing [MRS] */ #define DEBUG diff --git a/include/wintty.h b/include/wintty.h index 1f270128f..a4a6625a5 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -69,6 +69,19 @@ struct DisplayDesc { #endif /* WINDOW_STRUCTS */ +#ifdef STATUS_HILITES +struct tty_status_fields { + int idx; + int color; + int attr; + int x, y; + size_t lth; + boolean valid; + boolean dirty; + boolean redraw; +}; +#endif + #define MAXWIN 20 /* maximum number of windows, cop-out */ /* tty dependent window types */ diff --git a/src/allmain.c b/src/allmain.c index 6942aa232..2c1364d75 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -6,6 +6,7 @@ /* various code that was replicated in *main.c */ #include "hack.h" +#include #ifndef NO_SIGNAL #include @@ -16,6 +17,7 @@ STATIC_DCL void NDECL(do_positionbar); #endif STATIC_DCL void FDECL(regen_hp, (int)); STATIC_DCL void FDECL(interrupt_multi, (const char *)); +STATIC_DCL void FDECL(debug_fields, (const char *)); void moveloop(resuming) @@ -752,11 +754,18 @@ const char *msg; */ static struct early_opt earlyopts[] = { - {ARG_DEBUG, "debug", 5, FALSE}, + {ARG_DEBUG, "debug", 5, TRUE}, {ARG_VERSION, "version", 4, TRUE}, }; -boolean +/* + * Returns: + * 0 = no match + * 1 = found and skip past this argument + * 2 = found and trigger immediate exit + */ + +int argcheck(argc, argv, e_arg) int argc; char *argv[]; @@ -774,7 +783,7 @@ enum earlyarg e_arg; if ((idx >= SIZE(earlyopts)) || (argc <= 1)) return FALSE; - for (i = 1; i < argc; ++i) { + for (i = 0; i < argc; ++i) { if (argv[i][0] != '-') continue; if (argv[i][1] == '-') { @@ -789,15 +798,20 @@ enum earlyarg e_arg; } if (match) { + const char *extended_opt = index(userea,':'); + + if (!extended_opt) + extended_opt = index(userea, '='); switch(e_arg) { case ARG_DEBUG: + if (extended_opt) { + extended_opt++; + debug_fields(extended_opt); + } + return 1; break; case ARG_VERSION: { boolean insert_into_pastebuf = FALSE; - const char *extended_opt = index(userea,':'); - - if (!extended_opt) - extended_opt = index(userea, '='); if (extended_opt) { extended_opt++; @@ -812,7 +826,7 @@ enum earlyarg e_arg; } } early_version_info(insert_into_pastebuf); - return TRUE; + return 2; break; } default: @@ -822,4 +836,62 @@ enum earlyarg e_arg; return FALSE; } +/* + * These are internal controls to aid developers with + * testing and debugging particular aspects of the code. + * They are not player options and the only place they + * are documented is right here. No gameplay is altered. + * + * test - test whether this parser is working + * ttystatus - TTY: + * immediateflips - WIN32: turn off display performance + * optimization so that display output + * can be debugged without buffering. + */ +void +debug_fields(opts) +const char *opts; +{ + char *op; + boolean negated = FALSE; + + while ((op = index(opts, ',')) != 0) { + *op++ = 0; + /* recurse */ + debug_fields(op); + } + if (strlen(opts) > BUFSZ / 2) + return; + + + /* strip leading and trailing white space */ + while (isspace((uchar) *opts)) + opts++; + op = eos((char *) opts); + while (--op >= opts && isspace((uchar) *op)) + *op = '\0'; + + if (!*opts) { + /* empty */ + return; + } + while ((*opts == '!') || !strncmpi(opts, "no", 2)) { + if (*opts == '!') + opts++; + else + opts += 2; + negated = !negated; + } + if (match_optname(opts, "test", 4, FALSE)) + iflags.debug.test = negated ? FALSE : TRUE; +#ifdef TTY_GRAPHICS + if (match_optname(opts, "ttystatus", 9, FALSE)) + iflags.debug.ttystatus = negated ? FALSE : TRUE; +#endif +#ifdef WIN32 + if (match_optname(opts, "immediateflips", 14, FALSE)) + iflags.debug.immediateflips = negated ? FALSE : TRUE; +#endif + return; +} /*allmain.c*/ diff --git a/src/botl.c b/src/botl.c index f58c45947..68a38b62b 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1506903619 2017/10/02 00:20:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1525696908 2018/05/07 12:41:48 $ $NHDT-Branch: tty-status $:$NHDT-Revision: 1.91 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -483,14 +483,14 @@ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH), INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN), INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE), - INIT_BLSTAT("carrying-capacity", " %s", ANY_LONG, 20, BL_CAP), + INIT_BLSTAT("carrying-capacity", " %s", ANY_INT, 20, BL_CAP), INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD), INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE), INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX), - INIT_BLSTAT("experience-level", " Xp:%s", ANY_LONG, 10, BL_XP), + INIT_BLSTAT("experience-level", " Xp:%s", ANY_INT, 10, BL_XP), INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC), INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD), - INIT_BLSTAT("time", " T:%s", ANY_INT, 20, BL_TIME), + INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME), INIT_BLSTAT("hunger", " %s", ANY_UINT, 40, BL_HUNGER), INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP), INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX), @@ -630,7 +630,7 @@ bot_via_windowport() /* Experience */ blstats[idx][BL_XP].a.a_int = u.ulevel; - blstats[idx][BL_EXP].a.a_int = u.uexp; + blstats[idx][BL_EXP].a.a_long = u.uexp; /* Time (moves) */ blstats[idx][BL_TIME].a.a_long = moves; @@ -858,7 +858,10 @@ boolean : TRUE; fieldname = initblstats[i].fldname; - fieldfmt = initblstats[i].fldfmt; + if (fld == BL_TITLE && iflags.wc2_hitpointbar) + fieldfmt = "%-30s"; + else + fieldfmt = initblstats[i].fldfmt; status_enablefield(fld, fieldname, fieldfmt, fldenabled); } update_all = TRUE; diff --git a/src/files.c b/src/files.c index 8a490d09e..cd5277acb 100644 --- a/src/files.c +++ b/src/files.c @@ -3542,7 +3542,7 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; /* ---------- END SCOREBOARD CREATION ----------- */ -/* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */ +/* ---------- BEGIN PANIC/IMPOSSIBLE/TESTING LOG ----------- */ /*ARGSUSED*/ void @@ -3579,7 +3579,31 @@ const char *reason; /* explanation */ return; } -/* ---------- END PANIC/IMPOSSIBLE LOG ----------- */ +/*ARGSUSED*/ +void +testinglog(filenm, type, reason) +const char *filenm; /* ad hoc file name */ +const char *type; +const char *reason; /* explanation */ +{ + FILE *lfile; + char fnbuf[BUFSZ]; + + if (!filenm) return; + Strcpy(fnbuf, filenm); + if (index(fnbuf, '.') == 0) + Strcat(fnbuf, ".log"); + lfile = fopen_datafile(fnbuf, "a", TROUBLEPREFIX); + if (lfile) { + time_t now = getnow(); + int uid = getuid(); + (void) fprintf(lfile, "%s\n%s\n", type, reason); + (void) fclose(lfile); + } + return; +} + +/* ---------- END PANIC/IMPOSSIBLE/TESTING LOG ----------- */ #ifdef SELF_RECOVER diff --git a/src/mapglyph.c b/src/mapglyph.c index 4ba4ee7e6..04ef068e9 100644 --- a/src/mapglyph.c +++ b/src/mapglyph.c @@ -247,6 +247,78 @@ int glyph; return encbuf; } +const char * +decode_mixed(buf, str) +char *buf; +const char *str; +{ + static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; + char *put = buf; + + if (!put || !str) + return ""; + + while (*str) { + if (*str == '\\') { + int rndchk, dcount, so, gv, ch = 0, oc = 0; + unsigned os = 0; + const char *dp, *save_str; + + save_str = str++; + switch (*str) { + case 'G': /* glyph value \GXXXXNNNN*/ + rndchk = dcount = 0; + for (++str; *str && ++dcount <= 4; ++str) + if ((dp = index(hex, *str)) != 0) + rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); + else + break; + if (rndchk == context.rndencode) { + gv = dcount = 0; + for (; *str && ++dcount <= 4; ++str) + if ((dp = index(hex, *str)) != 0) + gv = (gv * 16) + ((int) (dp - hex) / 2); + else + break; + so = mapglyph(gv, &ch, &oc, &os, 0, 0); + *put++ = showsyms[so]; + /* 'str' is ready for the next loop iteration and '*str' + should not be copied at the end of this iteration */ + continue; + } else { + /* possible forgery - leave it the way it is */ + str = save_str; + } + break; +#if 0 + case 'S': /* symbol offset */ + so = rndchk = dcount = 0; + for (++str; *str && ++dcount <= 4; ++str) + if ((dp = index(hex, *str)) != 0) + rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); + else + break; + if (rndchk == context.rndencode) { + dcount = 0; + for (; *str && ++dcount <= 2; ++str) + if ((dp = index(hex, *str)) != 0) + so = (so * 16) + ((int) (dp - hex) / 2); + else + break; + } + *put++ = showsyms[so]; + break; +#endif + case '\\': + break; + } + } + *put++ = *str++; + } + *put = '\0'; + return buf; +} + /* * This differs from putstr() because the str parameter can * contain a sequence of characters representing: @@ -265,71 +337,9 @@ winid window; int attr; const char *str; { - static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; char buf[BUFSZ]; - const char *cp = str; - char *put = buf; - - while (*cp) { - if (*cp == '\\') { - int rndchk, dcount, so, gv, ch = 0, oc = 0; - unsigned os = 0; - const char *dp, *save_cp; - - save_cp = cp++; - switch (*cp) { - case 'G': /* glyph value \GXXXXNNNN*/ - rndchk = dcount = 0; - for (++cp; *cp && ++dcount <= 4; ++cp) - if ((dp = index(hex, *cp)) != 0) - rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); - else - break; - if (rndchk == context.rndencode) { - gv = dcount = 0; - for (; *cp && ++dcount <= 4; ++cp) - if ((dp = index(hex, *cp)) != 0) - gv = (gv * 16) + ((int) (dp - hex) / 2); - else - break; - so = mapglyph(gv, &ch, &oc, &os, 0, 0); - *put++ = showsyms[so]; - /* 'cp' is ready for the next loop iteration and '*cp' - should not be copied at the end of this iteration */ - continue; - } else { - /* possible forgery - leave it the way it is */ - cp = save_cp; - } - break; -#if 0 - case 'S': /* symbol offset */ - so = rndchk = dcount = 0; - for (++cp; *cp && ++dcount <= 4; ++cp) - if ((dp = index(hex, *cp)) != 0) - rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); - else - break; - if (rndchk == context.rndencode) { - dcount = 0; - for (; *cp && ++dcount <= 2; ++cp) - if ((dp = index(hex, *cp)) != 0) - so = (so * 16) + ((int) (dp - hex) / 2); - else - break; - } - *put++ = showsyms[so]; - break; -#endif - case '\\': - break; - } - } - *put++ = *cp++; - } - *put = '\0'; /* now send it to the normal putstr */ - putstr(window, attr, buf); + putstr(window, attr, decode_mixed(buf, str)); } /*mapglyph.c*/ diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index eea29f8de..6bb6cfc29 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -331,9 +331,14 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(hackdir, HACKDIR); #endif if (argc > 1) { - if (argcheck(argc, argv, ARG_VERSION)) + if (argcheck(argc, argv, ARG_VERSION) == 2) nethack_exit(EXIT_SUCCESS); + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + argc--; + argv++; + } + if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { /* avoid matching "-dec" for DECgraphics; since the man page * says -d directory, hope nobody's using -desomething_else diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index c856e758b..3c91b3e55 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -111,9 +111,14 @@ char *argv[]; dir = nh_getenv("HACKDIR"); if (argc > 1) { - if (argcheck(argc, argv, ARG_VERSION)) + if (argcheck(argc, argv, ARG_VERSION) == 2) exit(EXIT_SUCCESS); + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + argc--; + argv++; + } + if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { /* avoid matching "-dec" for DECgraphics; since the man page * says -d directory, hope nobody's using -desomething_else diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index f0532b083..4fbc11c63 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 nttty.c $NHDT-Date: 1524931557 2018/04/28 16:05:57 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.71 $ */ +/* NetHack 3.6 nttty.c $NHDT-Date: 1525643540 2018/05/06 21:52:20 $ $NHDT-Branch: tty-status $:$NHDT-Revision: 1.77 $ */ /* Copyright (c) NetHack PC Development Team 1993 */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,6 +9,7 @@ * Switch to low level console output routines M. Allison 2003/10/01 * Restrict cursor movement until input pending M. Lehotay 2003/10/02 * Call Unicode version of output API on NT R. Chason 2005/10/28 + * Use of back buffer to improve performance B. House 2018/05/06 * */ @@ -151,6 +152,249 @@ SOURCEWHERE pSourceWhere; SOURCEAUTHOR pSourceAuthor; KEYHANDLERNAME pKeyHandlerName; +/* CP437 to Unicode mapping according to the Unicode Consortium */ +static const WCHAR cp437[] = { + 0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, + 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, + 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, + 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, + 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, + 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, + 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, + 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, + 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, + 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, + 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, + 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 +}; + +/* + * cpConsole provides the mapping of characters in the console code page to + * UNICODE. It maps a character to at most two WCHARs storing the number of + * WCHARs in count. + * + * NOTE: cpConsole is only valid if has_unicode is TRUE. + */ + +typedef struct { + WCHAR characters[2]; + int count; +} CodePageMapping; + +static CodePageMapping cpConsole[256]; + +static void initialize_cp_console() +{ + if (has_unicode) { + UINT codePage = GetConsoleOutputCP(); + + for (int i = 0; i < 256; i++) { + char c = (char)i; + cpConsole[i].count = MultiByteToWideChar(codePage, 0, &c, 1, + &cpConsole[i].characters[0], 2); + } + } +} + +/* + * Console Buffer Flipping Support + * + * To minimize the number of calls into the WriteConsoleOutputXXX methods, + * we implement a notion of a console back buffer which keeps the next frame + * of console output as it is being composed. When ready to show the new + * frame, we compare this next frame to what is currently being output and + * only call WriteConsoleOutputXXX for those console values that need to + * change. + * + */ + +#define CONSOLE_CLEAR_ATTRIBUTE (FOREGROUND_RED | FOREGROUND_GREEN \ + | FOREGROUND_BLUE) +#define CONSOLE_CLEAR_CHARACTER (' ') + +typedef struct { + WCHAR characters[2]; + int count; + WORD attribute; +} cell_t; + +typedef struct { + cell_t * cells; +} console_buffer_t; + +static int buffer_width; +static int buffer_height; + +console_buffer_t back_buffer; +console_buffer_t front_buffer; +cell_t clear_cell; +cell_t undefined_cell; + +static boolean buffer_flipping_initialized = FALSE; + +static void check_buffer_size(int width, int height); +static cell_t * buffer_get_cell(console_buffer_t * buffer, int x, int y); + +static void back_buffer_flip(); + +static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * cell, + int x, int y); +static void back_buffer_clear_to_end_of_line(int x, int y); +static void back_buffer_write(cell_t * cell, int x, int y); + +static void initialize_buffer_flipping(int width, int height) +{ + if (buffer_flipping_initialized) { + check_buffer_size(width, height); + return; + } + + buffer_width = 0; + buffer_height = 0; + + back_buffer.cells = NULL; + front_buffer.cells = NULL; + + clear_cell.attribute = CONSOLE_CLEAR_ATTRIBUTE; + clear_cell.characters[0] = CONSOLE_CLEAR_CHARACTER; + clear_cell.characters[1] = 0; + clear_cell.count = 1; + + undefined_cell = clear_cell; + undefined_cell.count = 0; + + check_buffer_size(width, height); + + buffer_flipping_initialized = TRUE; +} + +static void resize_buffer(console_buffer_t * buffer, cell_t * fill, + int width, int height) +{ + cell_t * cells = (cell_t *)malloc(sizeof(cell_t) * width * height); + cell_t * dst = cells; + cell_t * sentinel = dst + (width * height); + + while (dst != sentinel) + *dst++ = *fill; + + int height_to_copy = (buffer_height > height ? height : buffer_height); + int bytes_to_copy = (buffer_width > width ? width : buffer_width) + * sizeof(cell_t); + + for (int y = 0; y < height_to_copy; y++) + memcpy(cells + (width * y), buffer->cells + (buffer_width * y), + bytes_to_copy); + + free(buffer->cells); + buffer->cells = cells; +} + +static void check_buffer_size(int width, int height) +{ + if (width != buffer_width || height != buffer_height) { + resize_buffer(&back_buffer, &clear_cell, width, height); + resize_buffer(&front_buffer, &undefined_cell, width, height); + buffer_width = width; + buffer_height = height; + } +} + +static cell_t * buffer_get_cell(console_buffer_t * buffer, int x, int y) +{ + return buffer->cells + (buffer_width * y) + x; +} + +static void back_buffer_flip() +{ + if (!buffer_flipping_initialized) + return; + + cell_t * back = back_buffer.cells; + cell_t * front = front_buffer.cells; + COORD pos; + + for (pos.Y = 0; pos.Y < buffer_height; pos.Y++) { + for (pos.X = 0; pos.X < buffer_width; pos.X++) { + if (back->attribute != front->attribute) { + WriteConsoleOutputAttribute(hConOut, &back->attribute, + 1, pos, &acount); + front->attribute = back->attribute; + } + if (back->count != front->count || + back->characters[0] != front->characters[0] || + back->characters[1] != front->characters[1]) { + if (has_unicode) { + WriteConsoleOutputCharacterW(hConOut, back->characters, + back->count, pos, &ccount); + } else { + char ch = (char)back->characters[0]; + WriteConsoleOutputCharacterA(hConOut, &ch, 1, pos, + &ccount); + } + *front = *back; + } + back++; + front++; + } + } +} + +static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * src, + int x, int y) +{ + cell_t * dst = buffer_get_cell(buffer, x, y); + cell_t * sentinel = buffer_get_cell(buffer, 0, buffer_height); + while (dst != sentinel) + *dst++ = clear_cell; + + if (iflags.debug.immediateflips && buffer == &back_buffer) + back_buffer_flip(); +} + +static void back_buffer_write(cell_t * cell, int x, int y) +{ + cell_t * dst = buffer_get_cell(&back_buffer, x, y); + *dst = *cell; + + if (iflags.debug.immediateflips) + back_buffer_flip(); +} + +static void back_buffer_clear_to_end_of_line(int x, int y) +{ + cell_t * cell; + cell_t *sentinel; + + cell = buffer_get_cell(&back_buffer, x, y); + sentinel = buffer_get_cell(&back_buffer, 0, y+1); + while (cell != sentinel) + *cell++ = clear_cell; + + if (iflags.debug.immediateflips) + back_buffer_flip(); +} + /* * Called after returning from ! or ^Z */ @@ -197,7 +441,6 @@ setftty() adjust_palette(); #endif start_screen(); - has_unicode = ((GetVersion() & 0x80000000) == 0); } void @@ -232,6 +475,10 @@ tty_end_screen() clear_screen(); really_move_cursor(); if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { + + buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0); + buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0); + DWORD ccnt; COORD newcoord; @@ -270,7 +517,16 @@ DWORD ctrltype; } } -/* called by init_tty in wintty.c for WIN32 port only */ +/* + * ntty_open() is called in several places. It is called by win_tty_init + * passing in a mode of zero. It is then later called again by pcmain passing + * in a mode of one. Finally, it can also be called by process_options also + * with a mode of one. + * + * barthouse - The fact this is getting called multiple times needs to be + * reviewed and perhaps cleaned up. + * + */ void nttty_open(mode) int mode; @@ -279,6 +535,10 @@ int mode; DWORD cmode; long mask; + has_unicode = ((GetVersion() & 0x80000000) == 0); + + initialize_cp_console(); + GUILaunched = 0; try : @@ -307,6 +567,9 @@ int mode; mode = 0; goto try; } else { + /* barthouse - Need to understand how this can happen and + * whether we should bail instead of returning. + */ return; } @@ -314,6 +577,30 @@ int mode; hConIn = GetStdHandle(STD_INPUT_HANDLE); hConOut = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hConOut, &csbi); + + int height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + + /* NOTE: We currently force to a width of 80 due to unresolved issues + * within the TTY code. These issues will need to resolved before + * we could allow widths > 80. We will always need a width of + * atleast 80. + */ + + int width = 80; + + /* If the window is not big enough to meet our minimum height needs, + * grow the console's buffer to be large enough. The user will have + * to manually extend the size of the window. + */ + + height = max(25, height); + + COORD size = { width, height }; + SetConsoleScreenBufferSize(hConOut, size); + + initialize_buffer_flipping(width, height); + load_keyboard_handler(); /* Initialize the function pointer that points to * the kbhit() equivalent, in this TTY case nttty_kbhit() @@ -338,7 +625,10 @@ int mode; /* Unable to set control handler */ cmode = 0; /* just to have a statement to break on for debugger */ } - get_scr_size(); + + LI = height; + CO = width; + console.cursor.X = console.cursor.Y = 0; really_move_cursor(); } @@ -363,32 +653,6 @@ nttty_kbhit() return pNHkbhit(hConIn, &ir); } -void -get_scr_size() -{ - int lines, cols; - - GetConsoleScreenBufferInfo(hConOut, &csbi); - - lines = csbi.srWindow.Bottom - (csbi.srWindow.Top + 1); - cols = csbi.srWindow.Right - (csbi.srWindow.Left + 1); - - LI = lines; - CO = min(cols, 80); - - if ((LI < 25) || (CO < 80)) { - COORD newcoord; - - LI = 25; - CO = 80; - - newcoord.Y = LI; - newcoord.X = CO; - - SetConsoleScreenBufferSize(hConOut, newcoord); - } -} - int tgetch() { @@ -440,6 +704,8 @@ really_move_cursor() console.cursor.Y = ttyDisplay->cury; } SetConsoleCursorPosition(hConOut, console.cursor); + + back_buffer_flip(); } void @@ -498,6 +764,7 @@ xputc_core(ch) char ch; { boolean inverse = FALSE; + cell_t cell; switch (ch) { case '\n': console.cursor.Y++; @@ -515,17 +782,19 @@ char ch; ttycolors[console.current_nhcolor]; if (console.current_nhattr[ATR_BOLD]) console.attr |= (inverse) ? - BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; - WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount); + BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; + + cell.attribute = console.attr; if (has_unicode) { - /* Avoid bug in ANSI API on WinNT */ - WCHAR c2[2]; - int rc; - rc = MultiByteToWideChar(GetConsoleOutputCP(), 0, &ch, 1, c2, 2); - WriteConsoleOutputCharacterW(hConOut, c2, rc, console.cursor, &ccount); + cell.characters[0] = cpConsole[ch].characters[0]; + cell.characters[1] = cpConsole[ch].characters[1]; + cell.count = cpConsole[ch].count; } else { - WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount); + cell.characters[0] = ch; + cell.characters[1] = 0; + cell.count = 1; } + back_buffer_write(&cell, console.cursor.X, console.cursor.Y); console.cursor.X++; } } @@ -535,46 +804,11 @@ char ch; * for win32. It is used for glyphs only, not text. */ -/* CP437 to Unicode mapping according to the Unicode Consortium */ -static const WCHAR cp437[] = { - 0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, - 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, - 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, - 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, - 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, - 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, - 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, - 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, - 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, - 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, - 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, - 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, - 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, - 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, - 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, - 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, - 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, - 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, - 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 -}; - void g_putch(in_ch) int in_ch; { + cell_t cell; boolean inverse = FALSE; unsigned char ch = (unsigned char) in_ch; @@ -587,23 +821,24 @@ int in_ch; ttycolors[console.current_nhcolor]; if (console.current_nhattr[ATR_BOLD]) console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; - WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount); - + cell.attribute = console.attr; + cell.characters[1] = 0; + cell.count = 1; if (has_unicode) - WriteConsoleOutputCharacterW(hConOut, &cp437[ch], 1, console.cursor, &ccount); + cell.characters[0] = cp437[ch]; else - WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount); + cell.characters[0] = ch; + back_buffer_write(&cell, console.cursor.X, console.cursor.Y); } void cl_end() { - int cx; console.cursor.X = ttyDisplay->curx; console.cursor.Y = ttyDisplay->cury; - cx = CO - console.cursor.X; - FillConsoleOutputAttribute(hConOut, DEFTEXTCOLOR, cx, console.cursor, &acount); - FillConsoleOutputCharacter(hConOut, ' ', cx, console.cursor, &ccount); + + back_buffer_clear_to_end_of_line(console.cursor.X, console.cursor.Y); + tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury); } @@ -611,6 +846,10 @@ void raw_clear_screen() { if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { + + buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0); + buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0); + DWORD ccnt; COORD newcoord; @@ -621,6 +860,7 @@ raw_clear_screen() csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); FillConsoleOutputCharacter( hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); + } } @@ -651,11 +891,18 @@ cl_eos() { int cy = ttyDisplay->cury + 1; if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { + + buffer_fill_to_end(&front_buffer, &clear_cell, ttyDisplay->curx, + ttyDisplay->cury); + buffer_fill_to_end(&back_buffer, &clear_cell, ttyDisplay->curx, + ttyDisplay->cury); + DWORD ccnt; COORD newcoord; newcoord.X = ttyDisplay->curx; newcoord.Y = ttyDisplay->cury; + FillConsoleOutputAttribute( hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, csbi.dwSize.X * csbi.dwSize.Y - cy, newcoord, &ccnt); @@ -684,6 +931,7 @@ tty_delay_output() int k; goal = 50 + clock(); + back_buffer_flip(); while (goal > clock()) { k = junk; /* Do nothing */ } @@ -1063,10 +1311,10 @@ VA_DECL(const char *, fmt) else { if(!init_ttycolor_completed) init_ttycolor(); - xputs(buf); if (ttyDisplay) curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); + really_move_cursor(); } VA_END(); return; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 63d1aac8c..dbfc6d863 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1520825319 2018/03/12 03:28:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.142 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1525885919 2018/05/09 17:11:59 $ $NHDT-Branch: tty-status $:$NHDT-Revision: 1.146 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -66,7 +66,7 @@ struct window_procs tty_procs = { | WC2_SELECTSAVED #endif #if defined(STATUS_HILITES) - | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS + | WC2_HILITE_STATUS | WC2_HITPOINTBAR #endif | WC2_DARKGRAY), tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, @@ -176,6 +176,14 @@ 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)); +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); +#endif /* * A string containing all the default commands -- to add to a list @@ -2489,8 +2497,7 @@ const char *str; { register struct WinDesc *cw = 0; register char *ob; - register const char *nb; - register long i, j, n0; + register long i, n0; /* Assume there's a real problem if the window is missing -- * probably a panic message @@ -2518,7 +2525,7 @@ const char *str; #endif update_topl(str); break; - +#ifndef STATUS_HILITES case NHW_STATUS: ob = &cw->data[cw->cury][j = cw->curx]; if (context.botlx) @@ -2532,32 +2539,14 @@ const char *str; nb = str; for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) { if (!*nb) { -#ifndef STATUS_HILITES if (*ob || context.botlx) { -#else - /* STATUS_HILITES will call cl_end() when finished - * its sequence of putstr's. We don't want to call - * cl_end() with each putstr() which may cause flashing - * in the Windows port - */ - if (context.botlx) { -#endif /* last char printed may be in middle of line */ tty_curs(WIN_STATUS, i, cw->cury); cl_end(); } break; } -#ifdef STATUS_HILITES - /* Don't optimize the putsym away, in case it happens - to be the same character but different color/attr. - We don't optimize on iflags.use_status_hilites either, - in case old chars were written with highlighting and - that option has just now been toggled off. [We could - do better by tracking color/attr more closely.] */ -#else if (*ob != *nb) -#endif tty_putsym(WIN_STATUS, i, cw->cury, *nb); if (*ob) ob++; @@ -2565,11 +2554,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 */ -#ifndef STATUS_HILITES cw->cury = (cw->cury + 1) % 2; cw->curx = 0; -#endif break; +#endif /* STATUS_HILITES */ case NHW_MAP: tty_curs(window, cw->curx + 1, cw->cury); term_start_attr(attr); @@ -3426,36 +3414,108 @@ char *posbar; video_update_positionbar(posbar); #endif } -#endif +#endif /* POSITIONBAR */ + +/* + * +------------------+ + * | STATUS CODE | + * +------------------+ + */ + /* * 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_fieldfmt[MAXBLSTATS]; extern char *status_vals[MAXBLSTATS]; extern boolean status_activefields[MAXBLSTATS]; extern winid WIN_STATUS; -#ifdef STATUS_HILITES -static long tty_condition_bits; -static int tty_status_colors[MAXBLSTATS]; -int hpbar_percent, hpbar_color; +const char *fieldnames[] = { + "title", "strength", "dexterity", "constitution", "intelligence", + "wisdom", "charisma", "alignment", "score", "carrying-capacity", + "gold", "power", "power-max", "experience-level", "armor-class", + "HD", "time", "hunger", "hitpoints", "hitpoints-max", + "dungeon-level", "experience", "condition", +}; +#ifdef STATUS_HILITES static int FDECL(condcolor, (long, unsigned long *)); static int FDECL(condattr, (long, unsigned long *)); -#endif /* STATUS_HILITES */ +static long tty_condition_bits; +static unsigned long *tty_colormasks; +static struct tty_status_fields + tty_status[2][MAXBLSTATS]; /* 2: first index is for current + and previous */ +static int hpbar_percent, hpbar_color; +static struct condition_t { + long mask; + const char *text[3]; /* 3: potential display values, progressively + * smaller */ +} 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", "Cn"}}, + { BL_MASK_HALLU, {"Hallu", "Hal", "Ha"}}, + { BL_MASK_LEV, {"Lev", "Lev", "Lv"}}, + { BL_MASK_FLY, {"Fly", "Fly", "Fl"}}, + { BL_MASK_RIDE, {"Ride", "Rid", "Ri"}}, +}; +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, + 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 } +}; +static boolean windowdata_init = 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. + */ +#if defined(UNIX) +static int do_field_opt = 0; +#else +static int do_field_opt = 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() { #ifdef STATUS_HILITES int i; - for (i = 0; i < MAXBLSTATS; ++i) - tty_status_colors[i] = NO_COLOR; /* no color */ + for (i = 0; i < MAXBLSTATS; ++i) { + tty_status[NOW][i].idx = -1; + tty_status[NOW][i].color = NO_COLOR; /* no color */ + tty_status[NOW][i].attr = ATR_NONE; + tty_status[NOW][i].x = 0; + 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[BEFORE][i] = tty_status[NOW][i]; + } tty_condition_bits = 0L; hpbar_percent = 0, hpbar_color = NO_COLOR; #endif /* STATUS_HILITES */ @@ -3464,6 +3524,8 @@ tty_status_init() genl_status_init(); } +#ifdef STATUS_HILITES + /* * *_status_update() * -- update the value of a status field. @@ -3517,8 +3579,165 @@ tty_status_init() * See doc/window.doc for more details. */ +void +tty_status_update(fldidx, ptr, chg, percent, color, colormasks) +int fldidx, chg UNUSED, percent, color; +genericptr_t ptr; +unsigned long *colormasks; +{ + long *condptr = (long *) ptr; + char *text = (char *) ptr; + boolean do_color = FALSE; + boolean force_update = FALSE; + +#ifdef TEXTCOLOR + do_color = TRUE; +#endif + + if (fldidx != BL_FLUSH && !status_activefields[fldidx]) + return; + + switch (fldidx) { + case BL_FLUSH: + force_update = TRUE; + break; + 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; + break; + default: + tty_status[NOW][fldidx].idx = fldidx; + Sprintf(status_vals[fldidx], + status_fieldfmt[fldidx] ? + status_fieldfmt[fldidx] : "%s", text); + tty_status[NOW][fldidx].color = do_color ? (color & 0x00FF) + : NO_COLOR; + tty_status[NOW][fldidx].attr = (color & 0xFF00) >> 8; + 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 + && status_vals[fldidx][0] == ' ') { + status_vals[fldidx][0] = '\0'; + 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) { + char *lastchar = eos(status_vals[fldidx]); + lastchar--; + while (lastchar && *lastchar == ' ' + && lastchar >= status_vals[fldidx]) { + *lastchar-- = '\0'; + tty_status[NOW][fldidx].lth--; + } + } + + /* 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 (check_fields(force_update)) + render_status(); + return; +} + +/* + * 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. + */ +boolean +check_fields(forcefields) +boolean forcefields; +{ + int c, i, row, col; + boolean valid = TRUE, matchprev = FALSE, update_right; + + if (!windowdata_init && !check_windowdata()) + return FALSE; + + for (row = 0; row < 2; ++row) { + col = 1; + update_right = FALSE; + for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { + int idx = fieldorder[row][i]; + + if (!status_activefields[idx]) + continue; + + if (!tty_status[NOW][idx].valid) + valid = FALSE; + + tty_status[NOW][idx].y = row; + tty_status[NOW][idx].x = col; + + /* evaluate */ + 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 */ + + 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; + } + + /* + * With STATUS_HILITES, it is possible that the color + * needs to change even if the text is the same, so + * we flag that here by setting .redraw. + * 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. + */ + 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; + col += tty_status[NOW][idx].lth; + } + } + return valid; +} -/* new approach through status_update() only */ #define Begin_Attr(m) \ if (m) { \ if ((m) & HL_BOLD) \ @@ -3547,235 +3766,272 @@ tty_status_init() term_end_attr(ATR_BOLD); \ } -#ifdef STATUS_HILITES - -#ifdef TEXTCOLOR -#define MaybeDisplayCond(bm,txt) \ - if (tty_condition_bits & (bm)) { \ - putstr(WIN_STATUS, 0, " "); \ - if (iflags.hilite_delta) { \ - attrmask = condattr(bm, colormasks); \ - Begin_Attr(attrmask); \ - if ((coloridx = condcolor(bm, colormasks)) != NO_COLOR) \ - term_start_color(coloridx); \ - } \ - putstr(WIN_STATUS, 0, txt); \ - if (iflags.hilite_delta) { \ - if (coloridx != NO_COLOR) \ - term_end_color(); \ - End_Attr(attrmask); \ - } \ - } -#else -#define MaybeDisplayCond(bm,txt) \ - if (tty_condition_bits & (bm)) { \ - putstr(WIN_STATUS, 0, " "); \ - if (iflags.hilite_delta) { \ - attrmask = condattr(bm, colormasks); \ - Begin_Attr(attrmask); \ - } \ - putstr(WIN_STATUS, 0, txt); \ - if (iflags.hilite_delta) { \ - End_Attr(attrmask); \ - } \ - } -#endif - void -tty_status_update(fldidx, ptr, chg, percent, color, colormasks) -int fldidx, chg UNUSED, percent UNUSED, color; -genericptr_t ptr; -unsigned long *colormasks; +render_status(VOID_ARGS) { - long *condptr = (long *) ptr; - int i, attrmask = 0; -#ifdef TEXTCOLOR - int coloridx = NO_COLOR; -#endif - char *text = (char *) ptr; - static boolean oncearound = FALSE; /* prevent premature partial display */ - 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 } - }; - int attridx = 0; + long mask = 0L; + int i, c, row, shrinklvl = 0, attrmask = 0; + struct WinDesc *cw = 0; + boolean do_color = FALSE, fit = FALSE; + struct tty_status_fields *nullfield = (struct tty_status_fields *)0; - if (fldidx != BL_FLUSH) { - if (!status_activefields[fldidx]) +#ifdef TEXTCOLOR + do_color = TRUE; +#endif + + if (WIN_STATUS == WIN_ERR + || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) { + paniclog("render_status", "WIN_ERR on status window."); return; - switch (fldidx) { - case BL_CONDITION: - tty_condition_bits = *condptr; - oncearound = TRUE; - break; - default: - Sprintf(status_vals[fldidx], - (fldidx == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" : - status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s", - text); -#ifdef TEXTCOLOR - tty_status_colors[fldidx] = color; -#else - tty_status_colors[fldidx] = NO_COLOR; -#endif - if (iflags.wc2_hitpointbar && fldidx == BL_HP) { - hpbar_percent = percent; -#ifdef TEXTCOLOR - hpbar_color = color; -#else - hpbar_color = NO_COLOR; -#endif - } - break; - } } - if (!oncearound) return; + for (row = 0; row < 2; ++row) { + curs(WIN_STATUS, 1, row); + for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { + int fldidx = fieldorder[row][i]; - curs(WIN_STATUS, 1, 0); - for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) { - int fldidx1 = fieldorder[0][i]; - if (status_activefields[fldidx1]) { - if (fldidx1 != BL_TITLE || !iflags.wc2_hitpointbar) { -#ifdef TEXTCOLOR - coloridx = tty_status_colors[fldidx1] & 0x00FF; -#endif - attridx = (tty_status_colors[fldidx1] & 0xFF00) >> 8; - text = status_vals[fldidx1]; - if (iflags.hilite_delta) { - if (*text == ' ') { - putstr(WIN_STATUS, 0, " "); - text++; + if (status_activefields[fldidx]) { + int coloridx = tty_status[NOW][fldidx].color; + int attridx = tty_status[NOW][fldidx].attr; + int x = tty_status[NOW][fldidx].x; + int y = row; + char *text = status_vals[fldidx]; + boolean hitpointbar = (fldidx == BL_TITLE && iflags.wc2_hitpointbar); + + if (do_field_opt && !tty_status[NOW][fldidx].redraw) + continue; + + /* + * Ignore zero length fields. check_fields() didn't count + * them in either. + */ + if (!tty_status[NOW][fldidx].lth && fldidx != BL_CONDITION) + continue; + + if (fldidx == BL_CONDITION) { + /* + * +-----------------+ + * | 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) { + tty_putstatusfield(nullfield, " ", x++, y); + if (iflags.hilite_delta) { + attrmask = condattr(mask, tty_colormasks); + Begin_Attr(attrmask); + if (do_color) { + coloridx = condcolor(mask, tty_colormasks); + if (coloridx != NO_COLOR) + term_start_color(coloridx); + } + } + tty_putstatusfield(nullfield, + conditions[c].text[shrinklvl], x, y); + x += (int) strlen(conditions[c].text[shrinklvl]); + if (iflags.hilite_delta) { + if (do_color && coloridx != NO_COLOR) + term_end_color(); + End_Attr(attrmask); + } + } } - /* multiple attributes can be in effect concurrently */ - Begin_Attr(attridx); -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR && coloridx != CLR_MAX) - term_start_color(coloridx); -#endif - } + tty_curs(WIN_STATUS, x, y); + cl_end(); + } else if (fldidx == BL_GOLD) { + char buf[BUFSZ]; + /* + * +-----------+ + * | Gold | + * +-----------+ + */ + /* decode_mixed() due to GOLD glyph */ + tty_putstatusfield(nullfield, + decode_mixed(buf, text), x, y); + } else if (hitpointbar) { + /* + * +-------------------------+ + * | Title with Hitpoint Bar | + * +-------------------------+ + */ + /* hitpointbar using hp percent calculation */ + int bar_pos, bar_len; + char *bar2 = (char *)0; + char bar[MAXCO], savedch; + boolean twoparts = FALSE; - putstr(WIN_STATUS, 0, text); - - if (iflags.hilite_delta) { -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR) - term_end_color(); -#endif - End_Attr(attridx); - } - } else { - /* hitpointbar using hp percent calculation */ - int bar_pos, bar_len; - char *bar2 = (char *)0; - char bar[MAXCO], savedch; - boolean twoparts = FALSE; - - text = status_vals[fldidx1]; - bar_len = strlen(text); - if (bar_len < MAXCO-1) { - Strcpy(bar, text); - 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; - if (bar_pos > 0 && bar_pos < bar_len) { - twoparts = TRUE; - bar2 = &bar[bar_pos]; - savedch = *bar2; - *bar2 = '\0'; + bar_len = strlen(text); + if (bar_len < MAXCO-1) { + Strcpy(bar, text); + 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; + if (bar_pos > 0 && bar_pos < bar_len) { + twoparts = TRUE; + bar2 = &bar[bar_pos]; + savedch = *bar2; + *bar2 = '\0'; + } } - } - if (iflags.hilite_delta && iflags.wc2_hitpointbar) { - putstr(WIN_STATUS, 0, "["); -#ifdef TEXTCOLOR - coloridx = hpbar_color & 0x00FF; - /* attridx = (hpbar_color & 0xFF00) >> 8; */ - if (coloridx != NO_COLOR) - term_start_color(coloridx); -#endif - term_start_attr(ATR_INVERSE); - putstr(WIN_STATUS, 0, bar); - term_end_attr(ATR_INVERSE); -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR) - term_end_color(); -#endif - if (twoparts) { - *bar2 = savedch; - putstr(WIN_STATUS, 0, bar2); - } - putstr(WIN_STATUS, 0, "]"); - } else - putstr(WIN_STATUS, 0, text); - } - } - } - cl_end(); - curs(WIN_STATUS, 1, 1); - for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) { - int fldidx2 = fieldorder[1][i]; - - if (status_activefields[fldidx2]) { - if (fldidx2 != BL_CONDITION) { -#ifdef TEXTCOLOR - coloridx = tty_status_colors[fldidx2] & 0x00FF; -#endif - attridx = (tty_status_colors[fldidx2] & 0xFF00) >> 8; - text = status_vals[fldidx2]; - if (iflags.hilite_delta) { - if (*text == ' ') { - putstr(WIN_STATUS, 0, " "); - text++; - } - /* multiple attributes can be in effect concurrently */ - Begin_Attr(attridx); -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR && coloridx != CLR_MAX) - term_start_color(coloridx); -#endif - } - - if (fldidx2 == BL_GOLD) { - /* putmixed() due to GOLD glyph */ - putmixed(WIN_STATUS, 0, text); + if (iflags.hilite_delta) { + tty_putstatusfield(nullfield, "[", x++, y); + if (do_color && hpbar_color != NO_COLOR) + term_start_color(hpbar_color); + term_start_attr(ATR_INVERSE); + tty_putstatusfield(nullfield, bar, x, y); + x += (int) strlen(bar); + term_end_attr(ATR_INVERSE); + if (do_color && hpbar_color != NO_COLOR) + term_end_color(); + if (twoparts) { + *bar2 = savedch; + tty_putstatusfield(nullfield, bar2, x, y); + x += (int) strlen(bar2); + tty_curs(WIN_STATUS, x, y); + } + tty_putstatusfield(nullfield, "]", x++, y); + } else { + tty_putstatusfield(&tty_status[NOW][fldidx], + (char *)0, x, y); + } } else { - putstr(WIN_STATUS, 0, text); + /* + * +-----------------------------+ + * | Everything else that is not | + * | in a special case above | + * +-----------------------------+ + */ + if (iflags.hilite_delta) { + if (*text == ' ') { + tty_putstatusfield(nullfield, " ", x++, y); + text++; + } + /* multiple attributes can be in effect concurrently */ + Begin_Attr(attridx); + if (do_color && coloridx != NO_COLOR + && coloridx != CLR_MAX) + term_start_color(coloridx); + } + tty_putstatusfield(&tty_status[NOW][fldidx], + text, x, y); + if (iflags.hilite_delta) { + if (do_color && coloridx != NO_COLOR) + term_end_color(); + End_Attr(attridx); + } } - - if (iflags.hilite_delta) { -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR) - term_end_color(); -#endif - End_Attr(attridx); - } - } else { - MaybeDisplayCond(BL_MASK_STONE, "Stone"); - MaybeDisplayCond(BL_MASK_SLIME, "Slime"); - MaybeDisplayCond(BL_MASK_STRNGL, "Strngl"); - MaybeDisplayCond(BL_MASK_FOODPOIS, "FoodPois"); - MaybeDisplayCond(BL_MASK_TERMILL, "TermIll"); - MaybeDisplayCond(BL_MASK_BLIND, "Blind"); - MaybeDisplayCond(BL_MASK_DEAF, "Deaf"); - MaybeDisplayCond(BL_MASK_STUN, "Stun"); - MaybeDisplayCond(BL_MASK_CONF, "Conf"); - MaybeDisplayCond(BL_MASK_HALLU, "Hallu"); - MaybeDisplayCond(BL_MASK_LEV, "Lev"); - MaybeDisplayCond(BL_MASK_FLY, "Fly"); - MaybeDisplayCond(BL_MASK_RIDE, "Ride"); + /* reset .redraw and .dirty now that they've been rendered */ + tty_status[NOW][fldidx].dirty = FALSE; + tty_status[NOW][fldidx].redraw = FALSE; + /* + * Make a copy of the entire tty_status struct for comparison + * of current and previous. + */ + tty_status[BEFORE][fldidx] = tty_status[NOW][fldidx]; /* copy struct */ } - } + } } - cl_end(); 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 @@ -3828,8 +4084,10 @@ unsigned long *bmarray; } return attr; } + #endif /* STATUS_HILITES */ #endif /* TTY_GRAPHICS */ /*wintty.c*/ +