A native Windows terminal multiplexer — a tmux clone built on the Windows ConPTY (Pseudo Console) API, with no WSL/Cygwin/MSYS2 dependency.
Early development. Milestones (see the full plan for detail):
- Phase 0 — scaffolding + ConPTY passthrough (spawn a shell, stream I/O)
- Phase 1 — terminal emulation via libvterm (per-pane screen grid + renderer)
- Phase 2 — multiple panes + binary layout tree + prefix key + compositor
- Phase 3 — client/server split over Named Pipes (detach / attach)
- Phase 4 — windows (tabs) + status bar (window list + clock)
- Phase 5 — scrollback + vi-style copy mode + clipboard
- Phase 6 — command system + key bindings + config file
- Phase 7 — layout presets · pane resize · zoom · swap/rotate · mouse
Prebuilt Windows x64 downloads are attached to each Release:
- Setup wizard —
tmuxw-<version>-setup.exe. A GUI installer with a license page, install-location picker, and an option to add tmuxw to yourPATH. It installs both command names,tmuxandtmuxw, and includes a clean uninstaller. - Portable —
tmuxw-<version>-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(bothtmuxandtmuxwcommands).
- 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)
This repo vendors libvterm as a git submodule, so clone recursively:
git clone --recursive <repo-url>
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
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 <name> / -S <path> 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.
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 <session> 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 | 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.
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 <c> |
jump to next / previous char c |
t / T <c> |
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 |
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 <name>, 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 <name>,
rename-session <name>, copy-mode, detach-client, send-prefix,
send-keys <keys...>, display-panes, choose-window, display-message <text>,
run-shell <cmd>, if-shell <cond> <then> [else], clock-mode,
command-prompt, set <option> <value>,
bind <key> <command>, unbind <key>, source-file <path>,
list-windows (alias lsw), list-panes (alias lsp),
list-keys (alias lsk), show-options (aliases show-window-options, show),
list-buffers (alias lsb), show-buffer [-b name], set-buffer [-b name] <text>,
delete-buffer [-b name], load-buffer [-b name] <path>, save-buffer [-b name] <path>,
capture-pane (alias capturep), clear-history (alias clearhist),
confirm-before -p <message> <command> (alias confirm),
respawn-pane, respawn-window,
pipe-pane [-o] [<shell-command>], list-commands (alias lscm),
list-clients (alias lsc),
set-environment [-u] <name> [value], show-environment, unset-environment <name>,
choose-buffer, choose-client, set-hook [-u] <event> <command>, show-hooks,
wait-for [-S|-L|-U] <channel>, switch-client -t <session>, suspend-client,
refresh-client, resize-window [-x width] [-y height],
lock-session / lock-client / lock-server / lock.
set-environment overrides apply to panes created from then on (new-window,
split-window), not retroactively to already-running ones, matching tmux.
Hooks fired automatically by this build: window-linked (new-window),
pane-died, client-attached, client-detached. set-hook/show-hooks
accept and store any event name (matching tmux), but only those four ever
fire on their own.
wait-for (no flag) blocks the caller until wait-for -S <channel> is run
elsewhere against the same session — the server itself never blocks (that
would freeze the session for anyone attached to it), so the CLI polls
underneath.
switch-client -t <session> reconnects an attached client to a different
session's server in place, without detaching first. lock-* locks the
Windows workstation (LockWorkStation) — broader than tmux's per-terminal
lock, but the same practical intent. Global -2 / -8 / -u / -q are
accepted (for script/shebang compatibility) and are no-ops: color depth and
encoding are whatever the terminal's own VT support is, since tmuxw passes
escape sequences straight through rather than reinterpreting them.
link-window inserts a second reference to the same window at another
index, within one session: both indices show the exact same live window,
so a rename (or anything else) via either index is visible via the other,
and unlink-window on one index leaves the other fully intact.
Paste buffers are a small internal stack (newest on top), separate from but
kept in sync with the Windows clipboard: copy-mode yanks and capture-pane
push a new buffer; paste-buffer prefers the top of that stack and falls
back to the clipboard so a plain OS-level copy still pastes as before.
Multiple commands can be chained on one line (in a binding, config, or prompt)
with a standalone ;, e.g. bind S "split-window -v ; select-pane -D".
bind -r <key> <command> makes a binding repeatable: for a short window after
it runs you can press the key again without the prefix (handy for bind -r H resize-pane -L).
Layout presets for select-layout: even-horizontal, even-vertical,
main-horizontal, main-vertical, tiled (also cycled with Ctrl-B Space).
Options for set: prefix <key>, status on|off, mouse on|off,
base-index <n> (first window number), pane-base-index <n>,
status-left <fmt>, status-right <fmt>. With mouse on, click a pane to
select it, drag a divider to resize, and use the scroll wheel to enter copy mode
and scroll back.
Status formats expand #S (session), #W/#I (current window name/index),
#H (host), ## (literal #), and %H %M %S %Y %m %d (time). Defaults:
status-left "[#S] ", status-right "%H:%M".
Useful without an interactive terminal (also run in CI):
build\tmuxw.exe --selftest [cmd] REM ConPTY output straight to stdout
build\tmuxw.exe --selftest-render [cmd] REM ConPTY -> libvterm -> print grid
build\tmuxw.exe --selftest-split REM two panes + layout + compositor
build\tmuxw.exe --selftest-ipc REM full server/client round trip
build\tmuxw.exe --selftest-cmdipc REM one-off commands against a detached session
build\tmuxw.exe --selftest-attachd REM attach -d kicks an already-attached client
build\tmuxw.exe --selftest-switch REM switch-client sends MSG_SWITCH to the right target
build\tmuxw.exe --selftest-link REM link-window shares one window across two indices
build\tmuxw.exe --selftest-confirm REM confirm-before y/n gate + capture-pane/clear-history/previous-layout
build\tmuxw.exe --selftest-windows REM window (tab) switching + status bar
build\tmuxw.exe --selftest-copymode REM enter/exit copy mode via the session
build\tmuxw.exe --selftest-cmd REM command system + bindings + config
build\tmuxw.exe --selftest-mouse REM mouse reporting + SGR parse + actions
ctest --test-dir build REM emu, layout, ipc, status, copymode, cmd
tmuxw is released under the MIT License. Bundled third-party components and their licenses are listed in THIRD-PARTY-NOTICES.md.