tmuxw
A native Windows terminal multiplexer — a tmux clone built on the Windows
ConPTY (Pseudo Console) API, with no WSL/Cygwin/MSYS2 dependency.
Status
Early development. Milestones (see the full plan for detail):
Install
Prebuilt Windows x64 downloads are attached to each
Release:
- Setup wizard —
tmuxw--setup.exe. A GUI installer with a license
page, install-location picker, and an option to add tmuxw to your PATH. It
installs both command names, tmux and tmuxw, and includes a clean
uninstaller.
- Portable —
tmuxw--win-x64.zip (or the bare .exe). No install;
unzip and run. Statically linked, so no VC++ redistributable is required.
- winget —
winget install Turin.tmuxw (both tmux and tmuxw commands).
Requirements
- Windows 10 1809+ (build 17763) or Windows 11 — for the ConPTY API
- To build from source: Visual Studio 2022/2026 with the C++ toolset (MSVC,
CMake, Ninja)
Build
This repo vendors libvterm as a git
submodule, so clone recursively:
git clone --recursive
REM or, in an existing clone:
git submodule update --init --recursive
Then build with the bundled VS toolchain:
scripts\build.bat REM Debug build -> build\tmuxw.exe
scripts\build.bat Release
Or manually from a Developer prompt:
cmake -S . -B build -G Ninja
cmake --build build
Run
build\tmuxw.exe REM auto-detected shell (pwsh > powershell > cmd)
build\tmuxw.exe cmd.exe REM a specific command line
Running tmuxw starts a background server (if one isn't already running)
that owns the session's panes, and attaches this terminal as a thin client.
The server survives detach: Ctrl-B d leaves the client and keeps the shells
running; run tmuxw again to reattach. Each pane runs a shell inside a pseudo
console; output is parsed by libvterm into a cell grid, composited by the server,
and streamed to the client. Exit the last pane to end the session.
Once installed (or with build\ on your PATH), the familiar tmux name works
too — tmux and tmuxw are the same binary:
tmux REM attach to (or start) the default session
tmux new REM start a session and attach (alias: new-session)
tmux new -s work REM start/attach a named session
tmux new -d -s work REM start a session without attaching (background it)
tmux new -s work -c C:\proj REM start a session with a given starting directory
tmux new -s work -x 200 -y 50 REM start a session at a given initial size
tmux -f other.conf new -s work REM start a session, loading an alternate config file
tmux -L work new -s build REM start a session in an independent namespace
tmux attach -t work REM attach to an existing session (alias: a)
tmux attach -d -t work REM attach, kicking any client already attached to it
tmux has-session -t work REM exit 0 if that session's server is running (alias: has)
tmux ls REM list running sessions (alias: list-sessions)
tmux kill-session -t work REM stop one session's server
tmux kill-session -t work -a REM stop every OTHER session's server
tmux kill-server REM stop every tmuxw session
tmux -V REM print the version (alias: --version)
tmuxw --standalone REM run in one process, no server (for debugging)
-L / -S put every session created or targeted by that
invocation in an independent namespace, so -L work and the default
namespace never collide even if they use the same session names. Windows
named pipes don't have a real notion of a "socket path" the way -S implies
on Unix, so both flags do the same thing here: fold a token into the pipe
name.
Each named session is an independent background server, so sessions detach and
reattach separately and tmux ls enumerates them. A session's server keeps
running independently of the terminal that started it — including across an
SSH disconnect, which on Windows normally tears down everything in the login
session's process tree; the server escapes that so reconnecting finds your
panes and shells exactly as you left them.
Scripting: run a command without attaching
Any subcommand that isn't one of the ones above (send-keys, new-window,
split-window, rename-session, kill-window, set, ...) is forwarded to a
session's server as a single in-session command line — whether or not
anything is attached to it, so it works against a session started with
new -d:
tmux send-keys -t work "make test" Enter REM type a command into the session
tmux new-window -t work REM open a window in it
tmux rename-session -t work built REM rename it
tmux list-windows -t work REM list windows (alias: lsw)
tmux list-panes -t work REM list panes in the current window (alias: lsp)
A -t pair immediately after the subcommand name picks the
session (default session otherwise); it's only recognized in that position,
since several in-session commands (select-window -t, select-pane -t, ...)
use -t for their own window/pane-local targeting.
Key bindings (prefix = Ctrl-B)
| Key | Action |
|---|
Ctrl-B % | split active pane left | right |
Ctrl-B " | split active pane top / bottom |
Ctrl-B o | select next pane |
Ctrl-B ←↑↓→ | select pane by direction |
Ctrl-B Ctrl-←↑↓→ | resize active pane (1 cell) |
Ctrl-B Alt-←↑↓→ | resize active pane (5 cells) |
Ctrl-B q | show pane numbers (digit selects) |
Ctrl-B w | choose window from a list (j/k, Enter) |
Ctrl-B t | big-clock mode (any key exits) |
Ctrl-B z | zoom / unzoom active pane |
Ctrl-B Space | cycle layout preset |
Ctrl-B { / } | swap pane with prev / next |
Ctrl-B Ctrl-O | rotate panes |
Ctrl-B ; | select last (previously active) pane |
Ctrl-B l | select last window |
Ctrl-B ! | break active pane into a new window |
Ctrl-B ] | paste the clipboard into the active pane |
Ctrl-B x | kill active pane |
Ctrl-B c | create a new window (tab) |
Ctrl-B n / p | next / previous window |
Ctrl-B 0–9 | select window by number |
Ctrl-B & | kill the current window |
Ctrl-B [ | enter copy mode (scrollback) |
Ctrl-B : | command prompt |
Ctrl-B d | detach (server keeps running) |
Ctrl-B Ctrl-B | send a literal Ctrl-B |
Bindings are configurable — see the config file below.
A status bar along the bottom row shows the session name, the window list (the
current window marked *), and a clock.
Copy mode (Ctrl-B [)
Scroll back through a pane's history and copy text to the Windows clipboard:
| Key | Action |
|---|
↑↓←→ / h j k l | move the cursor |
w / b / e | next / previous / end of word |
f / F `` | jump to next / previous char c |
t / T `` | jump just before next / previous c |
0 / ^ / $ | line start / first word / line end |
PageUp / PageDown | scroll a full page |
Ctrl-U / Ctrl-D | scroll a half page |
H / M / L | top / middle / bottom of the viewport |
g / G | jump to top / bottom of history |
/ / ? | search forward / backward |
n / N | repeat search, same / opposite way |
Space / v | start / clear a characterwise selection |
V | linewise selection |
Ctrl-V | rectangle (block) selection |
Enter or y | copy the selection and exit |
q / Esc | cancel |
Configuration
On startup the server reads %USERPROFILE%\.tmuxw.conf (if present) — a list of
commands, one per line (# starts a comment). The same commands work at the
command prompt (Ctrl-B :). See tmuxw.conf.example.
set prefix C-a # change the prefix key
set status on # show/hide the status bar
bind | split-window -h # bind a key to a command
unbind '"'
Commands: new-window, split-window [-h|-v], select-pane [-U|-D|-L|-R|-t N],
resize-pane [-U|-D|-L|-R [n]] [-Z], select-layout , next-layout,
previous-layout,
rotate-window [-U], swap-pane [-U|-D], break-pane, join-pane -s N [-h|-v],
paste-buffer [-b name],
last-pane, last-window, kill-pane [-a], next-window, previous-window,
select-window -t N, swap-window [-s a] -t b, move-window -t N,
kill-window,
link-window [-s src] [-t dst], unlink-window [-t idx],
rename-window ,
rename-session , copy-mode, detach-client, send-prefix,
send-keys , display-panes, choose-window, display-message ,
run-shell , if-shell [else], clock-mode,
command-prompt, `set