diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ca45e5826..413e7f0f0 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -8,6 +8,9 @@ shift to the next major release. General Fixes and Modified Features ----------------------------------- last line of config file wasn't being heeded if it had no newline +list MSGTYPE values shows empty strings as reported in H7140 +Killing Vlad while he was in bat/fog cloud/wolf form gave poorly + worded feedback when he reverted to vampire form Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository @@ -16,7 +19,24 @@ Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository Platform- and/or Interface-Specific Fixes ----------------------------------------- +windows-gui: In nethackw, there could be conflicts between menu accelerators + and an extra choice accelerator to fix H7132. windows-tty: Specify both width and height when creating font for width testing +windows-tty: To counter lag problems that were occuring with the Win32 console + port, implement a console back buffer to reduce the number of calls made to + WriteConsoleOutputXXX +windows-tty: Additional changes to xputc_core() and early_raw_print() to manage + the cursor position correctly as that is needed to handle raw printing + correctly +windows-tty: Added check for when we might be running off the bottom of the + screen when handling msmsg() +windows-tty: Added runtime checks to keep cursor always within bounds +windows-tty: Fix memory leaks as reported in H5779 +windows-tty: Use nhraykey by default if the players keyboard layout is + non-english as reported in H4216 +windows-tty: We now support changing altkeyhandler in game +windows: Added ntassert() mechanism for Windows based port use +tty: some optimizations for performance and per field rendering Code Cleanup and Reorganization diff --git a/include/extern.h b/include/extern.h index be57ba71d..e4137f239 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 ### */ @@ -1621,6 +1623,7 @@ E int NDECL(tgetch); E int FDECL(ntposkey, (int *, int *, int *)); E void FDECL(set_output_mode, (int)); E void NDECL(synch_cursor); +E void NDECL(nethack_enter_nttty); #endif /* ### o_init.c ### */ @@ -2780,6 +2783,11 @@ E void NDECL(dump_close_log); E void FDECL(dump_redirect, (BOOLEAN_P)); E void FDECL(dump_forward_putstr, (winid, int, const char*, int)); +/* ### winnt.c ### */ +#ifdef WIN32 +E void NDECL(nethack_enter_winnt); +#endif + /* ### wizard.c ### */ E void NDECL(amulet); 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 a30695052..7e6405af6 100644 --- a/include/global.h +++ b/include/global.h @@ -363,4 +363,10 @@ struct savefile_info { dropleveltempsfn = held; held=0; if(dropleveltempsfn)(*dropleveltempsfn)(); #endif +/* Supply nethack_enter macro if not supplied by port */ +#ifndef nethack_enter +#define nethack_enter(argc, argv) ((void) 0) +#endif + + #endif /* GLOBAL_H */ diff --git a/include/ntconf.h b/include/ntconf.h index 534fa6e5d..36a87e2e4 100644 --- a/include/ntconf.h +++ b/include/ntconf.h @@ -203,7 +203,9 @@ extern void NDECL(win32_abort); extern void FDECL(nttty_preference_update, (const char *)); extern void NDECL(toggle_mouse_support); extern void FDECL(map_subkeyvalue, (char *)); -extern void NDECL(load_keyboard_handler); +#if defined(WIN32CON) +extern void FDECL(set_altkeyhandler, (const char *)); +#endif extern void NDECL(raw_clear_screen); #include @@ -246,4 +248,16 @@ extern int FDECL(set_win32_option, (const char *, const char *)); extern int FDECL(alternative_palette, (char *)); #endif +#ifdef NDEBUG +#define ntassert(expression) ((void)0) +#else +extern void FDECL(ntassert_failed, (const char * exp, const char * file, + int line)); + +#define ntassert(expression) (void)((!!(expression)) || \ + (ntassert_failed(#expression, __FILE__, __LINE__), 0)) +#endif + +#define nethack_enter(argc, argv) nethack_enter_winnt() + #endif /* NTCONF_H */ 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 60a6d3bbc..1fbd5cdf9 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) @@ -760,11 +762,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[]; @@ -782,7 +791,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] == '-') { @@ -797,15 +806,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++; @@ -820,7 +834,7 @@ enum earlyarg e_arg; } } early_version_info(insert_into_pastebuf); - return TRUE; + return 2; break; } default: @@ -830,4 +844,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/src/mon.c b/src/mon.c index b088460ec..2ccb585cb 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1522540516 2018/03/31 23:55:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.250 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1526132509 2018/05/12 13:41:49 $ $NHDT-Branch: master $:$NHDT-Revision: 1.252 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1876,9 +1876,13 @@ register struct monst *mtmp; else mtmp->cham = mndx; if (canspotmon(mtmp)) { + const char *whom = mtmp->data->mname; + /* was using a_monnam(mtmp) but that's weird if mtmp is named: "Dracula suddenly transforms and rises as Dracula" */ - pline(upstart(buf), an(mtmp->data->mname)); + if (!type_is_pname(mtmp->data)) + whom = an(whom); + pline(upstart(buf), whom); vamp_rise_msg = TRUE; } newsym(x, y); diff --git a/src/options.c b/src/options.c index b0c125051..e61eaa906 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1510963525 2017/11/18 00:05:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.319 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1526112322 2018/05/12 08:05:22 $ $NHDT-Branch: master $:$NHDT-Revision: 1.323 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -263,7 +263,7 @@ static struct Comp_Opt { DISP_IN_GAME }, { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/ { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/ - { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME }, + { "altkeyhandler", "alternate key handler", 20, SET_IN_GAME }, #ifdef BACKWARD_COMPAT { "boulder", "deprecated (use S_boulder in sym file instead)", 1, SET_IN_GAME }, @@ -2651,9 +2651,8 @@ boolean tinitial, tfrom_file; bad_negation(fullname, FALSE); return FALSE; } else if ((op = string_for_opt(opts, negated)) != 0) { -#ifdef WIN32 - (void) strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5); - load_keyboard_handler(); +#if defined(WIN32CON) + set_altkeyhandler(op); #endif } else return FALSE; @@ -4821,7 +4820,7 @@ boolean setinitial, setfromfile; if (strlen(tmp->pattern) > ln) Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\""); else - Strcat(mtbuf, "\""); + Strcat(strcat(mtbuf, tmp->pattern), "\""); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf, MENU_UNSELECTED); tmp = tmp->next; diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index eea29f8de..c7d275071 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -57,12 +57,14 @@ extern void FDECL(nethack_exit, (int)); extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ extern int redirect_stdout; /* from sys/share/pcsys.c */ extern int GUILaunched; -HANDLE hStdOut; char *NDECL(exename); char default_window_sys[] = "mswin"; +#ifndef WIN32CON +HANDLE hStdOut; boolean NDECL(fakeconsole); void NDECL(freefakeconsole); #endif +#endif #if defined(MSWIN_GRAPHICS) extern void NDECL(mswin_destroy_reg); @@ -93,6 +95,8 @@ char *argv[]; { boolean resuming; + nethack_enter(argc, argv); + sys_early_init(); #ifdef WIN32 Strcpy(default_window_sys, "tty"); @@ -331,9 +335,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 @@ -353,7 +362,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(hackdir, dir); } if (argc > 1) { -#if defined(WIN32) +#if defined(WIN32) && !defined(WIN32CON) int sfd = 0; boolean tmpconsole = FALSE; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); @@ -363,7 +372,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ * may do a prscore(). */ if (!strncmp(argv[1], "-s", 2)) { -#if defined(WIN32) +#if defined(WIN32) && !defined(WIN32CON) #if 0 if (!hStdOut) { @@ -390,7 +399,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ initoptions(); #endif prscore(argc, argv); -#ifdef WIN32 +#if defined(WIN32) && !defined(WIN32CON) if (tmpconsole) { getreturn("to exit"); freefakeconsole(); @@ -416,7 +425,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif nhusage(); -#ifdef WIN32 +#if defined(WIN32) && !defined(WIN32CON) if (tmpconsole) { getreturn("to exit"); freefakeconsole(); @@ -913,7 +922,7 @@ authorize_wizard_mode() #define PATH_SEPARATOR '\\' #endif -#ifdef WIN32 +#if defined(WIN32) && !defined(WIN32CON) static char exenamebuf[PATHLEN]; extern HANDLE hConIn; extern HANDLE hConOut; 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..f7417adbe 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -8,7 +8,8 @@ * Initial Creation M. Allison 1993/01/31 * 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 + * 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 * */ @@ -20,6 +21,34 @@ #include #include "win32api.h" +/* + * 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 (' ') + +#define CONSOLE_UNDEFINED_ATTRIBUTE (0) +#define CONSOLE_UNDEFINED_CHARACTER ('\0') + +typedef struct { + WCHAR character; + WORD attribute; +} cell_t; + +cell_t clear_cell = { CONSOLE_CLEAR_CHARACTER, CONSOLE_CLEAR_ATTRIBUTE }; +cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER, + CONSOLE_UNDEFINED_ATTRIBUTE }; + /* * The following WIN32 Console API routines are used in this file. * @@ -49,19 +78,11 @@ static boolean NDECL(check_font_widths); static void NDECL(set_known_good_console_font); static void NDECL(restore_original_console_font); -/* Win32 Console handles for input and output */ -HANDLE hConIn; -HANDLE hConOut; - /* Win32 Screen buffer,coordinate,console I/O information */ -CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi; COORD ntcoord; INPUT_RECORD ir; /* Support for changing console font if existing glyph widths are too wide */ -boolean console_font_changed; -CONSOLE_FONT_INFOEX original_console_font_info; -UINT original_console_code_page; extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ extern int redirect_stdout; @@ -73,9 +94,8 @@ extern int redirect_stdout; * immediately after it is displayed, yet not bother when started * from the command line. */ -int GUILaunched; +int GUILaunched = FALSE; /* Flag for whether unicode is supported */ -static boolean has_unicode; static boolean init_ttycolor_completed; #ifdef PORT_DEBUG static boolean display_cursor_info = FALSE; @@ -106,14 +126,41 @@ struct console_t { int current_nhcolor; int current_nhattr[ATR_INVERSE+1]; COORD cursor; + HANDLE hConOut; + HANDLE hConIn; + CONSOLE_SCREEN_BUFFER_INFO origcsbi; + int width; + int height; + boolean has_unicode; + int buffer_size; + cell_t * front_buffer; + cell_t * back_buffer; + WCHAR cpMap[256]; + boolean font_changed; + CONSOLE_FONT_INFOEX original_font_info; + UINT original_code_page; } console = { 0, (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), NO_COLOR, {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}, - {0, 0} + {0, 0}, + NULL, + NULL, + { 0 }, + 0, + 0, + FALSE, + 0, + NULL, + NULL, + { 0 }, + FALSE, + { 0 }, + 0 }; + static DWORD ccount, acount; #ifndef CLR_MAX #define CLR_MAX 16 @@ -143,13 +190,94 @@ typedef int(__stdcall *SOURCEAUTHOR)(char **); typedef int(__stdcall *KEYHANDLERNAME)(char **, int); -HANDLE hLibrary; -PROCESS_KEYSTROKE pProcessKeystroke; -NHKBHIT pNHkbhit; -CHECKINPUT pCheckInput; -SOURCEWHERE pSourceWhere; -SOURCEAUTHOR pSourceAuthor; -KEYHANDLERNAME pKeyHandlerName; +typedef struct { + char * name; // name without DLL extension + HANDLE hLibrary; + PROCESS_KEYSTROKE pProcessKeystroke; + NHKBHIT pNHkbhit; + CHECKINPUT pCheckInput; + SOURCEWHERE pSourceWhere; + SOURCEAUTHOR pSourceAuthor; + KEYHANDLERNAME pKeyHandlerName; +} keyboard_handler_t; + +keyboard_handler_t keyboard_handler; + + +/* Console buffer flipping support */ + +static void back_buffer_flip() +{ + cell_t * back = console.back_buffer; + cell_t * front = console.front_buffer; + COORD pos; + DWORD unused; + + for (pos.Y = 0; pos.Y < console.height; pos.Y++) { + for (pos.X = 0; pos.X < console.width; pos.X++) { + if (back->attribute != front->attribute) { + WriteConsoleOutputAttribute(console.hConOut, &back->attribute, + 1, pos, &unused); + front->attribute = back->attribute; + } + if (back->character != front->character) { + if (console.has_unicode) { + WriteConsoleOutputCharacterW(console.hConOut, + &back->character, 1, pos, &unused); + } else { + char ch = (char)back->character; + WriteConsoleOutputCharacterA(console.hConOut, &ch, 1, pos, + &unused); + } + *front = *back; + } + back++; + front++; + } + } +} + +void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y) +{ + ntassert(x >= 0 && x < console.width); + ntassert(y >= 0 && ((y < console.height) || (y == console.height && + x == 0))); + + cell_t * dst = buffer + console.width * y + x; + cell_t * sentinel = buffer + console.buffer_size; + while (dst != sentinel) + *dst++ = *fill; + + if (iflags.debug.immediateflips && buffer == console.back_buffer) + back_buffer_flip(); +} + +static void buffer_clear_to_end_of_line(cell_t * buffer, int x, int y) +{ + ntassert(x >= 0 && x < console.width); + ntassert(y >= 0 && ((y < console.height) || (y == console.height && + x == 0))); + cell_t * dst = buffer + console.width * y + x; + cell_t *sentinel = buffer + console.width * (y + 1); + + while (dst != sentinel) + *dst++ = clear_cell; + + if (iflags.debug.immediateflips) + back_buffer_flip(); +} + +void buffer_write(cell_t * buffer, cell_t * cell, COORD pos) +{ + ntassert(pos.X >= 0 && pos.X < console.width); + ntassert(pos.Y >= 0 && pos.Y < console.height); + + cell_t * dst = buffer + (console.width * pos.Y) + pos.X; + *dst = *cell; + + if (iflags.debug.immediateflips && buffer == console.back_buffer) + back_buffer_flip(); +} /* * Called after returning from ! or ^Z @@ -157,10 +285,6 @@ KEYHANDLERNAME pKeyHandlerName; void gettty() { - console_font_changed = FALSE; - - check_and_set_font(); - #ifndef TEXTCOLOR int k; #endif @@ -197,19 +321,14 @@ setftty() adjust_palette(); #endif start_screen(); - has_unicode = ((GetVersion() & 0x80000000) == 0); } void tty_startup(wid, hgt) int *wid, *hgt; { - int twid = origcsbi.srWindow.Right - origcsbi.srWindow.Left + 1; - - if (twid > 80) - twid = 80; - *wid = twid; - *hgt = origcsbi.srWindow.Bottom - origcsbi.srWindow.Top + 1; + *wid = console.width; + *hgt = console.height; set_option_mod_status("mouse_support", SET_IN_GAME); } @@ -217,6 +336,7 @@ void tty_number_pad(state) int state; { + // do nothing } void @@ -231,19 +351,9 @@ tty_end_screen() { clear_screen(); really_move_cursor(); - if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { - DWORD ccnt; - COORD newcoord; - - newcoord.X = 0; - newcoord.Y = 0; - FillConsoleOutputAttribute( - hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, - csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); - FillConsoleOutputCharacter( - hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); - } - FlushConsoleInputBuffer(hConIn); + buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); + back_buffer_flip(); + FlushConsoleInputBuffer(console.hConIn); } static BOOL @@ -262,7 +372,7 @@ DWORD ctrltype; hangup(0); #endif #if defined(SAFERHANGUP) - CloseHandle(hConIn); /* trigger WAIT_FAILED */ + CloseHandle(console.hConIn); /* trigger WAIT_FAILED */ return TRUE; #endif default: @@ -270,76 +380,26 @@ DWORD ctrltype; } } -/* called by init_tty in wintty.c for WIN32 port only */ +/* called by pcmain() and process_options() */ void nttty_open(mode) -int mode; +int mode; // unused { - HANDLE hStdOut; DWORD cmode; - long mask; - GUILaunched = 0; - - try : - /* The following lines of code were suggested by - * Bob Landau of Microsoft WIN32 Developer support, - * as the only current means of determining whether - * we were launched from the command prompt, or from - * the NT program manager. M. Allison - */ - hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - - if (hStdOut) { - GetConsoleScreenBufferInfo(hStdOut, &origcsbi); - } else if (mode) { - HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); - - if (!hStdOut && !hStdIn) { - /* Bool rval; */ - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - /* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */ - freopen("CON", "w", stdout); - freopen("CON", "r", stdin); - } - mode = 0; - goto try; - } else { - return; - } - - /* Obtain handles for the standard Console I/O devices */ - hConIn = GetStdHandle(STD_INPUT_HANDLE); - hConOut = GetStdHandle(STD_OUTPUT_HANDLE); - - load_keyboard_handler(); /* Initialize the function pointer that points to - * the kbhit() equivalent, in this TTY case nttty_kbhit() - */ + * the kbhit() equivalent, in this TTY case nttty_kbhit() + */ nt_kbhit = nttty_kbhit; - GetConsoleMode(hConIn, &cmode); -#ifdef NO_MOUSE_ALLOWED - mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT - | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT; -#else - mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT - | ENABLE_WINDOW_INPUT; -#endif - /* Turn OFF the settings specified in the mask */ - cmode &= ~mask; -#ifndef NO_MOUSE_ALLOWED - cmode |= ENABLE_MOUSE_INPUT; -#endif - SetConsoleMode(hConIn, cmode); if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) { /* Unable to set control handler */ cmode = 0; /* just to have a statement to break on for debugger */ } - get_scr_size(); - console.cursor.X = console.cursor.Y = 0; + + LI = console.height; + CO = console.width; + really_move_cursor(); } @@ -350,7 +410,8 @@ boolean *valid; boolean numberpad; int portdebug; { - int ch = pProcessKeystroke(hConIn, ir, valid, numberpad, portdebug); + int ch = keyboard_handler.pProcessKeystroke( + console.hConIn, ir, valid, numberpad, portdebug); /* check for override */ if (ch && ch < MAX_OVERRIDES && key_overrides[ch]) ch = key_overrides[ch]; @@ -360,33 +421,7 @@ int portdebug; int 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); - } + return keyboard_handler.pNHkbhit(console.hConIn, &ir); } int @@ -398,8 +433,8 @@ tgetch() really_move_cursor(); return (program_state.done_hup) ? '\033' - : pCheckInput(hConIn, &ir, &count, iflags.num_pad, 0, &mod, - &cc); + : keyboard_handler.pCheckInput( + console.hConIn, &ir, &count, iflags.num_pad, 0, &mod, &cc); } int @@ -412,7 +447,8 @@ int *x, *y, *mod; really_move_cursor(); ch = (program_state.done_hup) ? '\033' - : pCheckInput(hConIn, &ir, &count, iflags.num_pad, 1, mod, &cc); + : keyboard_handler.pCheckInput( + console.hConIn, &ir, &count, iflags.num_pad, 1, mod, &cc); if (!ch) { *x = cc.x; *y = cc.y; @@ -420,6 +456,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() { @@ -431,15 +476,16 @@ really_move_cursor() oldtitle[39] = '\0'; } Sprintf(newtitle, "%-55s tty=(%02d,%02d) nttty=(%02d,%02d)", oldtitle, - ttyDisplay->curx, ttyDisplay->cury, console.cursor.X, console.cursor.Y); + ttyDisplay->curx, ttyDisplay->cury, + console.cursor.X, console.cursor.Y); (void) SetConsoleTitle(newtitle); } #endif - if (ttyDisplay) { - console.cursor.X = ttyDisplay->curx; - console.cursor.Y = ttyDisplay->cury; - } - SetConsoleCursorPosition(hConOut, console.cursor); + if (ttyDisplay) + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); + + back_buffer_flip(); + SetConsoleCursorPosition(console.hConOut, console.cursor); } void @@ -448,26 +494,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); } @@ -478,10 +523,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) @@ -497,16 +540,27 @@ 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; + 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; 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); @@ -516,18 +570,24 @@ char ch; if (console.current_nhattr[ATR_BOLD]) console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; - WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount); - 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.attribute = console.attr; + cell.character = (console.has_unicode ? console.cpMap[ch] : ch); + + buffer_write(console.back_buffer, &cell, console.cursor); + + if (console.cursor.X == console.width - 1) { + if (console.cursor.Y < console.height - 1) { + console.cursor.X = 1; + console.cursor.Y++; + } } else { - WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount); + console.cursor.X++; } - console.cursor.X++; } + + ntassert(console.cursor.X >= 0 && console.cursor.X < console.width); + ntassert(console.cursor.Y >= 0 && console.cursor.Y < console.height); } /* @@ -578,8 +638,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) ? @@ -587,41 +646,28 @@ 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); - if (has_unicode) - WriteConsoleOutputCharacterW(hConOut, &cp437[ch], 1, console.cursor, &ccount); - else - WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount); + cell_t cell; + + cell.attribute = console.attr; + cell.character = (console.has_unicode ? cp437[ch] : ch); + + buffer_write(console.back_buffer, &cell, console.cursor); } 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); + 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); } void raw_clear_screen() { - if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { - DWORD ccnt; - COORD newcoord; - - newcoord.X = 0; - newcoord.Y = 0; - FillConsoleOutputAttribute( - hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, - csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); - FillConsoleOutputCharacter( - hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt); - } + buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); } void @@ -634,35 +680,22 @@ 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'); } void cl_eos() { - int cy = ttyDisplay->cury + 1; - if (GetConsoleScreenBufferInfo(hConOut, &csbi)) { - 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); - FillConsoleOutputCharacter(hConOut, ' ', - csbi.dwSize.X * csbi.dwSize.Y - cy, - newcoord, &ccnt); - } + buffer_fill_to_end(console.back_buffer, &clear_cell, ttyDisplay->curx, + ttyDisplay->cury); tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury); } @@ -684,6 +717,7 @@ tty_delay_output() int k; goal = 50 + clock(); + back_buffer_flip(); while (goal > clock()) { k = junk; /* Do nothing */ } @@ -856,12 +890,12 @@ void toggle_mouse_support() { DWORD cmode; - GetConsoleMode(hConIn, &cmode); + GetConsoleMode(console.hConIn, &cmode); if (iflags.wc_mouse_support) cmode |= ENABLE_MOUSE_INPUT; else cmode &= ~ENABLE_MOUSE_INPUT; - SetConsoleMode(hConIn, cmode); + SetConsoleMode(console.hConIn, cmode); } #endif @@ -890,7 +924,7 @@ win32con_debug_keystrokes() xputs("\n"); while (!valid || ch != 27) { nocmov(ttyDisplay->curx, ttyDisplay->cury); - ReadConsoleInput(hConIn, &ir, 1, &count); + ReadConsoleInput(console.hConIn, &ir, 1, &count); if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown) ch = process_keystroke(&ir, &valid, iflags.num_pad, 1); } @@ -901,20 +935,23 @@ win32con_handler_info() { char *buf; int ci; - if (!pSourceAuthor && !pSourceWhere) + if (!keyboard_handler.pSourceAuthor && !keyboard_handler.pSourceWhere) pline("Keyboard handler source info and author unavailable."); else { - if (pKeyHandlerName && pKeyHandlerName(&buf, 1)) { + if (keyboard_handler.pKeyHandlerName && + keyboard_handler.pKeyHandlerName(&buf, 1)) { xputs("\n"); xputs("Keystroke handler loaded: \n "); xputs(buf); } - if (pSourceAuthor && pSourceAuthor(&buf)) { + if (keyboard_handler.pSourceAuthor && + keyboard_handler.pSourceAuthor(&buf)) { xputs("\n"); xputs("Keystroke handler Author: \n "); xputs(buf); } - if (pSourceWhere && pSourceWhere(&buf)) { + if (keyboard_handler.pSourceWhere && + keyboard_handler.pSourceWhere(&buf)) { xputs("\n"); xputs("Keystroke handler source code available at:\n "); xputs(buf); @@ -966,86 +1003,79 @@ register char *op; key_overrides[idx] = val; } -void -load_keyboard_handler() +void unload_keyboard_handler() { - char suffx[] = ".dll"; - char *truncspot; -#define MAX_DLLNAME 25 - char kh[MAX_ALTKEYHANDLER]; - if (iflags.altkeyhandler[0]) { - if (hLibrary) { /* already one loaded apparently */ - FreeLibrary(hLibrary); - hLibrary = (HANDLE) 0; - pNHkbhit = (NHKBHIT) 0; - pCheckInput = (CHECKINPUT) 0; - pSourceWhere = (SOURCEWHERE) 0; - pSourceAuthor = (SOURCEAUTHOR) 0; - pKeyHandlerName = (KEYHANDLERNAME) 0; - pProcessKeystroke = (PROCESS_KEYSTROKE) 0; - } - if ((truncspot = strstri(iflags.altkeyhandler, suffx)) != 0) - *truncspot = '\0'; - (void) strncpy(kh, iflags.altkeyhandler, - (MAX_ALTKEYHANDLER - sizeof suffx) - 1); - kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0'; - Strcat(kh, suffx); - Strcpy(iflags.altkeyhandler, kh); - hLibrary = LoadLibrary(kh); - if (hLibrary) { - pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress( - hLibrary, TEXT("ProcessKeystroke")); - pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit")); - pCheckInput = - (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput")); - pSourceWhere = - (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere")); - pSourceAuthor = - (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor")); - pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress( - hLibrary, TEXT("KeyHandlerName")); - } + ntassert(keyboard_handler.hLibrary != NULL); + + FreeLibrary(keyboard_handler.hLibrary); + memset(&keyboard_handler, 0, sizeof(keyboard_handler_t)); +} + +boolean +load_keyboard_handler(const char * inName) +{ + char path[MAX_ALTKEYHANDLER + 4]; + strcpy(path, inName); + strcat(path, ".dll"); + + HANDLE hLibrary = LoadLibrary(path); + + if (hLibrary == NULL) + return FALSE; + + PROCESS_KEYSTROKE pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress( + hLibrary, TEXT("ProcessKeystroke")); + NHKBHIT pNHkbhit = (NHKBHIT) GetProcAddress( + hLibrary, TEXT("NHkbhit")); + CHECKINPUT pCheckInput = + (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput")); + + if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) + { + return FALSE; + } else { + if (keyboard_handler.hLibrary != NULL) + unload_keyboard_handler(); + + keyboard_handler.hLibrary = hLibrary; + + keyboard_handler.pProcessKeystroke = pProcessKeystroke; + keyboard_handler.pNHkbhit = pNHkbhit; + keyboard_handler.pCheckInput = pCheckInput; + + keyboard_handler.pSourceWhere = + (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere")); + keyboard_handler.pSourceAuthor = + (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor")); + keyboard_handler.pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress( + hLibrary, TEXT("KeyHandlerName")); } - if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) { - if (hLibrary) { - FreeLibrary(hLibrary); - hLibrary = (HANDLE) 0; - pNHkbhit = (NHKBHIT) 0; - pCheckInput = (CHECKINPUT) 0; - pSourceWhere = (SOURCEWHERE) 0; - pSourceAuthor = (SOURCEAUTHOR) 0; - pKeyHandlerName = (KEYHANDLERNAME) 0; - pProcessKeystroke = (PROCESS_KEYSTROKE) 0; - } - (void) strncpy(kh, "nhdefkey.dll", - (MAX_ALTKEYHANDLER - sizeof suffx) - 1); - kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0'; - Strcpy(iflags.altkeyhandler, kh); - hLibrary = LoadLibrary(kh); - if (hLibrary) { - pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress( - hLibrary, TEXT("ProcessKeystroke")); - pCheckInput = - (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput")); - pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit")); - pSourceWhere = - (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere")); - pSourceAuthor = - (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor")); - pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress( - hLibrary, TEXT("KeyHandlerName")); - } + + return TRUE; +} + +void set_altkeyhandler(const char * inName) +{ + if (strlen(inName) >= MAX_ALTKEYHANDLER) { + config_error_add("altkeyhandler name '%s' is too long", inName); + return; } - if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) { - if (!hLibrary) - raw_printf("\nNetHack was unable to load keystroke handler.\n"); - else { - FreeLibrary(hLibrary); - hLibrary = (HANDLE) 0; - raw_printf("\nNetHack keystroke handler is invalid.\n"); - } - exit(EXIT_FAILURE); + + char name[MAX_ALTKEYHANDLER]; + strcpy(name, inName); + + /* We support caller mistakenly giving name with '.dll' extension */ + char * ext = strchr(name, '.'); + if (ext != NULL) *ext = '\0'; + + if (load_keyboard_handler(name)) + strcpy(iflags.altkeyhandler, name); + else { + config_error_add("unable to load altkeyhandler '%s'", name); + return; } + + return; } /* this is used as a printf() replacement when the window @@ -1064,6 +1094,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); @@ -1521,7 +1562,7 @@ check_font_widths() { CONSOLE_FONT_INFOEX console_font_info; console_font_info.cbSize = sizeof(console_font_info); - BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE, + BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE, &console_font_info); /* get console window and DC @@ -1622,12 +1663,12 @@ set_known_good_console_font() { CONSOLE_FONT_INFOEX console_font_info; console_font_info.cbSize = sizeof(console_font_info); - BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE, + BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE, &console_font_info); - console_font_changed = TRUE; - original_console_font_info = console_font_info; - original_console_code_page = GetConsoleOutputCP(); + console.font_changed = TRUE; + console.original_font_info = console_font_info; + console.original_code_page = GetConsoleOutputCP(); wcscpy_s(console_font_info.FaceName, sizeof(console_font_info.FaceName) @@ -1635,12 +1676,10 @@ set_known_good_console_font() L"Consolas"); success = SetConsoleOutputCP(437); - if (!success) - raw_print("Unable to set console code page to 437\n"); + ntassert(success); - success = SetCurrentConsoleFontEx(hConOut, FALSE, &console_font_info); - if (!success) - raw_print("Unable to set console font to Consolas\n"); + success = SetCurrentConsoleFontEx(console.hConOut, FALSE, &console_font_info); + ntassert(success); } /* restore_original_console_font will restore the console font and code page @@ -1649,19 +1688,228 @@ set_known_good_console_font() void restore_original_console_font() { - if (console_font_changed) { + if (console.font_changed) { BOOL success; raw_print("Restoring original font and code page\n"); - success = SetConsoleOutputCP(original_console_code_page); + success = SetConsoleOutputCP(console.original_code_page); if (!success) raw_print("Unable to restore original code page\n"); - success = SetCurrentConsoleFontEx(hConOut, FALSE, - &original_console_font_info); + success = SetCurrentConsoleFontEx(console.hConOut, FALSE, + &console.original_font_info); if (!success) raw_print("Unable to restore original font\n"); - console_font_changed = FALSE; + console.font_changed = FALSE; + } +} + +/* set_cp_map() creates a mapping of every possible character of a code + * page to its corresponding WCHAR. This is necessary due to the high + * cost of making calls to MultiByteToWideChar() for every character we + * wish to print to the console. + */ + +void set_cp_map() +{ + if (console.has_unicode) { + UINT codePage = GetConsoleOutputCP(); + + for (int i = 0; i < 256; i++) { + char c = (char)i; + int count = MultiByteToWideChar(codePage, 0, &c, 1, + &console.cpMap[i], 1); + ntassert(count == 1); + } + } +} + +/* early_raw_print() is used during early game intialization prior to the + * setting up of the windowing system. This allows early errors and panics + * to have there messages displayed. + * + * early_raw_print() eventually gets replaced by tty_raw_print(). + * + */ + +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': + if (console.cursor.Y < console.height - 1) + console.cursor.Y++; + /* fall through */ + case '\r': + console.cursor.X = 1; + break; + case '\b': + 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); + 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); + +} + +/* nethack_enter_nttty() is the first thing that is called from main. + * + * We initialize all console state to support rendering to the console + * through out flipping support at this time. This allows us to support + * raw_print prior to our returning. + * + * During this early initialization, we also determine the width and + * height of the console that will be used. This width and height will + * not later change. + * + * We also check and set the console font to a font that we know will work + * well with nethack. + * + * The intent of this early initialization is to get all state that is + * not dependent upon game options initialized allowing us to simplify + * any additional initialization that might be needed when we are actually + * asked to open. + * + * Other then the call below which clears the entire console buffer, no + * other code outputs directly to the console other then the code that + * handles flipping the back buffer. + * + */ + +void nethack_enter_nttty() +{ + /* set up state needed by early_raw_print() */ + windowprocs.win_raw_print = early_raw_print; + + console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); + ntassert(console.hConOut != NULL); // NOTE: this assert will not print + + GetConsoleScreenBufferInfo(console.hConOut, &console.origcsbi); + + /* Testing of widths != COLNO has not turned up any problems. Need + * to do a bit more testing and then we are likely to enable having + * console width match window width. + */ +#if 0 + console.width = console.origcsbi.srWindow.Right - + console.origcsbi.srWindow.Left + 1; + console.Width = max(console.Width, COLNO); +#else + console.width = COLNO; +#endif + + console.height = console.origcsbi.srWindow.Bottom - + console.origcsbi.srWindow.Top + 1; + console.height = max(console.height, ROWNO + 3); + + 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); + + FillConsoleOutputCharacter( + 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 work */ + + console.hConIn = GetStdHandle(STD_INPUT_HANDLE); + ntassert(console.hConIn != NULL); + + /* grow the size of the console buffer if it is not wide enough */ + if (console.origcsbi.dwSize.X < console.width) { + COORD size = { + size.Y = console.origcsbi.dwSize.Y, + size.X = console.width + }; + + SetConsoleScreenBufferSize(console.hConOut, size); + } + + /* setup front and back buffers */ + int buffer_size_bytes = sizeof(cell_t) * console.buffer_size; + + console.front_buffer = (cell_t *)malloc(buffer_size_bytes); + buffer_fill_to_end(console.front_buffer, &undefined_cell, 0, 0); + + console.back_buffer = (cell_t *)malloc(buffer_size_bytes); + buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); + + /* determine whether OS version has unicode support */ + console.has_unicode = ((GetVersion() & 0x80000000) == 0); + + /* check the font before we capture the code page map */ + check_and_set_font(); + set_cp_map(); + + /* Set console mode */ + DWORD cmode, mask; + GetConsoleMode(console.hConIn, &cmode); +#ifdef NO_MOUSE_ALLOWED + mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT + | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT; +#else + mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT + | ENABLE_WINDOW_INPUT; +#endif + /* Turn OFF the settings specified in the mask */ + cmode &= ~mask; +#ifndef NO_MOUSE_ALLOWED + cmode |= ENABLE_MOUSE_INPUT; +#endif + SetConsoleMode(console.hConIn, cmode); + + /* load default keyboard handler */ + HKL keyboard_layout = GetKeyboardLayout(0); + DWORD primary_language = (UINT_PTR) keyboard_layout & 0x3f; + + if (primary_language == LANG_ENGLISH) { + if (!load_keyboard_handler("nhdefkey")) + error("Unable to load nhdefkey.dll"); + } else { + if (!load_keyboard_handler("nhraykey")) + error("Unable to load nhraykey.dll"); } } diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index 712164dc1..ef161ae9b 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -136,12 +136,6 @@ register char *op; return; } -void -load_keyboard_handler() -{ - return; -} - /* this is used as a printf() replacement when the window * system isn't initialized yet */ @@ -176,4 +170,15 @@ more() return; } +void +nethack_enter_nttty() +{ + return; +} + +void +set_altkeyhandler(const char *inName) +{ + return; +} #endif /* TTYSTUBS */ diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 826a02d40..067ba5abd 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -207,7 +207,7 @@ VA_DECL(const char *, s) /* error() may get called before tty is initialized */ if (iflags.window_inited) end_screen(); - if (!strncmpi(windowprocs.name, "tty", 3)) { + if (windowprocs.name != NULL && !strncmpi(windowprocs.name, "tty", 3)) { buf[0] = '\n'; (void) vsprintf(&buf[1], s, VA_ARGS); Strcat(buf, "\n"); @@ -461,6 +461,31 @@ char *buf; } #endif /* RUNTIME_PORT_ID */ +/* ntassert_failed is called when an ntassert's condition is false */ +void ntassert_failed(const char * exp, const char * file, int line) +{ + char message[128]; + _snprintf(message, sizeof(message), + "NHASSERT(%s) in '%s' at line %d\n", exp, file, line); + + if (IsDebuggerPresent()) { + OutputDebugStringA(message); + DebugBreak(); + } + + // strip off the newline + message[strlen(message) - 1] = '\0'; + error(message); +} + +/* nethack_enter_winnt() is the first thing called from main */ +void nethack_enter_winnt() +{ +#ifdef WIN32CON + nethack_enter_nttty(); +#endif +} + #endif /* WIN32 */ /*winnt.c*/ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 63d1aac8c..b13318cc2 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,17 @@ STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL boolean NDECL(reset_role_filtering); +#ifdef STATUS_HILITES +STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *, int *)); +STATIC_DCL void NDECL(render_status); +STATIC_DCL void FDECL(tty_putstatusfield, (struct tty_status_fields *, + const char *, 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 /* * A string containing all the default commands -- to add to a list @@ -2434,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); @@ -2489,8 +2502,11 @@ const char *str; { register struct WinDesc *cw = 0; register char *ob; + register long i, n0; +#ifndef STATUS_HILITES register const char *nb; - register long i, j, n0; + register long j; +#endif /* Assume there's a real problem if the window is missing -- * probably a panic message @@ -2518,7 +2534,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 +2548,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 +2563,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); @@ -3410,10 +3407,6 @@ int dir; { if (dir != WININIT) return; -#if defined(WIN32CON) - if (!strncmpi(windowprocs.name, "tty", 3)) - nttty_open(0); -#endif return; } @@ -3426,36 +3419,165 @@ char *posbar; video_update_positionbar(posbar); #endif } -#endif +#endif /* POSITIONBAR */ + + +/* + * +------------------+ + * | STATUS CODE | + * +------------------+ + */ + +/* + * ---------------------------------------------------------------- + * tty_status_update + * + * Called from the NetHack core and receives info hand-offs + * from the core, processes them, storing some information + * for rendering to the tty. + * -> make_things_fit() + * -> render_status() + * + * make_things_fit + * + * Called from tty_status_update(). It calls check_fields() + * (described next) to get the required number of columns + * and tries a few different ways to squish a representation + * of the status window values onto the 80 column tty display. + * ->check_fields() + * ->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 * "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 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 */ +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 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, + 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; +static int cond_shrinklvl = 0, cond_width_at_shrink = 0; +static int enclev = 0, enc_shrinklvl = 0; +static int 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(). + * + * 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 +3586,8 @@ tty_status_init() genl_status_init(); } +#ifdef STATUS_HILITES + /* * *_status_update() * -- update the value of a status field. @@ -3517,265 +3641,363 @@ tty_status_init() * See doc/window.doc for more details. */ - -/* new approach through status_update() only */ -#define Begin_Attr(m) \ - if (m) { \ - if ((m) & HL_BOLD) \ - term_start_attr(ATR_BOLD); \ - if ((m) & HL_INVERSE) \ - term_start_attr(ATR_INVERSE); \ - if ((m) & HL_ULINE) \ - term_start_attr(ATR_ULINE); \ - if ((m) & HL_BLINK) \ - term_start_attr(ATR_BLINK); \ - if ((m) & HL_DIM) \ - term_start_attr(ATR_DIM); \ - } - -#define End_Attr(m) \ - if (m) { \ - if ((m) & HL_DIM) \ - term_end_attr(ATR_DIM); \ - if ((m) & HL_BLINK) \ - term_end_attr(ATR_BLINK); \ - if ((m) & HL_ULINE) \ - term_end_attr(ATR_ULINE); \ - if ((m) & HL_INVERSE) \ - term_end_attr(ATR_INVERSE); \ - if ((m) & HL_BOLD) \ - term_end_attr(ATR_BOLD); \ - } - -#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; +int fldidx, chg UNUSED, percent, color; genericptr_t ptr; unsigned long *colormasks; { + int i; 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; + char *lastchar = (char *) 0; + char *fval = (char *) 0; + boolean do_color = FALSE; + boolean force_update = FALSE; - if (fldidx != BL_FLUSH) { - if (!status_activefields[fldidx]) - return; - switch (fldidx) { +#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_condition_bits = *condptr; - oncearound = TRUE; - break; + 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; + truncation_expected = FALSE; + 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; - } + 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; } - if (!oncearound) return; - - 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++; - } - /* multiple attributes can be in effect concurrently */ - Begin_Attr(attridx); -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR && coloridx != CLR_MAX) - term_start_color(coloridx); -#endif - } - - putstr(WIN_STATUS, 0, text); - - if (iflags.hilite_delta) { -#ifdef TEXTCOLOR - if (coloridx != NO_COLOR) - term_end_color(); -#endif - End_Attr(attridx); + /* 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; + } + + /* 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; } - } 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'; + break; + case BL_LEVELDESC: + case BL_HUNGER: + /* The core sends trailing blanks for some fields + Let's suppress the trailing blanks */ + 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 */ + } } - } - 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); - } - } + } + break; } - 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); - } else { - putstr(WIN_STATUS, 0, text); - } - - 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"); - } - } - } - cl_end(); + 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 + * must be updated because they need to change. + * This is now done at an individual field case-by-case level. + */ +boolean +check_fields(forcefields, topsz, bottomsz) +boolean forcefields; +int *topsz, *bottomsz; +{ + int c, i, row, col, trackx, idx; + boolean valid = TRUE, matchprev = FALSE, update_right; + + if (!windowdata_init && !check_windowdata()) + return FALSE; + + for (row = 0; row < 2; ++row) { + col = 1; + trackx = 1; + update_right = FALSE; + for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { + 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; + + 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++; + } + 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 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; + 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 && y < 2) { + 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 @@ -3828,8 +4050,217 @@ unsigned long *bmarray; } return attr; } + +#define Begin_Attr(m) \ + if (m) { \ + if ((m) & HL_BOLD) \ + term_start_attr(ATR_BOLD); \ + if ((m) & HL_INVERSE) \ + term_start_attr(ATR_INVERSE); \ + if ((m) & HL_ULINE) \ + term_start_attr(ATR_ULINE); \ + if ((m) & HL_BLINK) \ + term_start_attr(ATR_BLINK); \ + if ((m) & HL_DIM) \ + term_start_attr(ATR_DIM); \ + } + +#define End_Attr(m) \ + if (m) { \ + if ((m) & HL_DIM) \ + term_end_attr(ATR_DIM); \ + if ((m) & HL_BLINK) \ + term_end_attr(ATR_BLINK); \ + if ((m) & HL_ULINE) \ + term_end_attr(ATR_ULINE); \ + if ((m) & HL_INVERSE) \ + term_end_attr(ATR_INVERSE); \ + if ((m) & HL_BOLD) \ + term_end_attr(ATR_BOLD); \ + } + +void +render_status(VOID_ARGS) +{ + long mask = 0L; + 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; + +#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; + } + + 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]; + + 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 | + * +-----------------+ + */ + 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); + } + } + if (x >= cw->cols && !truncation_expected) + impossible("Unexpected condition placement overflow"); + tty_putstatusfield(nullfield, + 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(); + End_Attr(attrmask); + } + } + } + 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; + + 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) { + 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 { + /* + * +-----------------------------+ + * | 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); + } + } + /* 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 */ + } + } + } + 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; +} + #endif /* STATUS_HILITES */ #endif /* TTY_GRAPHICS */ /*wintty.c*/ + diff --git a/win/win32/mhdlg.c b/win/win32/mhdlg.c index a91eecf20..6bc7d4eb3 100644 --- a/win/win32/mhdlg.c +++ b/win/win32/mhdlg.c @@ -391,10 +391,6 @@ PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case NM_KILLFOCUS: { - char buf[64]; - sprintf(buf, "KILLFOCUS %lx\n", (unsigned long) control); - OutputDebugStringA(buf); - if (data->focus == data->control_race) { data->focus = NULL; ListView_RedrawItems(data->control_race, 0, data->race_count - 1); @@ -406,9 +402,6 @@ PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case NM_SETFOCUS: { - char buf[64]; - sprintf(buf, "SETFOCUS %lx\n", (unsigned long) control); - OutputDebugStringA(buf); data->focus = control; if (control == data->control_race) { @@ -716,15 +709,9 @@ plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) if (lpdis->itemID < 0) return FALSE; - HWND control = GetDlgItem(hWnd, wParam); + HWND control = GetDlgItem(hWnd, (int) wParam); int i = lpdis->itemID; - { - char buf[64]; - sprintf(buf, "DRAW %lx %d\n", (unsigned long)control, i); - OutputDebugStringA(buf); - } - const char * string; boolean ok = TRUE; @@ -774,12 +761,6 @@ plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) client_rt.left + ListView_GetColumnWidth(lpdis->hwndItem, 0), lpdis->rcItem.bottom); DrawFocusRect(lpdis->hDC, &rect); - - { - char buf[64]; - sprintf(buf, "FOCUS %lx %d\n", (unsigned long)control, i); - OutputDebugStringA(buf); - } } } diff --git a/win/win32/mhmain.c b/win/win32/mhmain.c index f64ab7665..6c6bfea7d 100644 --- a/win/win32/mhmain.c +++ b/win/win32/mhmain.c @@ -795,6 +795,7 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) if (!OpenClipboard(hWnd)) { NHMessageBox(hWnd, TEXT("Cannot open clipboard"), MB_OK | MB_ICONERROR); + free(p); return 0; } @@ -803,6 +804,7 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(char)); if (hglbCopy == NULL) { CloseClipboard(); + free(p); return FALSE; }