mirror of
https://github.com/zellij-org/zellij.git
synced 2026-10-12 02:27:24 +00:00
fix(windows): route Windows Terminal through the VT input path (#5335)
Windows Terminal does not set TERM by default, so zellij on WT was using the native-console crossterm path that reads INPUT_RECORDs. That path can't observe bracketed-paste sequences (`\e[200~/\e[201~`), which is one of the reasons multi-line paste has never worked correctly on Windows (issue #4885). WT sets `WT_SESSION` per-process. Use that as a second trigger for the VT input path so default WT installs get raw VT byte input (and with it, termwiz's bracketed-paste detection, the SGR mouse format, correct emoji delivery, etc.) without requiring the user to set TERM. Introduce `use_vt_path()` in `os_input_output_windows.rs` as the single source of truth for the gate. Previously the stdin handler had one TERM check and `enable_mouse_support` / `disable_mouse_support` each had their own — so adding `WT_SESSION` to just the stdin gate caused mouse setup to take the native-console branch, which calls `crossterm::event::EnableMouseCapture` and clobbers the `ENABLE_VIRTUAL_TERMINAL_INPUT` flag the stdin path had just set. That broke all input from WT until the helper aligned every site.
This commit is contained in:
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
* fix: fragmented STDIN causing "garbage" input on slow SSH connections (https://github.com/zellij-org/zellij/pull/5320)
|
||||
* feat: stacked lists - new stack UI (https://github.com/zellij-org/zellij/pull/5331 and https://github.com/zellij-org/zellij/pull/5337)
|
||||
* fix: more verbose startup errors (https://github.com/zellij-org/zellij/pull/5334)
|
||||
* fix(windows): route Windows Terminal through the VT input path (https://github.com/zellij-org/zellij/pull/5335)
|
||||
|
||||
## [0.44.3] - 2026-05-13
|
||||
* fix(windows): bump windows-sys to 0.59 to align manifest with code, fixing source builds via `cargo install`/`cargo binstall` (https://github.com/zellij-org/zellij/pull/5139)
|
||||
|
||||
@@ -9,6 +9,30 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
use zellij_utils::ipc::{IpcReceiverWithContext, IpcSenderWithContext};
|
||||
|
||||
/// Whether zellij should use the VT byte path on Windows (raw stdin via
|
||||
/// `ReadFile` + termwiz/kitty parsing) instead of the native-console path
|
||||
/// (crossterm `INPUT_RECORD`s).
|
||||
///
|
||||
/// True when either of these environment variables is set:
|
||||
///
|
||||
/// * `TERM` — set automatically by terminal emulators that expose a Unix-style
|
||||
/// environment (Alacritty, WezTerm, WSL sessions inheriting bash's env), or
|
||||
/// set manually by users who want zellij on the VT path (e.g. adding
|
||||
/// `TERM=xterm-256color` to a Windows Terminal / PowerShell profile).
|
||||
/// * `WT_SESSION` — set per-session by Windows Terminal itself. Detecting it
|
||||
/// means default WT installs land on the VT path without the user having to
|
||||
/// set `TERM`, so they get bracketed paste, kitty keyboard, SGR mouse, and
|
||||
/// the other features that depend on raw VT input.
|
||||
///
|
||||
/// Every VT-path-conditional code site (stdin handler, mouse mode setup,
|
||||
/// future input-related code) should call this so the gates can't drift —
|
||||
/// when they did drift, mouse setup using `crossterm::EnableMouseCapture`
|
||||
/// clobbered the `ENABLE_VIRTUAL_TERMINAL_INPUT` flag the stdin path had
|
||||
/// just set and broke all input from WT.
|
||||
pub(crate) fn use_vt_path() -> bool {
|
||||
std::env::var("TERM").is_ok() || std::env::var("WT_SESSION").is_ok()
|
||||
}
|
||||
|
||||
/// Windows async signal listener.
|
||||
///
|
||||
/// Polls `crossterm::terminal::size()` at 100ms intervals for resize events,
|
||||
@@ -206,7 +230,7 @@ fn enable_vt_processing_on_stdout() {
|
||||
/// Windows Terminal) and use crossterm's Console API approach.
|
||||
pub(crate) fn enable_mouse_support(stdout: &mut dyn Write) -> Result<()> {
|
||||
let err_context = "failed to enable mouse mode";
|
||||
if std::env::var("TERM").is_ok() {
|
||||
if use_vt_path() {
|
||||
enable_vt_processing_on_stdout();
|
||||
stdout
|
||||
.write_all(super::os_input_output::ENABLE_MOUSE_SUPPORT.as_bytes())
|
||||
@@ -236,7 +260,7 @@ pub(crate) fn restore_console_mode() {
|
||||
/// See `enable_mouse_support()` for rationale on VT vs Console API paths.
|
||||
pub(crate) fn disable_mouse_support(stdout: &mut dyn Write) -> Result<()> {
|
||||
let err_context = "failed to disable mouse mode";
|
||||
if std::env::var("TERM").is_ok() {
|
||||
if use_vt_path() {
|
||||
stdout
|
||||
.write_all(super::os_input_output::DISABLE_MOUSE_SUPPORT.as_bytes())
|
||||
.context(err_context)?;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::keyboard_parser::{KittyKeyboardParser, KittyParseOutcome};
|
||||
use crate::os_input_output::ClientOsApi;
|
||||
#[cfg(windows)]
|
||||
use crate::os_input_output_windows::use_vt_path;
|
||||
use crate::stdin_ansi_parser::{PendingPartial, StdinAnsiParser};
|
||||
#[cfg(windows)]
|
||||
use crate::stdin_handler_windows::enable_vt_input;
|
||||
@@ -21,19 +23,11 @@ pub(crate) fn stdin_loop(
|
||||
explicitly_disable_kitty_keyboard_protocol: bool,
|
||||
resize_sender: Option<std::sync::mpsc::Sender<()>>,
|
||||
) {
|
||||
// On Windows, choose between two input strategies early — we need this
|
||||
// decision before the startup ANSI query below.
|
||||
//
|
||||
// 1. Native console (no TERM env var): Use crossterm's event::read() which
|
||||
// reads INPUT_RECORDs via ReadConsoleInput. Works in cmd.exe, PowerShell,
|
||||
// and Windows Terminal where ALT is reported as a modifier flag.
|
||||
//
|
||||
// 2. Terminal emulator (TERM is set, e.g. Alacritty): Enable
|
||||
// ENABLE_VIRTUAL_TERMINAL_INPUT so ReadFile on stdin returns raw VT bytes,
|
||||
// bypassing conpty's lossy VT→INPUT_RECORD translation. Then use the
|
||||
// termwiz byte parser (same as Unix) which understands ESC-prefixed ALT.
|
||||
// On Windows we choose between the VT byte path (termwiz/kitty parsing)
|
||||
// and the native-console path (crossterm INPUT_RECORDs) early, before the
|
||||
// startup ANSI query below. See `use_vt_path()` for the trigger conditions.
|
||||
#[cfg(windows)]
|
||||
let use_vt_reader = std::env::var("TERM").is_ok() && enable_vt_input();
|
||||
let use_vt_reader = use_vt_path() && enable_vt_input();
|
||||
|
||||
// Send the startup host query string so the host terminal replies
|
||||
// with its live pixel dimensions, fg/bg, sync-output support, and
|
||||
|
||||
Reference in New Issue
Block a user