commit 16680e57bacadbf2b0493e89f5f62e9fb1b69328
parent 41cac54325e9d79543b2908bd60daf11243f7893
Author: zeertzjq <zeertzjq@outlook.com>
Date: Tue, 3 Feb 2026 21:40:40 +0800
fix(tui): use 0x7f as Backspace on Windows with VT input (#37679)
Problem: 0x08 is treated as Backspace instead of Ctrl-H on Windows.
Solution: Treat 0x7f as Backspace if VT input is enabled, so that 0x08
is treated as Ctrl-H.
Ref: https://github.com/microsoft/terminal/issues/4949
Diffstat:
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
@@ -132,6 +132,9 @@ void tinput_init(TermInput *input, Loop *loop, TerminfoEntry *ti)
input->ttimeout = (bool)p_ttimeout;
input->ttimeoutlen = p_ttm;
+ // setup input handle
+ rstream_init_fd(loop, &input->read_stream, input->in_fd);
+
for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) {
pmap_put(int)(&kitty_key_map, kitty_key_map_entry[i].key, (ptr_t)kitty_key_map_entry[i].name);
}
@@ -145,9 +148,6 @@ void tinput_init(TermInput *input, Loop *loop, TerminfoEntry *ti)
int curflags = termkey_get_canonflags(input->tk);
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
- // setup input handle
- rstream_init_fd(loop, &input->read_stream, input->in_fd);
-
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(&loop->uv, &input->timer_handle);
input->timer_handle.data = input;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
@@ -2515,19 +2515,29 @@ static void flush_buf(TUIData *tui)
/// Try to get "kbs" code from stty because "the terminfo kbs entry is extremely
/// unreliable." (Vim, Bash, and tmux also do this.)
+/// On Windows, use 0x7f as Backspace if VT input has been enabled by stream_init().
///
/// @see tmux/tty-keys.c fe4e9470bb504357d073320f5d305b22663ee3fd
/// @see https://bugzilla.redhat.com/show_bug.cgi?id=142659
-static const char *tui_get_stty_erase(int fd)
+/// @see https://github.com/microsoft/terminal/issues/4949
+static const char *tui_get_stty_erase(TermInput *input)
{
static char stty_erase[2] = { 0 };
#if defined(HAVE_TERMIOS_H)
struct termios t;
- if (tcgetattr(fd, &t) != -1) {
+ if (tcgetattr(input->in_fd, &t) != -1) {
stty_erase[0] = (char)t.c_cc[VERASE];
stty_erase[1] = NUL;
DLOG("stty/termios:erase=%s", stty_erase);
}
+#elif defined(MSWIN)
+ DWORD dwMode;
+ if (((uv_handle_t *)&input->read_stream.s.uv)->type == UV_TTY
+ && GetConsoleMode(input->read_stream.s.uv.tty.handle, &dwMode)
+ && (dwMode & ENABLE_VIRTUAL_TERMINAL_INPUT)) {
+ stty_erase[0] = '\x7f';
+ stty_erase[1] = NUL;
+ }
#endif
return stty_erase;
}
@@ -2539,7 +2549,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, void *d
TermInput *input = data;
static const char *stty_erase = NULL;
if (stty_erase == NULL) {
- stty_erase = tui_get_stty_erase(input->in_fd);
+ stty_erase = tui_get_stty_erase(input);
}
if (strequal(name, "key_backspace")) {
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
@@ -2830,7 +2830,6 @@ describe('TUI', function()
end)
it('<C-h> #10134', function()
- t.skip(is_os('win'), 'FIXME: does not work on Windows #36660')
local screen = tt.setup_child_nvim({
'--clean',
'--cmd',