24 Commits
Author SHA1 Message Date
6343be42c6 fix(stack): scrollback truncation when adding a stacked pane in list mode (#5403)
When `stacked_pane_list` is on and the user adds a stacked pane, the classic
in-grid stack path first collapses the pre-existing pane to `Fixed(1)` and
then `focus_pane` fires `reapply_pane_frames`, which sends `resize_pty!` to
the collapsed pane at 1 row. The stack-list groupify pass (invoked later
during `render`) then extracts the pane to `suppressed_panes` and resizes it
back to the visible size, issuing a second `resize_pty!`. The two rapid
WINCH events cause the shell (nushell, pwsh, bash, …) inside the pane to
redraw its prompt twice, overwriting previously-visible rows of output.
The grid data is preserved in `lines_above`, but the visible viewport ends
up mostly blank — the user sees only the pre-existing rows that had already
scrolled off, giving the impression of a truncated buffer.

Sync stacked-pane-list mode right after the classic mutation, before
`focus_pane`. `groupify_all` moves the collapsed member into
`suppressed_panes` first, so the subsequent `reapply_pane_frames` (which
only iterates `tiled_panes`) never sends a 1-row `resize_pty!` to that
shell. The pane is then resized once to the visible size via
`resize_stack_list_hidden_members`.

Regression test asserts that after adding a stacked pane in this mode, the
pre-existing pane is in `suppressed_panes` and the new visible member has
non-fixed row dimensions — proving groupify happened before any classic
`reapply_pane_frames` step.

Co-authored-by: divens <[email protected]>
Co-authored-by: Aram Drevekenin <[email protected]>
2026-08-11 09:10:54 +02:00
divens 812ad861bc fix(windows): prefer PATHEXT variants over bare filename in resolver (#5364)
`find_executable` used to short-circuit on the first exists()-and-is_file
match and only consult PATHEXT if no bare file was present. That returned
the wrong file for tools whose Windows installer ships both a Unix-style
launcher and a `.bat` alongside it. The reported case is Composer, whose
setup places bare `composer` (a Unix launcher, invalid PE on Windows) next
to `composer.bat`. The bare file was resolved and handed to CreateProcessW,
which rejected it with ERROR_BAD_EXE_FORMAT.

On Windows, when the candidate has no user-supplied extension, walk PATHEXT
first and only fall back to the bare match if nothing matches. This mirrors
cmd.exe's and powershell's resolution.
2026-07-21 12:18:04 +02:00
divens d1a85aedfe fix(windows): preserve backslashes when quoting spawned-process arguments (#5336)
`build_command_line` in the ConPTY backend accumulated runs of backslashes
inside a quoted argument but never emitted them before non-quote characters,
so `C:\Users\me\Folder With Space\README.md` reached the child as
`C:UsersmeFolder With SpaceREADME.md`. Only args routed through the quoted
branch were affected, which matches the reported `zellij edit` failure on
Windows backslash paths containing spaces.

Rewrite the loop to follow `CommandLineToArgvW` rules exactly: n backslashes
not followed by a quote emit literally, 2n+1 escape an embedded quote, and
trailing backslashes are doubled before the closing quote. Add unit tests
for each case, including a regression test for the issue #5293 repro.
2026-07-07 16:05:31 +02:00
divens 39a75c5493 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.
2026-07-07 14:41:44 +02:00
divens cc86e31b84 fix(windows): bump windows-sys to 0.59 (#5139)
The workspace `windows-sys` constraint was pinned to 0.52 while the
Windows source uses APIs and namespaces from 0.59 (e.g. WriteFile in
Win32::Storage::FileSystem). CI built fine because Cargo.lock pinned
0.59, but downstream `cargo install`/`cargo binstall` did fresh
resolution and selected 0.52, breaking the build for users on Windows.

Align the manifest with the code:
- Bump workspace dep `windows-sys` from 0.52 to 0.59.
- HANDLE is now `*mut c_void` instead of `isize`: replace
  `handle == 0` / `!= 0` with `is_null()`, initialise HANDLE locals
  with `std::ptr::null_mut()`, and pass `null_mut()` as the
  hTemplateFile argument to CreateFileW.
- Pass the process HANDLE through the exit-monitor thread as `usize`
  (raw pointers are not Send; OS handles are safe to use cross-thread).
- Note: HPCON is still `isize` in 0.59, so its zero-init is unchanged.
2026-05-06 17:02:10 +02:00
divens 0d8ebaa234 fix(windows): skip modifier re-encoding for Char keys in cast_crossterm_key (#5014)
PR #4967 added VT modifier encoding for non-Char keys but the guard
condition also matched Char keys, re-encoding Ctrl+C from [0x03] to
CSI u sequence \x1b[3;5u which shells don't understand. Skip the
encoding block for Char keys since they already handle modifiers.
2026-04-15 09:54:38 +02:00
divensanddivens 442cb15724 fix(ci): pin WiX CLI to v6.0.2 to avoid v7 OSMF EULA gate (#5012)
WiX v7 introduced a mandatory EULA acceptance that breaks unattended
CI builds. Pin the dotnet tool install to 6.0.2 to match the extension.

Co-authored-by: divens <[email protected]>
2026-04-07 16:43:36 +02:00
divensanddivens 2b3ae1e69c fix: respect --name flag when creating new panes (#4979)
* fix: respect --name flag when creating and renaming panes

Three issues fixed:

1. Pane creation: --name was silently discarded for default shell panes
   because use_terminal_title was true. Now user-provided names take
   priority. Also pass initial_pane_title as pane_name to TerminalPane
   constructor (was hardcoded to empty string).

2. CLI rename without --pane-id: was routed through PaneNameInput which
   appends to existing name. Now uses a dedicated RenameActivePane action
   that fully replaces the name, working for any name length including
   single characters.

3. Interactive rename: entering rename mode preserves the existing name
   for editing (append/backspace). Esc reverts to the name before rename
   mode was entered.

Adds 28 tests covering rename semantics, interactive editing, undo,
OSC title interaction, and a wire-level IPC round-trip test.

* refactor: route active-pane rename via Option<PaneId>

Replace the dedicated Action::RenameActivePane variant with an
Option<PaneId> on the existing Action::RenamePaneByPaneId. route.rs
dispatches None to the server-internal ScreenInstruction::RenameActivePane
and Some(id) to the existing RenamePaneWithPaneId path.

Drops all proto/conversion boilerplate for the removed variant: plugin
API additions, IPC tag 137, and both directions of the wire conversion.

---------

Co-authored-by: divens <[email protected]>
2026-04-06 10:34:23 +02:00
divensanddivens cbe0237b1e fix(windows): encode modifiers in raw bytes for non-Char keys (#4967)
On the native console input path, crossterm captures modifier flags
from INPUT_RECORD but the raw bytes written to the PTY don't include
them. This causes inner applications to never see ALT, SHIFT, or CTRL
modifiers for keys like Enter, Tab, Backspace, arrows, and function
keys.

Encode modifiers using standard VT conventions:
- Simple keys (Enter, Tab, Backspace): ALT uses ESC prefix, other
  modifiers use CSI u encoding
- CSI letter-final keys (arrows, Home, End): xterm modifier parameter
- CSI tilde keys (PageUp, Delete, F5+): modifier before tilde
- SS3 keys (F1-F4): convert to CSI with modifier

Co-authored-by: divens <[email protected]>
2026-04-03 10:04:41 +02:00
divensanddivens d12b75800c fix: resolve Windows compilation warnings (#4890)
- Consume unused CompilationResult from embed_resource::compile in build.rs
- Gate std::io::prelude import with #[cfg(unix)] since it's only used by
  daemonize_web_server which is unix-only

Co-authored-by: divens <[email protected]>
2026-03-28 10:38:37 +01:00
divensanddivens 3fa7744906 Add windows support in strider (#4854)
* Add Windows path support to strider plugin

Strider is a WASM plugin where std::path::PathBuf uses Unix semantics,
so Windows backslash paths break parent(), join(), strip_prefix(), etc.

Fix by normalizing all incoming paths to forward slashes at plugin
boundaries and converting back to native backslashes for display. A new
platform.rs module detects the host OS at runtime from the initial_cwd
format and centralizes all platform-aware logic.

Also fix scan_host_folder on the server side: canonicalize
plugin_host_folder so the starts_with security check works on Windows,
where canonicalize() adds the \?\ prefix.

* Add virtual root view for drive and WSL navigation in strider

On Windows, navigating up from a drive root (e.g. C:\) now shows a
"Computer" view listing all available drives and WSL distributions.
Drives are enumerated via GetLogicalDriveStringsW and WSL distros
via `wsl -l -q` (since read_dir on \wsl.localhost fails with OS
error 64/67). Drive letters appear before WSL entries, which are
displayed as "Ubuntu-24.04 (WSL)" etc. On non-Windows platforms,
behavior is unchanged.

* Rename ListHostEntries to ListWindowsVolumes and gate behind FullHdAccess

The old name was misleading — on Unix it just returned `/`, which isn't
"listing host entries." Rename to ListWindowsVolumes to make the
Windows-specific intent explicit, gate behind FullHdAccess permission,
and replace the Unix implementation with a log::error since the command
is not meaningful on non-Windows platforms.

* Replace direct array indexing with .get() in strider platform detection

Use safe accessors and matches! macro instead of direct byte indexing
for bounds-checked access patterns.

---------

Co-authored-by: divens <[email protected]>
2026-03-21 19:20:32 +01:00
divensanddivens 62633f930d fix(windows): avoid hang when share plugin starts web server (#4863)
Use cmd.status() instead of cmd.output() on Windows to prevent pipe
handle inheritance from blocking indefinitely. cmd.output() creates
pipe handles that the grandchild web server process inherits via
CreateProcess bInheritHandles, keeping them open and preventing EOF.

Co-authored-by: divens <[email protected]>
2026-03-19 08:58:13 +01:00
divensanddivens 1166061f4c Fix Windows config/data paths and consolidate path helpers (#4852)
* Fix Windows config/data paths and consolidate path helpers

Move platform-specific path logic into home_unix.rs / home_windows.rs
behind a shared interface. Consolidate duplicated path helpers from
setup.rs into home.rs. Use Display formatting for paths in setup info
output to avoid escaped backslashes on Windows.

- Remove organization from ProjectDirs to get clean Zellij\ paths
- Add Windows-specific ProgramData paths for system config/data
- Remove duplicated find_default_config_dir, get_default_data_dir,
  xdg_config_dir, xdg_data_dir, home_config_dir, get_layout_dir,
  get_theme_dir from setup.rs (now in home.rs)

* Restore quoted paths in setup --check and org qualifier on non-Windows

Restore quotation marks around paths in setup --check output, and keep
the "org", "Zellij Contributors" qualifier in ProjectDirs on non-Windows
platforms to preserve existing config directory locations.

---------

Co-authored-by: divens <[email protected]>
2026-03-19 08:54:48 +01:00
divensanddivens c4a572a884 Add Windows MSI installer with WiX v6 (#4847)
* Add Windows MSI installer with WiX v6

Build a dual-scope MSI installer that lets users choose per-user
(%LocalAppData%\Zellij) or per-machine (Program Files\Zellij) install,
with automatic PATH configuration and clean uninstall support.

- Custom UI based on stock WixToolset.UI.wixext dialogs to work around
  the long-standing WixUI_Advanced dual-scope bug (wixtoolset/issues#7137)
- Custom folder resolution CAs (SetFolderPerUser/SetFolderPerMachine)
- Embed application icon into zellij.exe via embed-resource
- Icon shown in Add/Remove Programs via ARPPRODUCTICON
- MSI build integrated into release.yml CI workflow

* Add MSI installer to no-web build and fix artifact naming

- Add MSI build steps to build-release-noweb job
- Add SHA256 checksums for MSI artifacts
- Pin WixToolset.UI.wixext to 6.0.2 to avoid v7 RC incompatibility
- Use -installer suffix after target in artifact names

---------

Co-authored-by: divens <[email protected]>
2026-03-16 12:38:38 +01:00
15571f8083 Fix debug-mode stack overflow on Windows (#4843)
* Fix debug-mode stack overflow on Windows

The clap-derived augment_subcommands for CliAction (~70 variants)
produces a >1 MB stack frame in debug mode, overflowing the Windows
default 1 MB main-thread stack. Increase it to 8 MB via linker flag
to match Linux. Only applies to debug builds; release optimizes the
frame down and doesn't need it.

* move file and include dev-opt

---------

Co-authored-by: divens <[email protected]>
Co-authored-by: Aram Drevekenin <[email protected]>
2026-03-12 16:54:27 +01:00
divensanddivens 47f4a2f5a9 fix: restore console input mode on exit to prevent mouse sequence leak (#4829)
* fix: restore console input mode on exit to prevent mouse sequence leak

On Windows with WezTerm (VT input path), enable_vt_input() sets
ENABLE_MOUSE_INPUT and ENABLE_VIRTUAL_TERMINAL_INPUT on the console
handle. crossterm's disable_raw_mode() only ORs back LINE_INPUT,
ECHO_INPUT and PROCESSED_INPUT — it never clears those flags.

After Zellij exits, ConPTY continues delivering mouse events as VT
escape sequences (urxvt mode 1015) into the shell's stdin, causing
visible garbage like [555;99;32M at the prompt.

Fix: save the original console mode before enable_vt_input() modifies
it, and restore it on all exit paths via a new restore_console_mode()
method on the ClientOsApi trait. Also move disable_mouse() before
unset_raw_mode() on error/panic paths for correct ordering.

* Move Windows VT input state management to stdin_handler_windows.rs

Move enable_vt_input, restore_vt_input, ORIGINAL_CONSOLE_MODE, and
related windows-sys imports from the shared stdin_handler.rs into
stdin_handler_windows.rs to keep platform-specific code in _windows
files per project convention.

---------

Co-authored-by: divens <[email protected]>
2026-03-12 10:15:01 +01:00
divensanddivens f6bf1af1ce [Windows port PR8] feature: add Windows support (#4768)
* feat: add Windows signal and process implementations

- AsyncSignalListener: polls crossterm size at 100ms, tokio ctrl_c/break/close
- BlockingSignalIterator: SetConsoleCtrlHandler + AtomicBool, polls size at 50ms
- signal_process: GenerateConsoleCtrlEvent for Interrupt, TerminateProcess for Kill
- Add windows-sys workspace dependency

* feat: add Windows IPC support with named pipes and marker files

- ipc_connect/ipc_bind/ipc_bind_async helpers with cfg gates for named pipes
- Dual-pipe architecture on Windows: main pipe + reply pipe per connection to
  prevent IPC deadlock (half-duplex named pipe limitation)
- is_ipc_socket returns is_file() on non-Unix for marker file discovery
- ipc_bind creates empty marker file after binding named pipe on Windows
- assert_socket split: cfg(unix) probes with ConnStatus, cfg(not(unix)) returns
  true to avoid deadlocking the server accept loop
- Replace tokio UnixListener/UnixStream with interprocess tokio Listener/Stream
  in web server IPC for cross-platform support
- Add interprocess tokio feature to workspace deps

* feat: add Windows client support for input, server spawn, and stack size

- Two-mode Windows input: crossterm event reader for native console,
  termwiz byte parser for terminal emulators (VT input mode)
- cast_crossterm_key: convert crossterm KeyEvent to KeyWithModifier
- ENABLE_VIRTUAL_TERMINAL_INPUT on Windows stdin for raw VT byte reading
- Skip ANSI terminal query on Windows to unblock startup
- spawn_server: CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP on Windows
- 8MB stack trampoline in main() for Windows (default 1MB overflows)
- 8MB stack for server worker threads (WASM compilation needs it)
- Enable crossterm events and bracketed-paste features

* test: make tests cross-platform for Windows

- Socket tests: cross-platform IpcGuard/IpcName helpers with GenericNamespaced
  on Windows vs GenericFilePath/TempDir on Unix
- Layout tests: normalize_layout_debug() replaces backslash separators in Debug
  output on Windows; gate env_var_expansion test with cfg(unix)
- Setup tests: use PathBuf::join + display() instead of hardcoded / separators
- Server OS tests: cross-platform command helpers (long_running_cmd, echo_cmd,
  stdin_reader_cmd) with CREATE_NO_WINDOW on Windows
- zellij_exports: replace GenericFilePath with ipc_connect() helper

* ci: add Windows build and test jobs with debug logging

- Add build-windows job: cargo check --no-default-features on windows-latest
- Add test-windows job: cargo test --no-default-features for utils/server/client
- Enable manual workflow_dispatch trigger
- Add debug log::info! calls in server startup, plugin loading, PTY spawning,
  and screen layout application for troubleshooting

* feat: implement Windows stubs for shell, command hooks, and process listing

Replace unimplemented!() stubs with working Windows implementations:
- get_default_shell: reads $COMSPEC, falls back to cmd.exe
- run_command_hook: uses cmd /C with RESURRECT_COMMAND env var
- get_all_cmds_by_ppid: uses sysinfo to enumerate processes by parent PID

* feat: implement Windows daemonize_web_server with detached process spawning

Replace the `unimplemented!()` stub with a proper background process
spawner that mirrors the existing `spawn_server` pattern: launch a
foreground `zellij web --start` child with CREATE_NO_WINDOW |
CREATE_NEW_PROCESS_GROUP, poll the TCP port to confirm it's listening,
then exit the parent process.

* feat: implement native ConPTY backend for Windows PTY

Replace all WindowsPtyBackend stubs with a full ConPTY implementation:

- CreatePseudoConsole with overlapped named pipes for IOCP-based async
  I/O (zero extra threads on the read path per pane)
- CreateProcessW with PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE and proper
  UTF-16 command line quoting / environment block construction
- ConPtyAsyncReader with lazy promotion from OwnedHandle to tokio
  NamedPipeServer (reactor not available at spawn time)
- Per-terminal exit monitoring thread using WaitForSingleObject
- ResizePseudoConsole for terminal resize, WriteFile for stdin,
  TerminateProcess / GenerateConsoleCtrlEvent for signals

Note: for PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, the HPCON value must be
passed directly as lpValue (not a pointer to it), matching the C
convention where HPCON is void*. In Rust (windows-sys) HPCON is isize,
requiring an explicit cast. See microsoft/terminal#6705.

* chore: remove debug logging from ConPTY backend

* fix: resolve Windows plugin directory and WASI mount errors

Two Windows-specific fixes for the plugin system:

1. Replace colons with underscores in URL-derived plugin directory names
   (e.g. "zellij:tab-bar" -> "zellij_tab-bar"). Colons are illegal in
   Windows path components, causing OS error 123 on plugin dir creation.

2. Use FILE_FLAG_BACKUP_SEMANTICS when opening directories for WASI
   pre-opened mounts. Windows requires this flag to open directory
   handles, otherwise File::open() returns OS error 5 (access denied).

Both fixes are behind #[cfg(windows)] — Unix behavior is unchanged.

* fix: enable mouse support on Windows native console path

Mouse events were silently dropped in the crossterm event loop, and
enable_mouse() wrote VT escape sequences that don't set the required
ENABLE_MOUSE_INPUT console mode flag. Add from_crossterm_mouse()
converter, InputInstruction::MouseEvent variant, and use crossterm's
EnableMouseCapture/DisableMouseCapture on Windows.

* fix: Windows get_default_shell() now checks $SHELL before $COMSPEC

* fix: resolve bare command names via PATH and PATHEXT on Windows

CreateProcessW doesn't reliably resolve bare command names (e.g.
"pwsh") through PATH the way Unix execvp does. Additionally, the
existing command_exists() check didn't consider Windows PATHEXT
extensions (.exe, .cmd, .bat, etc.).

Add find_executable() and resolve_command() which search PATH with
PATHEXT support on Windows, and use the resolved absolute path when
spawning via CreateProcessW.

* chore: run cargo fmt --all

* fix: canonicalize expected paths in setup tests for Windows \\?\ prefix

* fix: Windows CI test failures in spawn_and_read_output and snapshot test

Use cmd /K instead of /C in spawn test so ConPTY has time to flush
output before the pipe closes. Normalize backslashes in snapshot test
to handle Windows path separators.

* fix: strip redundant Shift and AltGr modifiers from Windows key events

On the Windows native console path, crossterm reports physical modifier
flags alongside the already-translated character. For example on French
AZERTY, Shift+ù produces Char('%') with SHIFT, and AltGr+_ produces
Char('\') with CTRL+ALT. These extra modifiers cause keybinding
mismatches and prevent modified characters from being typed in plugin
text inputs.

Strip Shift for all Char events and Ctrl+Alt (AltGr) when the character
is printable, matching Unix terminal behavior where only the resulting
character is reported. Also fix raw byte synthesis for AltGr characters
to emit plain UTF-8 instead of ESC-prefixed sequences.

* fix: detect and clean up stale Windows sessions via PID-based liveness check

Write server PID to marker files instead of creating empty files.
assert_socket() on Windows now reads the PID and checks process liveness
via OpenProcess, cleaning up stale marker files for dead sessions.

* fix: connect reply pipe on Windows for kill-session and delete-session

On Windows the server blocks on reply_listener.accept() after accepting
the main pipe connection. Without a reply pipe connection, the route
thread is never spawned and the KillSession message is never processed.

* fix: wait for server Exit response in kill/delete session on Windows

The previous fire-and-forget approach was unreliable: the client would
disconnect before the server's route thread had time to read and process
the KillSession message. Now we wait for the Exit response on the reply
pipe, ensuring the server has fully processed the kill before we return.

* fix: resolve Windows console mode race between VT input and mouse setup

Two threads race over SetConsoleMode on the stdin console handle:
- stdin_handler calls enable_vt_input() (read-modify-write)
- input_handler calls enable_mouse() via crossterm's EnableMouseCapture
  (full SetConsoleMode overwrite to 0x0098)

Depending on ordering, crossterm could clobber ENABLE_VIRTUAL_TERMINAL_INPUT
(breaking the VT byte reader), or enable_vt_input could preserve
ENABLE_QUICK_EDIT_MODE (intercepting mouse events at the console level).

Fix enable_vt_input() to explicitly set the exact console mode needed
(0x0298) instead of read-modify-write, clearing QUICK_EDIT_MODE and other
line-editing flags.

Fix enable_mouse()/disable_mouse() on the VT path (TERM is set) to use
ANSI escape sequences instead of crossterm's Console API, avoiding the
competing SetConsoleMode call entirely. Enable ENABLE_VIRTUAL_TERMINAL_PROCESSING
on stdout first so ConPTY forwards the DEC private mode sequences to the
terminal emulator.

* fix: session-manager plugin kill session on Windows dual-pipe IPC

The plugin's kill_sessions() only connected the main pipe and sent
KillSession, but on Windows the server blocks on reply_listener.accept()
before spawning the route thread. Without connecting the reply pipe the
message was never processed.

Add the same dual-pipe handling as the CLI kill_session(): connect the
reply pipe first, send the message, then wait for the Exit response.

* fix(windows): replace resize polling with event forwarding from stdin thread

Forward crossterm Event::Resize from the stdin thread to the signal
handler thread via an mpsc channel, replacing the 50ms polling loop.
This makes terminal resizing much more responsive during mouse-driven
resizes and eliminates wasteful polling when idle.

When the VT reader path is active (terminal emulators with TERM set),
the sender is dropped, causing the signal handler to automatically
fall back to size-polling — preserving existing behavior for that path.

Unix is unaffected: handle_signals receives None for the receiver and
BlockingSignalIterator continues using signal_hook as before.

* fix(windows): enable ANSI terminal queries on the VT reader path

The startup ANSI query mechanism (pixel dimensions, colors, sync output)
was blanket-skipped on Windows. The actual limitation is specific to the
native console path, where crossterm's event::read() uses
ReadConsoleInput (INPUT_RECORDs) and never reaches the byte-reader loop
that parses ANSI responses.

On the VT reader path (terminal emulators like Alacritty with TERM set),
fill_buf() reads raw VT bytes via ReadFile — same as Unix — so ANSI
query responses work normally. Move the use_vt_reader decision earlier
and use it to gate the query, enabling it for the VT reader path while
still skipping it on native console.

* fix: handle early TerminalResize before client is fully registered

On Windows, enable_mouse()'s SetConsoleMode call can generate a
WINDOW_BUFFER_SIZE_EVENT that arrives as a resize before the server
finishes processing the initial client connection. At that point
set_client_data() hasn't been called yet, so set_client_size() silently
no-ops on the None entry and min_client_terminal_size() returns None.

Instead of panicking, skip the resize when the minimum size can't be
determined — the server will query the terminal size once setup
completes.

* fix: remove unnecessary custom stack size

Early versions of zelilj windows port crashed with stack overflow.
Increasing stack size allowed proper startup and moving further with the
porting effort. Current implementation no longer suffers from stack
issues, so we can revert back to default stack size.

* fix(windows): re-enable Ctrl+C handling inherited by ConPTY children

The server is spawned with CREATE_NEW_PROCESS_GROUP, which disables
Ctrl+C for the process.  Children inherit this disabled state, so
ConPTY child processes (shells, ping, etc.) silently ignore
CTRL_C_EVENT signals generated by the pseudo-console.

Call SetConsoleCtrlHandler(NULL, FALSE) early in start_server() to
clear the inherited ignore flag before any ConPTY children are created.

* refactor(windows): move inline cfg(windows) blocks to platform files

Break out inline #[cfg(windows)] blocks from shared client code into
the existing _unix.rs / _windows.rs platform files, per upstream
feedback.

- Split spawn_server into two #[cfg]-gated function definitions
- Extract setup_ipc, enable_mouse_support, disable_mouse_support into
  os_input_output_unix.rs and os_input_output_windows.rs
- Unify BlockingSignalIterator::new() signature across platforms
- Extract native console stdin loop into stdin_handler_windows.rs

* fix: remove sporadic info log statements

* ci(windows): add Windows x64 release builds and upgrade CI

- Add Windows x64 target to release.yml for both normal and no-web
  release matrices, with NASM, static CRT, .zip packaging, and
  PowerShell checksum steps
- Upgrade build-windows in rust.yml from cargo check to cargo xtask
  build, add wasm32-wasip1 target and NASM
- Add build_release() function and platform-aware e2e_build() to
  xtask/src/ci.rs
- Add BuildRelease command definition to xtask/src/flags.rs

* refactor(client): make resize channel unconditional across platforms

Create the resize mpsc channel on all platforms instead of gating it
behind #[cfg(windows)], avoiding conditionally-existing variables that
are hard to follow. On Unix the sender is immediately dropped and the
receiver is passed as None-equivalent to handle_signals.

* refactor(windows): restructure signal iterator with if/else for channel vs poll

Consolidate the quit check at the top of a single loop and use if/else
for channel mode (native console) vs poll mode (VT reader). On channel
disconnect, set resize_receiver to None and continue into the else
branch instead of falling through.

* docs(server): clarify comment about early resize before client init

Explain why min_client_terminal_size() can return None even after
set_client_size(): new_client() inserts None, and set_client_size()
is a no-op on None entries since it uses as_mut().map().

* feat(web): add --server-startup-timeout flag for web server

Make the hardcoded 10-second TCP-poll timeout in the non-Unix
daemonize_web_server configurable via a CLI flag, defaulting to 10s.

* fix(windows): use unique pipe names for ConPTY output on re-spawn

When a held pane re-runs a command or drops to shell, do_spawn tried
to create a named pipe with the same name as the previous spawn.  The
old ConPtyAsyncReader still held the read handle, so CreateNamedPipeW
failed (FILE_FLAG_FIRST_PIPE_INSTANCE, max instances = 1) and the
error was silently swallowed, leaving the pane blank.

Add a monotonic AtomicU64 counter to the pipe name so each spawn gets
a globally unique pipe, regardless of whether the previous one has
been cleaned up yet.

* ci(windows): enable static CRT for regular Windows release build

The no-web Windows build already had RUSTFLAGS="-C target-feature=+crt-static"
but the regular build was missing it, resulting in a dynamic CRT dependency.

* docs(web): clarify --server-startup-timeout is Windows-only

---------

Co-authored-by: divens <[email protected]>
2026-03-04 07:29:16 +01:00
divensanddivens c80786f4ff [Windows port PR7] refactor: upgrade sysinfo and add cross-compilation proof (#4759)
* refactor: platform-abstract path constants

Add #[cfg(not(unix))] counterpart to unix_only module in consts.rs so
path constants compile on non-Unix platforms. Move nix dependency to
cfg(unix)-only in both zellij-utils and root crate.

* refactor: Windows cross-compilation proof

Enable all 5 core crates to compile for x86_64-pc-windows-gnu by fixing
dependency issues and upgrading sysinfo. This validates that PRs 1.1–7.1
correctly gated all Unix-specific code behind cfg attributes.

- Enable crossterm "windows" feature in workspace deps (no-op on non-Windows)
- Remove dead mio dependency from zellij-client (zero imports)
- Add Windows cross-compilation linker config to .cargo/config.toml
- Upgrade sysinfo 0.22.5 → 0.33 (drops ntapi v0.3.7 packed struct UB)
  - Remove deprecated ProcessExt/SystemExt trait imports
  - Pid::from(i32) → Pid::from_u32(u32)
  - process.cwd() now returns Option<&Path>
  - process.cmd() now returns &[OsString]
  - Use precise ProcessRefreshKind (nothing + with_cwd/with_cmd)

---------

Co-authored-by: divens <[email protected]>
2026-02-23 16:40:51 +01:00
divensanddivens b3b4aa8ccb [Windows port PR6] refactor: gate daemonization and shell-related functions for cross-platform support (#4746)
* refactor: abstract daemonization behind cfg gates

Gate Unix-only daemonize/nix usage with #[cfg(unix)], add non-Unix
foreground fallback for web server, remove unused libc dep from both
crates, and move daemonize/nix to cfg(unix) dependencies.

* refactor: abstract shell invocation and process listing

Gate get_default_shell() with cfg(not(windows)) and add a Windows stub.
Split run_command_hook() with cfg(not(windows)) for the sh -c path and
a Windows stub. Add cfg(unix) gate on the ps-based
get_all_cmds_by_ppid() with a non-Unix stub.

---------

Co-authored-by: divens <[email protected]>
2026-02-23 08:59:52 +01:00
divensanddivens c5ba0dee80 [Windows port PR5] refactor: upgrade interprocess to v2+ and abstract IPC for cross-platform support (#4740)
* refactor: upgrade interprocess to v2+ with cross-platform IPC support

Replace interprocess 1.2.1 with 2.2+ which provides cross-platform
local socket support (Unix domain sockets on Unix, named pipes on
Windows). Migrate all call sites from the flat v1 API to the v2
enum-dispatch API, and replace nix::unistd::dup fd-duplication with
interprocess::TryClone::try_clone().

* refactor: abstract FD duplication in IPC behind IpcStream trait

Decouple IPC structs from the concrete transport by introducing an
IpcStream trait that abstracts read/write/clone. Internal fields change
from BufWriter<LocalSocketStream> to BufWriter<Box<dyn IpcStream>>,
while the public new() constructors still accept LocalSocketStream so
no callers need updating.

* refactor: abstract session discovery behind is_ipc_socket() helper

Replace all 6 direct uses of std::os::unix::fs::FileTypeExt::is_socket()
with a cross-platform is_ipc_socket() helper in consts.rs. On Unix it
delegates to FileTypeExt::is_socket(); on non-Unix it returns false as a
placeholder for platform-specific session discovery added later.

---------

Co-authored-by: divens <[email protected]>
2026-02-21 08:58:37 +01:00
divensanddivens 523fda9f63 refactor: split client signal handling into platform files (#4738)
Extract signal handling from os_input_output.rs into platform-specific
files, mirroring the server-side architecture from PR3:

- os_input_output_unix.rs: AsyncSignalListener (tokio unix signals),
  BlockingSignalIterator (signal_hook)
- os_input_output_windows.rs: stubs (unimplemented)
- os_input_output.rs: shared SignalEvent enum, AsyncSignals trait,
  handle_signals() now uses BlockingSignalIterator + SignalEvent

Moved signal-hook to cfg(unix)-only dependency in zellij-client.

Co-authored-by: divens <[email protected]>
2026-02-20 16:25:01 +01:00
divensanddivens 767328df15 [Windows port PR3] refactor: replace RawFd/AsyncFile, introduce platform-split PTY backend (#4722)
* refactor: replace RawFd with i32 in debug_to_file

Replace the Unix-specific RawFd type with plain i32 in the debug_to_file
infrastructure. This removes the last RawFd usage from the cross-crate
debug logging API, making it platform-independent.

* refactor: cross-platform PTY abstraction with AsyncFd

Replace portable-pty with native openpty and introduce a platform-split
architecture for PTY operations:

- os_input_output_unix.rs: UnixPtyBackend with native openpty, AsyncFd-based
  epoll reader (no spawn_blocking per pane), signal handling via nix
- os_input_output_windows.rs: WindowsPtyBackend stubs (unimplemented)
- os_input_output.rs: shared ServerOsApi trait, ServerOsInputOutput composes
  PtyBackendImpl, build_command() extracts TerminalAction→RunCommand logic

Key changes:
- AsyncFd<File> replaces both tokio::fs::File (upstream) and spawn_blocking
  SyncReadAsyncReader (previous port) for zero-overhead epoll PTY reads
- orig_termios restored: openpty(None, &orig_termios) passes parent's VERASE
- ServerOsApi returns Box<dyn AsyncReader> instead of Box<dyn Read + Send>
- TerminalBytes::new takes Box<dyn AsyncReader> directly
- NullAsyncReader replaces std::io::empty() for held panes
- Platform modules gated with #[cfg(not(windows))] / #[cfg(windows)]
- Removed portable-pty dependency, restored close_fds
- Added tokio "net" feature for AsyncFd

---------

Co-authored-by: divens <[email protected]>
2026-02-20 12:55:46 +01:00
divensanddivens 36fa696c1a [Windows port PR2] Replace client-side termios/ioctl with crossterm for raw mode and terminal size (#4721)
* refactor: replace nix::Error with std::io::Error in public APIs

* refactor: replace client-side termios/ioctl with crossterm

---------

Co-authored-by: divens <[email protected]>
2026-02-18 15:07:19 +01:00
divensanddivens 9d9af7a3a4 refactor: replace nix::Error with std::io::Error in public APIs (#4720)
Co-authored-by: divens <[email protected]>
2026-02-18 13:56:47 +01:00