From 5fdc43c4643107c8e48bb0c555642e105d5265c9 Mon Sep 17 00:00:00 2001 From: xhcoding Date: Sun, 28 Sep 2025 00:14:35 +0800 Subject: [PATCH 1/6] Enable Windows support by using a conpty-proxy The proxy launches processes through conpty and redirects their input/output to its own stdin and stdout. --- CMakeLists.txt | 14 +++++++ vterm-module.c | 14 ++++++- vterm.el | 100 +++++++++++++++++++++++++++++++++++-------------- 3 files changed, 99 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4917264c..da3c6553 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,20 @@ else() file(MAKE_DIRECTORY ${LIBVTERM_INCLUDE_DIR}) endif() +# Build conpty-proxy +if(WIN32) + ExternalProject_add(conpty-proxy + GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git + GIT_TAG v1.0.0 + INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} + INSTALL_COMMAND + COMMAND ${CMAKE_COMMAND} -E copy + /conpty_proxy.exe + /conpty_proxy.exe + ) + add_dependencies(vterm-module conpty-proxy) +endif() + add_library(vterm STATIC IMPORTED) set_target_properties(vterm PROPERTIES IMPORTED_LOCATION ${LIBVTERM_LIBRARY}) target_include_directories(vterm INTERFACE ${LIBVTERM_INCLUDE_DIR}) diff --git a/vterm-module.c b/vterm-module.c index 2294ef47..b9db9068 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -6,7 +6,11 @@ #include #include #include + +#ifndef _WIN32 #include +#endif + #include #include @@ -901,9 +905,13 @@ static void term_process_key(Term *term, emacs_env *env, unsigned char *key, if (is_key(key, len, "")) { term_clear_scrollback(term, env); } else if (is_key(key, len, "")) { +#ifndef _WIN32 tcflow(term->pty_fd, TCOON); +#endif } else if (is_key(key, len, "")) { +#ifndef _WIN32 tcflow(term->pty_fd, TCOOFF); +#endif } else if (is_key(key, len, "")) { vterm_keyboard_start_paste(term->vt); } else if (is_key(key, len, "")) { @@ -1382,7 +1390,8 @@ emacs_value Fvterm_set_size(emacs_env *env, ptrdiff_t nargs, emacs_value args[], emacs_value Fvterm_set_pty_name(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) { - Term *term = env->get_user_ptr(env, args[0]); +#ifndef _WIN32 + Term *term = env->get_user_ptr(env, args[0]); if (nargs > 1) { ptrdiff_t len = string_bytes(env, args[1]); @@ -1392,6 +1401,7 @@ emacs_value Fvterm_set_pty_name(emacs_env *env, ptrdiff_t nargs, term->pty_fd = open(filename, O_RDONLY); } +#endif return Qnil; } emacs_value Fvterm_get_pwd(emacs_env *env, ptrdiff_t nargs, emacs_value args[], @@ -1406,6 +1416,7 @@ emacs_value Fvterm_get_pwd(emacs_env *env, ptrdiff_t nargs, emacs_value args[], emacs_value Fvterm_get_icrnl(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) { +#ifndef _WIN32 Term *term = env->get_user_ptr(env, args[0]); if (term->pty_fd > 0) { @@ -1417,6 +1428,7 @@ emacs_value Fvterm_get_icrnl(emacs_env *env, ptrdiff_t nargs, else return Qnil; } +#endif return Qnil; } diff --git a/vterm.el b/vterm.el index 9bc0d161..556f89d9 100644 --- a/vterm.el +++ b/vterm.el @@ -436,6 +436,13 @@ copy-mode and set to nil on leaving." :type 'boolean :group 'vterm) +(defcustom vterm-decode-coding-system (if (eq system-type 'windows-nt) + 'utf-8 + locale-coding-system) + "The coding system used for decoding." + :type 'symbol + :group 'vterm) + ;;; Faces (defface vterm-color-black @@ -790,33 +797,35 @@ Exceptions are defined by `vterm-keymap-exceptions'." (local 'filter-buffer-substring-function) #'vterm--filter-buffer-substring) (setq vterm--process - (make-process - :name "vterm" - :buffer (current-buffer) - :command - `("/bin/sh" "-c" - ,(format - "stty -nl sane %s erase ^? rows %d columns %d >/dev/null && exec %s" - ;; Some stty implementations (i.e. that of *BSD) do not - ;; support the iutf8 option. to handle that, we run some - ;; heuristics to work out if the system supports that - ;; option and set the arg string accordingly. This is a - ;; gross hack but FreeBSD doesn't seem to want to fix it. - ;; - ;; See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=220009 - (if (eq system-type 'berkeley-unix) "" "iutf8") - (window-body-height) - width (vterm--get-shell))) - ;; :coding 'no-conversion - :connection-type 'pty - :file-handler t - :filter #'vterm--filter - ;; The sentinel is needed if there are exit functions or if - ;; vterm-kill-buffer-on-exit is set to t. In this latter case, - ;; vterm--sentinel will kill the buffer - :sentinel (when (or vterm-exit-functions - vterm-kill-buffer-on-exit) - #'vterm--sentinel)))) + (if (eq system-type 'windows-nt) + (vterm--conpty-proxy-make-process width (window-body-height)) + (make-process + :name "vterm" + :buffer (current-buffer) + :command + `("/bin/sh" "-c" + ,(format + "stty -nl sane %s erase ^? rows %d columns %d >/dev/null && exec %s" + ;; Some stty implementations (i.e. that of *BSD) do not + ;; support the iutf8 option. to handle that, we run some + ;; heuristics to work out if the system supports that + ;; option and set the arg string accordingly. This is a + ;; gross hack but FreeBSD doesn't seem to want to fix it. + ;; + ;; See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=220009 + (if (eq system-type 'berkeley-unix) "" "iutf8") + (window-body-height) + width (vterm--get-shell))) + ;; :coding 'no-conversion + :connection-type 'pty + :file-handler t + :filter #'vterm--filter + ;; The sentinel is needed if there are exit functions or if + ;; vterm-kill-buffer-on-exit is set to t. In this latter case, + ;; vterm--sentinel will kill the buffer + :sentinel (when (or vterm-exit-functions + vterm-kill-buffer-on-exit) + #'vterm--sentinel))))) ;; Change major-mode is not allowed ;; Vterm interfaces with an underlying process. Changing the major @@ -1452,6 +1461,39 @@ Search Manipulate Selection Data in (message "kill-ring is updated by vterm OSC 52(Manipulate Selection Data)")) )) +;;; conpty-proxy +(defun vterm--conpty-proxy-path () + "Path of conpty_proxy.exe." + (expand-file-name "conpty_proxy.exe" (file-name-directory (locate-library "vterm.el" t)))) + +(defun vterm--conpty-proxy-make-process (width height) + "Make conpty proxy process." + (let* ((conpty-id (format "econpty_%s_%s" (format-time-string "%s") (emacs-pid))) + conpty-process) + (setq conpty-process + (make-process + :name "vterm" + :buffer (current-buffer) + :command `(,(vterm--conpty-proxy-path) "new" + ,conpty-id ,(int-to-string width) ,(int-to-string height) ,(vterm--get-shell)) + :coding 'no-conversion + :file-handler t + :filter #'vterm--filter + ;; The sentinel is needed if there are exit functions or if + ;; vterm-kill-buffer-on-exit is set to t. In this latter case, + ;; vterm--sentinel will kill the buffer + :sentinel (when (or vterm-exit-functions + vterm-kill-buffer-on-exit) + #'vterm--sentinel))) + + (process-put conpty-process 'conpty-id conpty-id) + conpty-process)) + +(defun vterm--conpty-proxy-resize(width height) + "Call conpty proxy resize command." + (let ((conpty-id (process-get vterm--process 'conpty-id))) + (shell-command-to-string (format "%s resize %s %s %s" (vterm--conpty-proxy-path) conpty-id width height)))) + ;;; Entry Points ;;;###autoload @@ -1583,7 +1625,7 @@ Then triggers a redraw from the module." (setq decoded-substring (decode-coding-string (substring input i funny) - locale-coding-system t)) + vterm-decode-coding-system t)) ;; Check for multibyte characters that ends ;; before end of string, and save it for ;; next time. @@ -1656,6 +1698,8 @@ Argument EVENT process event." (> width 0) (> height 0)) (vterm--set-size vterm--term height width) + (when (eq system-type 'windows-nt) + (vterm--conpty-proxy-resize width height)) (cons width height))))) (defun vterm--get-margin-width () From 1eeda7b47e49cc54e66ec550d8385964f99decfc Mon Sep 17 00:00:00 2001 From: xhcoding Date: Mon, 29 Sep 2025 09:50:04 +0800 Subject: [PATCH 2/6] If conpty_proxy exists, do not compile it. --- CMakeLists.txt | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index da3c6553..fb6d449c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,16 +92,24 @@ endif() # Build conpty-proxy if(WIN32) - ExternalProject_add(conpty-proxy - GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git - GIT_TAG v1.0.0 - INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} - INSTALL_COMMAND - COMMAND ${CMAKE_COMMAND} -E copy - /conpty_proxy.exe - /conpty_proxy.exe - ) - add_dependencies(vterm-module conpty-proxy) + + find_program(CONPTY_PROXY_PATH conpty_proxy.exe) + + if (CONPTY_PROXY_PATH) + message(STATUS "Use system conpty: ${CONPTY_PROXY_PATH}") + else() + ExternalProject_add(conpty-proxy + GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git + GIT_TAG v1.0.0 + INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} + INSTALL_COMMAND + COMMAND ${CMAKE_COMMAND} -E copy + /conpty_proxy.exe + /conpty_proxy.exe + ) + add_dependencies(vterm-module conpty-proxy) + endif() + endif() add_library(vterm STATIC IMPORTED) From 6cf1d3cdc3fc8c814680b40f2f876c561930acc7 Mon Sep 17 00:00:00 2001 From: xhcoding Date: Tue, 30 Sep 2025 01:31:37 +0800 Subject: [PATCH 3/6] Update conpty_proxy --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb6d449c..150a6436 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ if(WIN32) else() ExternalProject_add(conpty-proxy GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git - GIT_TAG v1.0.0 + GIT_TAG v1.0.1 INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} INSTALL_COMMAND COMMAND ${CMAKE_COMMAND} -E copy From e387d0554660050aa0971d3b10edaf90dd85d893 Mon Sep 17 00:00:00 2001 From: xhcoding Date: Fri, 3 Oct 2025 21:28:05 +0800 Subject: [PATCH 4/6] add vterm-conpty-proxy-path and doc --- CMakeLists.txt | 26 +++++++----------- README.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ vterm.el | 22 +++++++++++++-- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 150a6436..e4d6d79d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,23 +92,15 @@ endif() # Build conpty-proxy if(WIN32) - - find_program(CONPTY_PROXY_PATH conpty_proxy.exe) - - if (CONPTY_PROXY_PATH) - message(STATUS "Use system conpty: ${CONPTY_PROXY_PATH}") - else() - ExternalProject_add(conpty-proxy - GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git - GIT_TAG v1.0.1 - INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} - INSTALL_COMMAND - COMMAND ${CMAKE_COMMAND} -E copy - /conpty_proxy.exe - /conpty_proxy.exe - ) - add_dependencies(vterm-module conpty-proxy) - endif() + ExternalProject_add(conpty-proxy + GIT_REPOSITORY https://github.com/xhcoding/conpty-proxy.git + GIT_TAG v1.0.1 + INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR} + INSTALL_COMMAND + COMMAND ${CMAKE_COMMAND} -E copy + /conpty_proxy.exe + /conpty_proxy.exe + ) endif() diff --git a/README.md b/README.md index 00bf2849..8e8aab34 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,57 @@ function vterm_printf; end ``` + +## vterm and Windows + +VTerm on Windows relies on ConPTY (Windows Console Pseudo-Terminal), which requires: + +- Windows 10 Insider Preview build 17733 or later, OR + +- Windows 10 version 1903 (May 2019 Update) or later + +### Compile vterm + +Building VTerm requires the MSYS2 environment with appropriate toolchains. + +#### Installing MSYS2 + +Download and install from https://www.msys2.org/ or use a package manager: + +Using the official installer: + +- Download the executable from the MSYS2 website + +- Follow the installation wizard + +Using Scoop (alternative method): + +```sh +scoop install msys2 +``` + +#### Installing Required Tools and Libraries + +Open the UCRT64 shell in MSYS2 and install the necessary packages: + +```sh +pacman -S --noconfirm \ + mingw-w64-ucrt-x86_64-toolchain \ + mingw-w64-ucrt-x86_64-cmake \ + mingw-w64-ucrt-x86_64-libvterm +``` + +Alternative shells: You can also use MINGW64 shell, but UCRT64 is recommended for better compatibility with modern Windows versions. + +#### Build vterm + +Open the UCRT64 shell in MSYS2: + +```sh +cmake -S . -Bbuild +cmake --build build +``` + # Debugging and testing If you have successfully built the module, you can test it by executing the @@ -510,6 +561,27 @@ Examples: Use tramp's default shell for all other methods. `'(("ssh" login-shell "/bin/bash") ("scp" login-shell "/bin/bash"))` +## `vterm-decode-coding-system` + +Controls the character encoding for decoding vterm output. + +Default Value: `locale-coding-system` + +**Important Note for Windows Users**: +If your Windows system's default encoding is not UTF-8, you must set this variable to utf-8 to ensure proper display of international characters and symbols in vterm. + +## `vterm-conpty-proxy-path` + +Specifies the file path to conpty_proxy.exe on Windows systems. + +When set to nil, vterm automatically searches for the executable in: + +1. System PATH environment variable + +2 Vterm package installation directory + +When set to a custom path, use the full absolute path to the executable + ## Keybindings If you want a key to be sent to the terminal, bind it to `vterm--self-insert`, diff --git a/vterm.el b/vterm.el index 556f89d9..3fd47f99 100644 --- a/vterm.el +++ b/vterm.el @@ -443,6 +443,18 @@ copy-mode and set to nil on leaving." :type 'symbol :group 'vterm) +(defcustom vterm-conpty-proxy-path nil + "Path to conpty_proxy.exe. + +If set to nil, search for the executable in the following order: +1. In the system PATH environment variable +2. In the vterm package installation directory + +Set this to the full path if you have conpty_proxy.exe in a custom location." + :type '(choice (const :tag "Auto-detect" nil) + (file :tag "Custom path")) + :group 'vterm) + ;;; Faces (defface vterm-color-black @@ -1463,8 +1475,14 @@ Search Manipulate Selection Data in ;;; conpty-proxy (defun vterm--conpty-proxy-path () - "Path of conpty_proxy.exe." - (expand-file-name "conpty_proxy.exe" (file-name-directory (locate-library "vterm.el" t)))) + "Path of conpty_proxy.exe. +If `vterm--conpty-proxy-path' is set, use that value. +Otherwise, search in PATH for 'conpty_proxy.exe'. +If not found in PATH, look in the vterm.el directory." + (or vterm--conpty-proxy-path + (executable-find "conpty_proxy.exe") + (expand-file-name "conpty_proxy.exe" + (file-name-directory (locate-library "vterm" t))))) (defun vterm--conpty-proxy-make-process (width height) "Make conpty proxy process." From 64cf15d87728481c6b8ec960d1aa2acbf535133c Mon Sep 17 00:00:00 2001 From: xhcoding Date: Fri, 3 Oct 2025 21:34:00 +0800 Subject: [PATCH 5/6] typo --- vterm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vterm.el b/vterm.el index 3fd47f99..29f9d734 100644 --- a/vterm.el +++ b/vterm.el @@ -1476,10 +1476,10 @@ Search Manipulate Selection Data in ;;; conpty-proxy (defun vterm--conpty-proxy-path () "Path of conpty_proxy.exe. -If `vterm--conpty-proxy-path' is set, use that value. +If `vterm-conpty-proxy-path' is set, use that value. Otherwise, search in PATH for 'conpty_proxy.exe'. If not found in PATH, look in the vterm.el directory." - (or vterm--conpty-proxy-path + (or vterm-conpty-proxy-path (executable-find "conpty_proxy.exe") (expand-file-name "conpty_proxy.exe" (file-name-directory (locate-library "vterm" t))))) From 58b3d992930f8b042191ca095401a925c25df9cf Mon Sep 17 00:00:00 2001 From: xhcoding Date: Sun, 5 Oct 2025 20:54:59 +0800 Subject: [PATCH 6/6] fix locate-library vterm.el error --- vterm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vterm.el b/vterm.el index 29f9d734..d4c05fe8 100644 --- a/vterm.el +++ b/vterm.el @@ -1482,7 +1482,7 @@ If not found in PATH, look in the vterm.el directory." (or vterm-conpty-proxy-path (executable-find "conpty_proxy.exe") (expand-file-name "conpty_proxy.exe" - (file-name-directory (locate-library "vterm" t))))) + (file-name-directory (locate-library "vterm.el" t))))) (defun vterm--conpty-proxy-make-process (width height) "Make conpty proxy process."