Add native tmux control mode and HTM integration - #20639
Add native tmux control mode and HTM integration#20639Jason Gauci (MisterTea) wants to merge 6 commits into
Conversation
Windows Terminal has no Hyper-style plugin API, so JSON fragments cannot intercept splits, new tabs, or pane close. Wrap every ConPTY in HtmLeaderConnection (the DebugTapConnection pattern) so typing `htm` in an existing profile can steal that PTY, apply INIT_STATE as follower panes with no local process, and map WT split/new-tab/close onto htmd. The wire protocol is unchanged from EternalTerminal and hyper-htm (ESC[###q / ESC[$$$q, 1-byte header + 8-char b64 length, SESSION_END is one byte). Feature_HtmIntegration is AlwaysEnabled in Dev and disabled for Release and WindowsInbox so the diff stays reviewable upstream. Optional profile env HTM_BIN_DIR is prepended to PATH so a local htm.exe/htmd.exe build is found without changing the default commandline. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the legacy framed HTM protocol handling with tmux -CC control-mode parsing, command replies, pane output routing, and DCS lifecycle handling. Route Windows Terminal split, tab, resize, keyboard, detach, and shutdown actions through HTM followers while preserving normal ConPTY behavior for non-HTM profiles. Synchronize pending follower assignment so asynchronous pane notifications cannot race tab creation. Add protocol helper coverage and a live concurrent htm/htmd stress test that exercises refresh-client, split-window, display-message, and clean server shutdown.
Synchronize leader and follower state shared between ConPTY output and UI action threads. Resolve new-window followers from the authoritative single-pane layout notification when needed, and route session-scoped new-tab actions without requiring a transient focused pane ID. This prevents split and tab actions from intermittently falling through or missing their initial resize during tmux control-mode operation.
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
check-spelling found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
This comment has been minimized.
This comment has been minimized.
@microsoft-github-policy-service agree [company="default"] |
|
@microsoft-github-policy-service agree |
Fixes check-spelling failures on PR 20639: - HtmProtocol.h Base64Decode valb renamed to valueBits - Add htmd, htmtst, Toolhelp, SNAPPROCESS, PROCESSENTRY, wcsicmp, wdupenv, daemonizes to expected spelling dictionary
|
/azp run |
|
Commenter does not have sufficient privileges for PR 20639 in repo microsoft/terminal |
Dustin L. Howett (DHowett)
left a comment
There was a problem hiding this comment.
It is curious how you have made line ending changes to only certain lines. It appears you have not run the code formatter.
Did you consult the existing open pull request that adds support for this feature to glean any information from it?
Do you have a video of it working properly?
I have end to end tests in the eternal terminal repo that test all of the features and also do stress-tests (e.g. spamming input & output across several panes/tabs simultaneously). This is how I was able to fix the issues in the Wezterm tmux implementation. I'll run the code formatter and post a video from my desktop ASAP. |
|
Jason Gauci (@MisterTea) What's different about your approach here, from the already open #18928? |
Close native HTM panes via ForceCloseUi plus hosting-page teardown, debounce resize storms, map new-tab/new-window correctly, fix UTF-16 surrogate input, and always tear down HTM followers even when closeOnExit is never. Co-authored-by: Cursor <cursoragent@cursor.com>
| { | ||
| if (_htmMode) | ||
| { | ||
| // Stateful conversion: KEYEVENTF_UNICODE may deliver one surrogate |
| if (_htmMode) | ||
| { | ||
| // Stateful conversion: KEYEVENTF_UNICODE may deliver one surrogate | ||
| // per WriteInput; til::u16u8 without state would emit CESU-8. |
| { | ||
| return; | ||
| } | ||
| // Stateful conversion: KEYEVENTF_UNICODE may deliver one surrogate |
| return; | ||
| } | ||
| // Stateful conversion: KEYEVENTF_UNICODE may deliver one surrogate | ||
| // per WriteInput; til::u16u8 without state would emit CESU-8. |
| std::string _paneId; | ||
| std::mutex _writeMutex; | ||
| bool _closed{ false }; | ||
| // SendInput KEYEVENTF_UNICODE delivers one UTF-16 code unit per call; |
| bool _suppressClosePacket{ false }; | ||
| bool _closed{ false }; | ||
| std::string _pendingOutput; | ||
| // SendInput KEYEVENTF_UNICODE delivers one UTF-16 code unit per call; |
| // bytes after DCS are ordinary newline-delimited tmux control records. | ||
| inline constexpr std::string_view TmuxControlDcs{ "\x1bP1000p" }; | ||
| inline constexpr std::string_view TmuxControlSt{ "\x1b\\" }; | ||
| // iTerm2's tmux -CC gateway banner. WezTerm prints the same text. |
| } | ||
| } | ||
|
|
||
| // KEYEVENTF_UNICODE may deliver one UTF-16 code unit per win32-input-mode |
check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.Unrecognized words (3)CESU These words are not needed and should be removedADDREFTo accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands... in a clone of the git@github.com:MisterTea/terminal.git repository curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/terminal/actions/runs/33892985100/attempts/1' &&
git commit -m 'Update check-spelling metadata'Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionaryThis includes both expected items (2035) from .github/actions/spelling/expect/alphabet.txt .github/actions/spelling/expect/expect.txt .github/actions/spelling/expect/web.txt and unrecognized words (3)
Consider adding to the "cspell:cpp/src/lang-keywords.txt",
"cspell:csharp/csharp.txt",
"cspell:cpp/src/compiler-clang-attributes.txt",
"cspell:aws/aws.txt",
"cspell:python/src/python/python.txt",
To stop checking additional dictionaries, put (in the "check_extra_dictionaries": []✏️ Contributor please read thisBy default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later. If the listed items are:
See the 🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉 If the flagged items are 🤯 false positivesIf items relate to a ...
|
|
Dustin L. Howett (@DHowett) Posted the videos Mike Griese (@zadjii-msft) To be honest, I didn't know there was a parallel effort so I can't comment on the differences. What I can say is that the wezterm tmux implementation had many issues, because multiplexing is fraught with race conditions and other challenges (see wezterm/wezterm#8125 ). My approach was to take iterm2 + tmux -CC as ground truth, create a ton of tests, record the console and mp4 videos of the windows for each tests, then use this ground truth to validate the other terminals. This allowed me to make https://github.com/MisterTea/hyper-htm robust enough that I could suggest people to use it, and allowed me to make this PR as well. |
Hey folks! I'm the creator & maintainer of Eternal Terminal, and I'm working on getting all terminal emulators to support the tmux -CC protocol, as part of releasing our own tmux -CC alternative, HTM. HTM uses the same protocol as tmux -CC but it's built around control mode and has a rigorous battery of tests involving >5 terminal emulators.
Summary
This change adds native tmux control mode (
tmux -CC) integration to Windows Terminal and uses it to support HTM as a terminal multiplexer.It:
-CCDCS/ST lifecycle emitted byhtm;Feature_HtmIntegration(enabled in Dev builds, disabled for Release and WindowsInbox).Why tmux
-CCNormal tmux renders a complete terminal UI inside one PTY. In control mode, tmux instead exposes a line-oriented protocol intended for terminal emulators. The emulator sends ordinary tmux commands such as
split-window,new-window,send-keys, andrefresh-client; tmux returns command transactions delimited by%beginand%end/%error, and publishes asynchronous events such as%output,%window-add, and%layout-change.With
-CC, tmux also wraps the control session in terminal-facing DCS/ST markers. That lets Windows Terminal detect that the foreground process has entered control mode, stop treating its output as a single rendered terminal surface, and represent the remote tmux windows and panes using native Windows Terminal tabs and splits.The protocol retains tmux's native identity model:
$sessionidentifies a session;@windowidentifies a window, represented as a Windows Terminal tab;%paneidentifies a pane, represented as a Windows Terminal pane.This avoids inventing a Windows-Terminal-specific multiplexer API and makes the implementation applicable to compatible control-mode servers.
HTM compatibility
HTM is a headless multiplexer from EternalTerminal. Its current protocol deliberately speaks tmux control mode on the terminal PTY rather than a private framed protocol. The companion EternalTerminal work was merged in MisterTea/EternalTerminal#806.
The authoritative HTM integration references are:
HTM implements the subset needed by graphical terminal clients, including command/reply transactions, pane output, layout snapshots and changes, client sizing, and pause/resume flow control. It advertises a compatible tmux version so existing GUI-client behavior can be reused.
Implementation notes
A leader connection initially wraps the normal ConPTY connection. On detecting the
ESC P 1000 pcontrol-mode marker, Windows Terminal promotes the connection into an HTM session. Follower connections then back native panes without launching redundant local shells.The parser handles control markers and protocol lines across arbitrary read boundaries, tmux octal escaping, reply correlation, pane output routing, and checksummed tmux layout trees. UI actions are translated back to tmux-compatible commands. Shared leader/follower state is synchronized because ConPTY output callbacks and XAML action handlers run on different threads.
Non-HTM profiles continue to use the existing ConPTY path.
Related requests
This implements the long-standing request in microsoft/terminal#3656 — Add support for tmux Control Mode, which grew out of the tmux and pane discussions in #532 and #1000. It also addresses the use case raised again in discussion #15613, which was closed as a duplicate of #3656.
Demo videos
Windows Terminal HTM end-to-end recordings from the EternalTerminal GUI driver (layout, stress, corners). Hosted as GitHub attachments (not in the Terminal tree).
Layout
windows-terminal-htm-layout-win01.mp4
Stress
windows-terminal-htm-stress-win01.mp4
Corners (native window 1)
windows-terminal-htm-corners-win01.mp4
Corners (native window 2)
windows-terminal-htm-corners-win02.mp4
Testing
HtmProtocolTests.cppcoverage for protocol parsing, escaping, control markers, replies, output, layouts, and related helpers.refresh-client, splits, queries, output routing, and clean shutdown.