From 4bc880c22fa0fcf23edc0b6b3404213ddb6dd578 Mon Sep 17 00:00:00 2001 From: infinigon Date: Sat, 6 Feb 2021 23:22:42 +0000 Subject: [PATCH 1/4] Add realtime display to curses status line (based on UnNetHack implementation by bhaak) --- include/botl.h | 6 +- include/config.h | 3 + include/extern.h | 3 + include/flag.h | 4 ++ include/optlist.h | 6 ++ src/botl.c | 62 ++++++++++++++++- src/options.c | 157 ++++++++++++++++++++++++++++++++++++++++++ src/windows.c | 14 ++-- win/curses/cursstat.c | 40 ++++++++--- win/tty/wintty.c | 25 +++---- 10 files changed, 284 insertions(+), 36 deletions(-) diff --git a/include/botl.h b/include/botl.h index 89ca4f2df5..673f7822bf 100644 --- a/include/botl.h +++ b/include/botl.h @@ -39,9 +39,9 @@ enum statusfields { BL_TITLE = 0, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /* 1..6 */ BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, /* 7..12 */ - BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, /* 13..18 */ - BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION, /* 19..22 */ - MAXBLSTATS /* [23] */ + BL_XP, BL_AC, BL_HD, BL_TIME, BL_REALTIME, BL_HUNGER, /* 13..18 */ + BL_HP, BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION, /* 19..23 */ + MAXBLSTATS /* [24] */ }; enum relationships { NO_LTEQGT = -1, diff --git a/include/config.h b/include/config.h index 2abb503bc3..fbba0e8f34 100644 --- a/include/config.h +++ b/include/config.h @@ -568,6 +568,9 @@ typedef unsigned char uchar; /* #define SCORE_ON_BOTL */ /* enable the 'showscore' option to show estimated score on status line */ +#define REALTIME_ON_BOTL /* enable the 'showrealtime' option to + show elapsed time */ + /* FREE_ALL_MEMORY is neither experimental nor inadequately tested, but it isn't necessary for successful operation of the program */ #define FREE_ALL_MEMORY /* free all memory at exit */ diff --git a/include/extern.h b/include/extern.h index 15480b6d44..2cd94944e0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -173,6 +173,9 @@ extern void max_rank_sz(void); #ifdef SCORE_ON_BOTL extern long botl_score(void); #endif +#ifdef REALTIME_ON_BOTL +extern const char *botl_realtime(void); +#endif extern int describe_level(char *); extern void status_initialize(boolean); extern void status_finish(void); diff --git a/include/flag.h b/include/flag.h index 1e75ffe7ed..1d3a197521 100644 --- a/include/flag.h +++ b/include/flag.h @@ -255,6 +255,10 @@ struct instance_flags { uchar bouldersym; /* symbol for boulder display */ char prevmsg_window; /* type of old message window to use */ boolean extmenu; /* extended commands use menu interface */ +#ifdef REALTIME_ON_BOTL + char show_realtime; /* type of time to show in status display */ + char realtime_format; /* format of time shown */ +#endif #ifdef MICRO boolean BIOS; /* use IBM or ST BIOS calls when appropriate */ #endif diff --git a/include/optlist.h b/include/optlist.h index 225ab5d65e..d01e0d6712 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -389,6 +389,12 @@ opt_##a, #else NHOPTB(rawio, 0, opt_in, set_in_config, Off, No, No, No, NoAlias, (boolean *) 0) +#endif +#ifdef REALTIME_ON_BOTL + NHOPTC(realtime, 5, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias, + "show realtime in status line") + NHOPTC(realtime_format, 5, opt_in, set_in_game, No, Yes, No, Yes, NoAlias, + "format of realtime in status line") #endif NHOPTB(rest_on_space, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.rest_on_space) diff --git a/src/botl.c b/src/botl.c index 3905dfda71..d4e099b30c 100644 --- a/src/botl.c +++ b/src/botl.c @@ -409,6 +409,48 @@ botl_score(void) } #endif /* SCORE_ON_BOTL */ +#ifdef REALTIME_ON_BOTL +const char * +botl_realtime(void) +{ + time_t currenttime; + + if (iflags.show_realtime == 'p') { + /* play time */ + currenttime = urealtime.realtime + (getnow() - urealtime.start_timing); + } else if (iflags.show_realtime == 'w') { + /* wallclock time */ + currenttime = getnow() - ubirthday; + } else { + return ""; + } + + static char buf[BUFSZ] = { 0 }; + switch (iflags.realtime_format) { + case 's': + Sprintf(buf, "%ld", currenttime); + break; + + case 'c': + Sprintf(buf, "%ld:%2.2ld", currenttime / 3600, (currenttime % 3600) / 60); + break; + + case 'u': + default: { + long ss, mm, hh; + ss = currenttime % 60; + currenttime /= 60; + mm = currenttime % 60; + currenttime /= 60; + hh = currenttime; + Sprintf(buf, "%02ld:%02ld:%02ld", hh, mm, ss); + } + } + return buf; +} + +#endif + /* provide the name of the current level for display by various ports */ int describe_level(char *buf) @@ -524,6 +566,7 @@ static struct istat_s initblstats[MAXBLSTATS] = { 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_LONG, 20, BL_TIME), + INIT_BLSTAT("realtime", " %s", ANY_STR, 10, BL_REALTIME), /* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */ INIT_BLSTAT("hunger", " %s", ANY_INT, 40, BL_HUNGER), INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP), @@ -811,6 +854,9 @@ bot_via_windowport(void) /* Time (moves) */ g.blstats[idx][BL_TIME].a.a_long = g.moves; + /* Realtime */ + Strcpy(g.blstats[idx][BL_REALTIME].val, botl_realtime()); + /* Hunger */ /* note: u.uhs is unsigned, and 3.6.1's STATUS_HILITE defined BL_HUNGER to be ANY_UINT, but that was the only non-int/non-long @@ -939,18 +985,25 @@ bot_via_windowport(void) } -/* update just the status lines' 'time' field */ +/* update just the status lines' 'time' and 'realtime' fields */ static void stat_update_time(void) { int idx = g.now_or_before_idx; /* no 0/1 toggle */ - int fld = BL_TIME; + int fld; /* Time (moves) */ + fld = BL_TIME; g.blstats[idx][fld].a.a_long = g.moves; g.valset[fld] = FALSE; + eval_notify_windowport_field(fld, g.valset, idx); + /* Realtime */ + fld = BL_REALTIME; + Strcpy(g.blstats[idx][fld].val, botl_realtime()); + g.valset[fld] = FALSE; eval_notify_windowport_field(fld, g.valset, idx); + if ((windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, NO_COLOR, (unsigned long *) 0); @@ -1241,6 +1294,7 @@ evaluate_and_notify_windowport(boolean *valsetlist, int idx) if (((i == BL_SCORE) && !flags.showscore) || ((i == BL_EXP) && !flags.showexp) || ((i == BL_TIME) && !flags.time) + || ((i == BL_REALTIME) && !iflags.show_realtime) || ((i == BL_HD) && !Upolyd) || ((i == BL_XP || i == BL_EXP) && Upolyd)) { notpresent++; @@ -1308,7 +1362,8 @@ status_initialize(boolean reassessment) /* TRUE: just recheck fields w/o other i : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd) : (fld == BL_XP) ? (boolean) !Upolyd : (fld == BL_HD) ? (boolean) Upolyd - : TRUE; + : (fld == BL_REALTIME) ? (boolean) iflags.show_realtime + : TRUE; fieldname = initblstats[i].fldname; fieldfmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30.30s" @@ -1768,6 +1823,7 @@ static struct fieldid_t { { "ac", BL_AC }, { "hit-dice", BL_HD }, { "turns", BL_TIME }, + { "realtime", BL_REALTIME }, { "hp", BL_HP }, { "hp-max", BL_HPMAX }, { "dgn", BL_LEVELDESC }, diff --git a/src/options.c b/src/options.c index 587455ddd7..a73288a79b 100644 --- a/src/options.c +++ b/src/options.c @@ -2603,6 +2603,163 @@ optfn_race(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } +#ifdef REALTIME_ON_BOTL +static int +optfn_realtime(int optidx, int req, boolean negated, char *opts, char *op) +{ + int retval = optn_ok; + int tmp; + + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + /* '\0': disabled + 'p': play time + 'w': wallclock time + */ + + if (op == empty_optstr) { + tmp = negated ? '\0' : 'p'; + } else { + if (negated) { + bad_negation(allopt[optidx].name, TRUE); + return optn_err; + } + tmp = lowc(*op); + } + switch (tmp) { + case 'd': + tmp = '\0'; + /*FALLTHRU*/ + case '\0': + case 'p': + case 'w': + iflags.show_realtime = (char) tmp; + break; + default: + config_error_add("Unknown %s parameter '%s'", allopt[optidx].name, + op); + retval = optn_err; + } + return retval; + } + if (req == get_val) { + if (!opts) + return optn_err; + opts[0] = '\0'; + tmp = iflags.show_realtime; + Sprintf(opts, "%s", (tmp == 'p') ? "play time" + : (tmp == 'w') ? "wallclock time" + : "disabled"); + return optn_ok; + } + if (req == do_handler) { + winid tmpwin; + anything any; + menu_item *window_pick = (menu_item *) 0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + any.a_char = 'd'; + add_menu(tmpwin, &nul_glyphinfo, &any, 'd', + 0, ATR_NONE, "disabled", MENU_ITEMFLAGS_NONE); + any.a_char = 'p'; + add_menu(tmpwin, &nul_glyphinfo, &any, 'p', + 0, ATR_NONE, "play time", MENU_ITEMFLAGS_NONE); + any.a_char = 'w'; + add_menu(tmpwin, &nul_glyphinfo, &any, 'w', + 0, ATR_NONE, "wallclock time", MENU_ITEMFLAGS_NONE); + end_menu(tmpwin, "Type of time to show on status bar:"); + if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) { + tmp = window_pick->item.a_char; + iflags.show_realtime = tmp == 'd' ? '\0' : tmp; + free((genericptr_t) window_pick); + + status_initialize(REASSESS_ONLY); + } + destroy_nhwindow(tmpwin); + return optn_ok; + } + return optn_ok; +} + +static int +optfn_realtime_format(int optidx, int req, boolean negated, char *opts, char *op) +{ + int retval = optn_ok; + int tmp; + + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + /* realtime_format:seconds, condensed, units */ + + if (op == empty_optstr) { + config_error_add("%s must take a parameter", allopt[optidx].name); + return optn_err; + } + if (negated) { + bad_negation(allopt[optidx].name, TRUE); + return optn_err; + } + tmp = lowc(*op); + switch (tmp) { + case 's': + case 'c': + case 'u': + iflags.realtime_format = (char) tmp; + break; + default: + config_error_add("Unknown %s parameter '%s'", allopt[optidx].name, + op); + retval = optn_err; + } + return retval; + } + if (req == get_val) { + if (!opts) + return optn_err; + opts[0] = '\0'; + tmp = iflags.realtime_format; + Sprintf(opts, "%s", (tmp == 's') ? "seconds" + : (tmp == 'c') ? "condensed" + : "units"); + return optn_ok; + } + if (req == do_handler) { + winid tmpwin; + anything any; + menu_item *window_pick = (menu_item *) 0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + any.a_char = 's'; + add_menu(tmpwin, &nul_glyphinfo, &any, 's', + 0, ATR_NONE, "seconds (s)", MENU_ITEMFLAGS_NONE); + any.a_char = 'c'; + add_menu(tmpwin, &nul_glyphinfo, &any, 'c', + 0, ATR_NONE, "condensed (h:mm)", MENU_ITEMFLAGS_NONE); + any.a_char = 'u'; + add_menu(tmpwin, &nul_glyphinfo, &any, 'u', + 0, ATR_NONE, "units (hh:mm:ss)", MENU_ITEMFLAGS_NONE); + end_menu(tmpwin, "Format used by realtime display:"); + if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) { + iflags.realtime_format = window_pick->item.a_char; + free((genericptr_t) window_pick); + + status_initialize(REASSESS_ONLY); + } + destroy_nhwindow(tmpwin); + return optn_ok; + } + return optn_ok; +} +#endif + static int optfn_roguesymset(int optidx, int req, boolean negated UNUSED, char *opts, char *op) diff --git a/src/windows.c b/src/windows.c index 48fe2ac944..5756dae4f0 100644 --- a/src/windows.c +++ b/src/windows.c @@ -902,16 +902,16 @@ genl_status_update(int idx, genericptr_t ptr, int chg UNUSED, enum statusfields idx1, idx2, *fieldlist; char *nb, *text = (char *) ptr; - static enum statusfields fieldorder[][15] = { + static enum statusfields fieldorder[][16] = { /* line one */ { 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_FLUSH, BL_FLUSH }, /* line two, default order */ { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, - BL_TIME, + BL_TIME, BL_REALTIME, BL_HUNGER, BL_CAP, BL_CONDITION, BL_FLUSH }, /* move time to the end */ @@ -919,16 +919,16 @@ genl_status_update(int idx, genericptr_t ptr, int chg UNUSED, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, BL_CAP, BL_CONDITION, - BL_TIME, BL_FLUSH }, + BL_TIME, BL_REALTIME, BL_FLUSH }, /* move experience and time to the end */ { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_HUNGER, BL_CAP, BL_CONDITION, - BL_XP, BL_EXP, BL_HD, BL_TIME, BL_FLUSH }, + BL_XP, BL_EXP, BL_HD, BL_TIME, BL_REALTIME, BL_FLUSH }, /* move level description plus gold and experience and time to end */ { BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_HUNGER, BL_CAP, BL_CONDITION, - BL_LEVELDESC, BL_GOLD, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_FLUSH }, + BL_LEVELDESC, BL_GOLD, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_REALTIME, BL_FLUSH }, }; /* in case interface is using genl_status_update() but has not @@ -1016,7 +1016,7 @@ genl_status_update(int idx, genericptr_t ptr, int chg UNUSED, case BL_HP: /* for pass 4, Hp comes first; mungspaces() will strip the unwanted leading spaces */ case BL_XP: case BL_HD: - case BL_TIME: + case BL_TIME: case BL_REALTIME: Strcpy(nb = eos(nb), " "); break; case BL_LEVELDESC: diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index c754d75eea..0f7039e5ed 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -89,8 +89,8 @@ curses_status_finish(void) * -- fldindex could be any one of the following from botl.h: * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, - * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - * BL_LEVELDESC, BL_EXP, BL_CONDITION + * BL_XP, BL_AC, BL_HD, BL_TIME, BL_REALTIME, BL_HUNGER, BL_HP, + * BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION * -- fldindex could also be BL_FLUSH (-1), which is not really * a field index, but is a special trigger to tell the * windowport that it should redisplay all its status fields, @@ -251,12 +251,12 @@ draw_horizontal(boolean border) /* almost all fields already come with a leading space; "xspace" indicates places where we'll generate an extra one */ static const enum statusfields - twolineorder[3][15] = { + twolineorder[3][16] = { { BL_TITLE, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_ALIGN, /*xspace*/ BL_SCORE, - BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_LEVELDESC, /*xspace*/ BL_GOLD, /*xspace*/ BL_HP, BL_HPMAX, @@ -264,16 +264,17 @@ draw_horizontal(boolean border) /*xspace*/ BL_AC, /*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_TIME, + /*xspace*/ BL_REALTIME, /*xspace*/ BL_HUNGER, BL_CAP, BL_CONDITION, BL_FLUSH }, { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }, - threelineorder[3][15] = { /* moves align to line 2, leveldesc+ to 3 */ + threelineorder[3][16] = { /* moves align to line 2, leveldesc+ to 3 */ { BL_TITLE, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_SCORE, - BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_ALIGN, /*xspace*/ BL_GOLD, /*xspace*/ BL_HP, BL_HPMAX, @@ -281,14 +282,15 @@ draw_horizontal(boolean border) /*xspace*/ BL_AC, /*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_HUNGER, BL_CAP, - BL_FLUSH, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD }, { BL_LEVELDESC, /*xspace*/ BL_TIME, + /*xspace*/ BL_REALTIME, /*xspecial*/ BL_CONDITION, - BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD } + BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, + blPAD, blPAD, blPAD, blPAD, blPAD } }; - const enum statusfields (*fieldorder)[15]; + const enum statusfields (*fieldorder)[16]; xchar spacing[MAXBLSTATS], valline[MAXBLSTATS]; enum statusfields fld, prev_fld; char *text, *p, cbuf[BUFSZ], ebuf[STATVAL_WIDTH]; @@ -431,6 +433,7 @@ draw_horizontal(boolean border) case BL_XP: case BL_HD: case BL_TIME: + case BL_REALTIME: spacing[fld] = status_activefields[fld] ? 1 : 0; break; case BL_SCORE: @@ -1914,6 +1917,11 @@ draw_horizontal(int x, int y, int hp, int hpmax) if (flags.time) print_statdiff(" T:", &prevtime, g.moves, STAT_TIME); +#ifdef REALTIME_ON_BOTL + if (iflags.show_realtime) + printw(win, " %s", botl_realtime()); +#endif + curses_add_statuses(win, FALSE, FALSE, NULL, NULL); } @@ -1995,6 +2003,11 @@ draw_horizontal_new(int x, int y, int hp, int hpmax) if (flags.time) print_statdiff(" T:", &prevtime, g.moves, STAT_TIME); +#ifdef REALTIME_ON_BOTL + if (iflags.show_realtime) + printw(win, " %s", botl_realtime()); +#endif + curses_add_statuses(win, TRUE, FALSE, &x, &y); /* Right-aligned attributes */ @@ -2149,6 +2162,11 @@ draw_vertical(int x, int y, int hp, int hpmax) wmove(win, y++, x); } +#ifdef REALTIME_ON_BOTL + if (iflags.show_realtime) + printw(win, "Realtime: %s", botl_realtime()); +#endif + #ifdef SCORE_ON_BOTL if (flags.showscore) { print_statdiff("Score: ", &prevscore, botl_score(), STAT_OTHER); diff --git a/win/tty/wintty.c b/win/tty/wintty.c index d9c0ef21d6..5abaad0823 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3677,27 +3677,28 @@ static const char *encvals[3][6] = { { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" } }; #define blPAD BL_FLUSH -#define MAX_PER_ROW 15 +#define MAX_PER_ROW 16 /* 2 or 3 status lines */ static const enum statusfields twolineorder[3][MAX_PER_ROW] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, - BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { 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_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_REALTIME, BL_HUNGER, BL_CAP, BL_CONDITION, BL_FLUSH }, /* third row of array isn't used for twolineorder */ { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }, /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */ threelineorder[3][MAX_PER_ROW] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, + blPAD }, { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, - BL_CAP, BL_FLUSH, blPAD, blPAD }, - { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD, + BL_CAP, BL_FLUSH, blPAD, blPAD, blPAD }, + { BL_LEVELDESC, BL_TIME, BL_REALTIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }; static const enum statusfields (*fieldorder)[MAX_PER_ROW]; @@ -3778,8 +3779,8 @@ tty_status_enablefield(int fieldidx, const char *nm, const char *fmt, * -- fldindex could be any one of the following from botl.h: * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, - * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - * BL_LEVELDESC, BL_EXP, BL_CONDITION + * BL_XP, BL_AC, BL_HD, BL_TIME, BL_REALTIME, BL_HUNGER, BL_HP, + * BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION * -- fldindex could also be BL_FLUSH (-1), which is not really * a field index, but is a special trigger to tell the * windowport that it should output all changes received @@ -4142,9 +4143,9 @@ status_sanity_check(void) static const char *const idxtext[] = { "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5 */ "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD", /* 6.. 10 */ - "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11.. 15 */ - "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16.. 19 */ - "BL_LEVELDESC", "BL_EXP", "BL_CONDITION" /* 20.. 22 */ + "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11.. 15 */ + "BL_TIME", "BL_REALTIME", "BL_HUNGER", "BL_HP", /* 16.. 19 */ + "BL_HPMAX", "BL_LEVELDESC", "BL_EXP", "BL_CONDITION" /* 20.. 23 */ }; if (in_sanity_check) From f3cdde6930fd465b74fc6acbe3f44e6c11c69545 Mon Sep 17 00:00:00 2001 From: Joanna Doyle Date: Sat, 3 Jul 2021 00:07:04 +0200 Subject: [PATCH 2/4] Advance botl clock display when user is idle --- include/botl.h | 4 ++++ src/botl.c | 6 ++++++ win/curses/cursdial.c | 21 +++++++++++++++++++++ win/curses/cursinvt.c | 4 ++++ win/curses/cursmain.c | 1 - win/curses/cursmesg.c | 14 ++++++++++++++ win/curses/cursmisc.c | 32 ++++++++++++++++++++++++++++++++ win/curses/cursstat.c | 8 ++++++++ 8 files changed, 89 insertions(+), 1 deletion(-) diff --git a/include/botl.h b/include/botl.h index 673f7822bf..588287c930 100644 --- a/include/botl.h +++ b/include/botl.h @@ -265,4 +265,8 @@ struct istat_s { extern const char *status_fieldnames[]; /* in botl.c */ +#ifdef REALTIME_ON_BOTL +void stat_update_time(void); +#endif + #endif /* BOTL_H */ diff --git a/src/botl.c b/src/botl.c index d4e099b30c..e807805cb4 100644 --- a/src/botl.c +++ b/src/botl.c @@ -15,7 +15,9 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed", static const char *rank(void); static void bot_via_windowport(void); +#ifndef REALTIME_ON_BOTL static void stat_update_time(void); +#endif #ifdef STATUS_HILITES static unsigned long query_conditions(void); static boolean status_hilite_remove(int); @@ -986,7 +988,11 @@ bot_via_windowport(void) /* update just the status lines' 'time' and 'realtime' fields */ +#ifdef REALTIME_ON_BOTL +void +#else static void +#endif stat_update_time(void) { int idx = g.now_or_before_idx; /* no 0/1 toggle */ diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 2d2d5f0479..952e326934 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -289,10 +289,18 @@ curses_character_input_dialog(const char *prompt, const char *choices, curs_set(1); while (1) { +#ifdef REALTIME_ON_BOTL +#ifdef PDCURSES + answer = wgetch_timeout(message_window); +#else + answer = getch_timeout(); +#endif +#else #ifdef PDCURSES answer = wgetch(message_window); #else answer = getch(); +#endif #endif if (answer == ERR) { answer = def; @@ -430,7 +438,12 @@ curses_ext_cmd(void) curs_set(1); wrefresh(extwin); +#ifdef REALTIME_ON_BOTL + letter = getch_timeout(); +#else letter = getch(); +#endif + curs_set(0); prompt_width = (int) strlen(cur_choice); matches = 0; @@ -1452,7 +1465,11 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) menu_display_page(menu, win, curpage, selectors); while (!dismiss) { +#ifdef REALTIME_ON_BOTL + curletter = getch_timeout(); +#else curletter = getch(); +#endif if (curletter == ERR) { num_selected = -1; @@ -1505,7 +1522,11 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) if (isdigit(curletter)) { count = curses_get_count(curletter); /* after count, we know some non-digit is already pending */ +#ifdef REALTIME_ON_BOTL + curletter = getch_timeout(); +#else curletter = getch(); +#endif count_letter = (count > 0L) ? curletter : '\0'; /* remove the count wind (erases last line of message wind) */ diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 2219024194..69e90aa32c 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -151,7 +151,11 @@ curs_scroll_invt(WINDOW *win UNUSED) menukeys, *menukeys ? " " : "", "Ret Esc"); curses_count_window(qbuf); +#ifdef REALTIME_ON_BOTL + ch = getch_timeout(); +#else ch = getch(); +#endif curses_count_window((char *) 0); curses_clear_unhighlight_message_window(); diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 835762546f..5d284e9216 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -816,7 +816,6 @@ curses_nhgetch(void) curses_prehousekeeping(); ch = curses_read_char(); curses_posthousekeeping(); - return ch; } diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 68a8d389f1..5a263cbf40 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -213,6 +213,7 @@ curses_block(boolean noscroll) /* noscroll - blocking because of msgtype mvwprintw(win, my, mx, ">>"), mx += 2; } curses_toggle_color_attr(win, MORECOLOR, moreattr, OFF); + curses_alert_main_borders(FALSE); wrefresh(win); /* cancel mesg suppression; all messages will have had chance to be read */ @@ -220,8 +221,13 @@ curses_block(boolean noscroll) /* noscroll - blocking because of msgtype oldcrsr = curs_set(1); do { +#ifdef REALTIME_ON_BOTL + ret = wgetch_timeout(win); + if (ret == '\0') +#else ret = wgetch(win); if (ret == ERR || ret == '\0') +#endif ret = '\n'; /* msgtype=stop should require space/enter rather than any key, as we want to prevent YASD from direction keys. */ @@ -598,10 +604,18 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) curs_set(1); wrefresh(win); curses_got_input(); /* despite its name, before rather than after... */ +#ifdef REALTIME_ON_BOTL +#ifdef PDCURSES + ch = wgetch_timeout(win); +#else + ch = getch_timeout(); +#endif +#else #ifdef PDCURSES ch = wgetch(win); #else ch = getch(); +#endif #endif curs_set(0); diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index fca63ed056..0f04dd2a4b 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -21,6 +21,34 @@ static int curs_y = -1; static int parse_escape_sequence(void); +#ifdef REALTIME_ON_BOTL +int +wgetch_timeout(WINDOW *win) +{ + int ch, y, x; + leaveok(win, false); + getyx(win, y, x); + for (;;) { + wtimeout(win, 333); + ch = wgetch(win); + if (ch == -1) { + stat_update_time(); + wmove(win, y, x); + refresh(); + } else { + wtimeout(win, -1); + refresh(); + return ch; + } + } +} + +int +getch_timeout() +{ + return wgetch_timeout(curses_get_nhwin(MAP_WIN)); +} +#endif /* Read a character of input from the user */ @@ -35,7 +63,11 @@ curses_read_char(void) /* cancel message suppression; all messages have had a chance to be read */ curses_got_input(); +#ifdef REALTIME_ON_BOTL + ch = getch_timeout(); +#else ch = getch(); +#endif #if defined(ALT_0) || defined(ALT_9) || defined(ALT_A) || defined(ALT_Z) tmpch = ch; #endif diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index 0f7039e5ed..4178300d82 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -30,7 +30,11 @@ static int curses_status_colors[MAXBLSTATS]; static int hpbar_percent, hpbar_color; static int vert_status_dirty; +#ifdef REALTIME_ON_BOTL +void draw_status(void); +#else static void draw_status(void); +#endif static void draw_vertical(boolean); static void draw_horizontal(boolean); static void curs_HPbar(char *, int); @@ -206,7 +210,11 @@ curses_status_update(int fldidx, genericptr_t ptr, int chg UNUSED, int percent, RESTORE_WARNING_FORMAT_NONLITERAL +#ifdef REALTIME_ON_BOTL +void +#else static void +#endif draw_status(void) { WINDOW *win = curses_get_nhwin(STATUS_WIN); From 04811450dcf11238867a455658c2a11769f0f637 Mon Sep 17 00:00:00 2001 From: Joanna Doyle Date: Sat, 3 Jul 2021 07:24:46 +0200 Subject: [PATCH 3/4] advance ui/statusline clock on tty as well --- win/tty/wintty.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 5abaad0823..c9df605041 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -77,6 +77,11 @@ extern short glyph2tile[]; #define HUPSKIP_RESULT(RES) /*empty*/ #endif /* ?HANGUP_HANDLING */ +#ifdef REALTIME_ON_BOTL +#include +#include +#endif + /* Interface definition, for windows.c */ struct window_procs tty_procs = { "tty", @@ -3513,11 +3518,37 @@ tty_nhgetch(void) i = randomkey(); } else { #ifdef UNIX +#ifdef REALTIME_ON_BOTL + fd_set read_set; + struct timespec timeout; + int select_retval; + for (;;) { + FD_SET(fileno(stdin), &read_set); + timeout.tv_sec = 0; + timeout.tv_nsec = 3e8; + select_retval = pselect(1, &read_set, NULL, NULL, &timeout, NULL); + if (FD_ISSET(fileno(stdin), &read_set)) { + FD_CLR(fileno(stdin), &read_set); + i = (++nesting == 1) + ? tgetch() + : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1) + ? (int) nestbuf : EOF; + --nesting; + break; + } else { + printf("\0337"); // save cursor + stat_update_time(); + printf("\0338"); // restore cursor + fflush(stdout); + } + } +#else i = (++nesting == 1) ? tgetch() : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1) ? (int) nestbuf : EOF; --nesting; +#endif #else i = tgetch(); #endif From b29fcd8fae90768f5b75d1e71dcee967d7395fb9 Mon Sep 17 00:00:00 2001 From: Joanna Doyle Date: Sat, 3 Jul 2021 08:40:41 +0200 Subject: [PATCH 4/4] implement tty clock with poll --- win/tty/wintty.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index c9df605041..86cf4bfc85 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -78,9 +78,11 @@ extern short glyph2tile[]; #endif /* ?HANGUP_HANDLING */ #ifdef REALTIME_ON_BOTL -#include +#ifdef UNIX +#include #include #endif +#endif /* Interface definition, for windows.c */ struct window_procs tty_procs = { @@ -3519,16 +3521,16 @@ tty_nhgetch(void) } else { #ifdef UNIX #ifdef REALTIME_ON_BOTL - fd_set read_set; + struct pollfd pf; + pf.fd = fileno(stdin); + pf.events = POLLIN; struct timespec timeout; - int select_retval; + int ppoll_ret; for (;;) { - FD_SET(fileno(stdin), &read_set); timeout.tv_sec = 0; - timeout.tv_nsec = 3e8; - select_retval = pselect(1, &read_set, NULL, NULL, &timeout, NULL); - if (FD_ISSET(fileno(stdin), &read_set)) { - FD_CLR(fileno(stdin), &read_set); + timeout.tv_nsec = 4e8; + ppoll_ret = ppoll(&pf, 1, &timeout, NULL); + if (ppoll_ret > 0) { i = (++nesting == 1) ? tgetch() : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1)