From d6a1b4110a87bf733389a73a4a4e900276255bd9 Mon Sep 17 00:00:00 2001 From: akiyosi Date: Tue, 30 Jun 2026 03:46:23 +0900 Subject: [PATCH] feat(terminal): try to find newer conpty.dll on Windows #40328 The system version of ConPTY in kernel32.dll is old and will mangle some VT sequences sent by shells. Newer versions of ConPTY available as part of the Windows Terminal project fix this by passing through VT sequences unmodified when the terminal has set ENABLE_VIRTUAL_TERMINAL_INPUT. This change allows users to fix buggy behaviour of Neovim's terminal on Windows by copying conpty.dll to Neovim's bin directory. Co-authored-by: Scott Young --- runtime/doc/terminal.txt | 12 ++++++++++++ src/nvim/os/pty_conpty_win.c | 22 ++++++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index ca32fff420..3f9c3f650f 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -42,6 +42,18 @@ by |:mksession| to restore a terminal buffer (by restarting the {cmd}). The terminal environment is initialized as in |jobstart-env|. +============================================================================== +MS-Windows ConPTY *terminal-ms-windows* *terminal-conpty* + +The system ConPTY (in "kernel32.dll") mangles some VT sequences, which can +garble |:terminal| output. To use a newer ConPTY from the Windows Terminal +project, put its "conpty.dll" (and "OpenConsole.exe") next to the Nvim +executable. Nvim uses that "conpty.dll" if present, else falls back to the +system ConPTY, and logs which one (|$NVIM_LOG_FILE|): > + Loaded new ConPTY from conpty.dll + Loaded system ConPTY from kernel32.dll +< + ============================================================================== Input *terminal-input* diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c index 5e0253089b..cb343bb876 100644 --- a/src/nvim/os/pty_conpty_win.c +++ b/src/nvim/os/pty_conpty_win.c @@ -28,11 +28,21 @@ bool os_has_conpty_working(void) TriState os_dyn_conpty_init(void) { - uv_lib_t kernel; - if (uv_dlopen("kernel32.dll", &kernel)) { - uv_dlclose(&kernel); - return kFalse; + uv_lib_t conpty_lib; + + if (!uv_dlopen("conpty.dll", &conpty_lib)) { + ILOG("Loaded new ConPTY from conpty.dll"); + } else { + // uv_dlopen() allocates an error message even on failure, so close the + // handle before reusing conpty_lib for the system ConPTY fallback. + uv_dlclose(&conpty_lib); + if (uv_dlopen("kernel32.dll", &conpty_lib)) { + uv_dlclose(&conpty_lib); + return kFalse; + } + ILOG("Loaded system ConPTY from kernel32.dll"); } + static struct { char *name; FARPROC *ptr; @@ -44,8 +54,8 @@ TriState os_dyn_conpty_init(void) }; for (int i = 0; conpty_entry[i].name != NULL && conpty_entry[i].ptr != NULL; i++) { - if (uv_dlsym(&kernel, conpty_entry[i].name, (void **)conpty_entry[i].ptr)) { - uv_dlclose(&kernel); + if (uv_dlsym(&conpty_lib, conpty_entry[i].name, (void **)conpty_entry[i].ptr)) { + uv_dlclose(&conpty_lib); return kFalse; } }