Skip to content

Add support for tmux control mode (#3656) - #18928

Open
Joe Xue (joexue) wants to merge 15 commits into
microsoft:mainfrom
joexue:tmux_cc_pr
Open

Joe Xue (joexue) wants to merge 15 commits into
microsoft:mainfrom
joexue:tmux_cc_pr

Conversation

@joexue

@joexue Joe Xue (joexue) commented May 20, 2025

Copy link
Copy Markdown

Summary of the Pull Request

Let WT supports tmux control mode

References and Relevant Issues

#3656

Detailed Description of the Pull Request / Additional comments

Screenshot 2025-05-13 232209

Support:

  • Create/attach tmux session

  • Split pane vertical/horizontal

  • Window/panes size change

  • Remove pane/tab if remote pane exit or window exit

Improvements may do:

  • Merge the tmux menus/button with existing menu/button
  • Add the keyboard accelerators for tmux menus
  • Unicode support(the VTParser just pass part of characters in DCS mode
  • When attached a session, focus on the same focused tab/pane when detaching(backend is done, need to do some work frontend)
  • Test code

Tested by using tmux 3.4:
ssh to a machine has tmux or use wsl, then run
"tmux -CC" or "tmux -CC a"

Validation Steps Performed

PR Checklist

@microsoft-github-policy-service microsoft-github-policy-service Bot added Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Area-Extensibility A feature that would ideally be fulfilled by us having an extension model. Area-Interop Communication between processes Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Product-Terminal The new Windows Terminal. labels May 20, 2025
@joexue

This comment was marked as outdated.

@github-actions

This comment has been minimized.

@joexue

This comment was marked as outdated.

@joexue

This comment was marked as outdated.

@joexue

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@DHowett

Copy link
Copy Markdown
Member

whoa

@iDarshan

Copy link
Copy Markdown

This is a long-awaited feature! Thanks Joe Xue (@joexue)!

@BinaryShrub

Copy link
Copy Markdown

gif

@DHowett

Copy link
Copy Markdown
Member

FYI: I am working with reduced staffing at the moment, so reviews may be slow to come. I apologize for that.

@iDarshan

Copy link
Copy Markdown

Is there a nightly build with this PR to test?

@carlos-zamora

Copy link
Copy Markdown
Member

Is there a nightly build with this PR to test?

Your best bet is to build this locally. Once it merges, it will be available on the Canary channel though.

@iDarshan

Copy link
Copy Markdown

Is there a nightly build with this PR to test?

Your best bet is to build this locally. Once it merges, it will be available on the Canary channel though.

I tried to find the build guide, but couldn't find one. Do you have the link handy?

@joexue

Copy link
Copy Markdown
Author

Is there a nightly build with this PR to test?

Your best bet is to build this locally. Once it merges, it will be available on the Canary channel though.

I tried to find the build guide, but couldn't find one. Do you have the link handy?

README.md ->Developer Guidance Darshan Prajapati (@iDarshan)

@carlos-zamora

Copy link
Copy Markdown
Member

Hi Joe Xue (@joexue)! Thanks for doing this! I published a build for the team to test internally and we've got a few notes we want to share with you 😊

  • Notes/Reactions:
    • Settings menu item is gone from the dropdown
    • It makes your existing window a special "tmux only" window; it disables the menu, but it doesn't disable the key bindings, so you can still split in non-tmux panes
    • Scrollbars are gone? I guess scrolling is server side
  • Requested changes:
    • [Bug] can't type in an older pane after splitting panes
    • tmux tabs should not be draggable into a new window
    • Add a setting to Profile > "Terminal Emulation" page that disables this (it should be disabled by default)
    • Bug: Closing a tab/pane will not kill the instance on the server side
    • Add a feature flag to features.xml (Guidance)
    • [Architectural notes] We should see if we can't just model a tmux pane as a connection so we don't need to pump output into the terminal from the App layer

Overall, this is a really exciting feature and we're excited to see it land! Let us know if you need any guidance or additional comments on any of the thoughts above. 😊

CC Dustin L. Howett (@DHowett) Leonard Hecker (@lhecker) Mike Griese (@zadjii-msft)

@joexue

Copy link
Copy Markdown
Author

Hi Joe Xue (@joexue)! Thanks for doing this! I published a build for the team to test internally and we've got a few notes we want to share with you 😊

  • Notes/Reactions:

    • Settings menu item is gone from the dropdown
    • It makes your existing window a special "tmux only" window; it disables the menu, but it doesn't disable the key bindings, so you can still split in non-tmux panes
    • Scrollbars are gone? I guess scrolling is server side

Scrollbars are gone is by design, I put the comment in the code, since we want to make local panes size match remote(tmux) panes size, but each split, tmux just lost 1 character to use it as separator, if we have side scrollbars, we cannot make two side's size match. To meet this, the padding size is changed too.

As the menu, it is because I tried to touch as less code as I can to do the job. So give the tmux a separate UI system. I described in the PR, this could be improved and should integrated into the present UI/menu system.
But, I think we can skip this ugly stage, directly change the code to make the tmux control mode work with present UI/menu system. I will address this, those +button/flyout splitting menus specifically for tmux will be gone.

  • Requested changes:

    • [Bug] can't type in an older pane after splitting panes

I did not see this bug, to help me to reproduce it here, could you describe your env? how do you connect to tmux server? what is the tmux version?

  • tmux tabs should not be draggable into a new window

Sure, let me fix it.

  • Add a setting to Profile > "Terminal Emulation" page that disables this (it should be disabled by default)

Sure, let me look it.

  • Bug: Closing a tab/pane will not kill the instance on the server side

This is by design, remote pane should close by quit it's app. but this is arguable, if we think close local pane should close remote pane too, it is easy and feasible.

  • Add a feature flag to features.xml (Guidance)

Sure. Fair enough.

  • [Architectural notes] We should see if we can't just model a tmux pane as a connection so we don't need to pump output into the terminal from the App layer

Please provide the idea, I can try to implement it.

Overall, this is a really exciting feature and we're excited to see it land! Let us know if you need any guidance or additional comments on any of the thoughts above. 😊

CC Dustin L. Howett (@DHowett) Leonard Hecker (@lhecker) Mike Griese (@zadjii-msft)

Thanks!

Since this PR is a bit big, another option is we split it into two or three parts to make it easy to review and integrate. Such as
part 1: infra libs(parser, TermControl, Terminal, ControlCore)
part 2: TmuxControl
part 3: integrating and UI/Menu

What do you think, split it or just keep as it is?

@joexue

Copy link
Copy Markdown
Author

Carlos Zamora (@carlos-zamora) Dustin L. Howett (@DHowett) Leonard Hecker (@lhecker) Mike Griese (@zadjii-msft)
All comments are addressed, please try again.

Thanks

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@iDarshan

Copy link
Copy Markdown

I tried to test this and seems like its not working

root@fst:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.3 LTS
Release:        24.04
Codename:       noble
root@fst:~# tmux ls
0: 1 windows (created Sat Jan 31 02:31:00 2026)
root@fst:~#
root@fst:~# tmux -CC attach
root@fst:~#
root@fst:~# tmux -V
tmux 3.4
root@fst:~#

Anything special need to be done to try this?

@DHowett

Copy link
Copy Markdown
Member

Anything special need to be done to try this?

You probably need to turn it on in Settings.

@iDarshan

Copy link
Copy Markdown

Anything special need to be done to try this?

You probably need to turn it on in Settings.

Which settings? in Terminal or System settings? Do you have path to setting?

@DHowett

Dustin L. Howett (DHowett) commented Jan 31, 2026

Copy link
Copy Markdown
Member

Which version of Terminal are you using? Did you compile it yourself? Since this pull request is still open and has not been accepted, you would need to have compiled it yourself.

@iDarshan

Copy link
Copy Markdown

Which version of Terminal are you using? Did you compile it yourself? Since this pull request is still open and has not been accepted, you would need to have compiled it yourself.

Compiled myself. It took hell of a time to figure out compilation steps. it always fails due to vcpkg. Readme needs to include steps around vcpkg failure.

@DHowett

Copy link
Copy Markdown
Member

Oh dang, that's a new failure state we haven't heard of. If you remember what you had to do, would you mind sharing? It's supposed to use the vcpkg install that comes with VS, but if that's broken it will try a couple other things.

I think this PR puts the control mode toggle into Terminal's in-app settings, profile (or defaults), advanced.

@iDarshan

Copy link
Copy Markdown

Enable this setting to allow Tmux Control mode:
Settings->Defaults->Additional Settings->Terminal Emulation->Allow Tmux Control

@iDarshan

Copy link
Copy Markdown

I have tested this briefly and here are the basic bugs:

  • Cannot resize the panes
  • Alt + (+ button) click always splits the current pane vertically, no way to split pane horizontally.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is recommended that the author optimize the memory safety aspect to make the overall logic more robust.

std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType);
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize = .5);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const float splitSize = .5

The correct way to write it is 0.5f. While using .5 might not show any obvious errors at runtime, it introduces numerous type conversions. It's also recommended to add a type specification to this variable.

- const float splitSize = .5
+ const float splitSize = 0.5f


namespace winrt::TerminalApp::implementation
{
void TmuxConnection::Initialize(const Windows::Foundation::Collections::ValueSet&) const noexcept

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void TmuxConnection::Initialize(const Windows::Foundation::Collections::ValueSet&) const noexcept
// WIP: `TmuxConnection::Initialize()` is working a progress......
void TmuxConnection::Initialize(const Windows::Foundation::Collections::ValueSet&) const noexcept {}

I suggest you add a comment declaring the purpose of this function. Or you can write a // WIP: comment to just to clarify, this feature is currently under development.

DiscoverPanes,
};

struct ResponseInfo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you use some more compatible functions to implement this functionality, such as std::variant.

void _handleDetach();
void _handleSessionChanged(int64_t sessionId);
void _handleWindowAdd(int64_t windowId);
void _handleWindowRenamed(int64_t windowId, winrt::hstring name);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void _handleWindowRenamed(int64_t windowId, winrt::hstring name);
void _handleWindowRenamed(int64_t windowId, const winrt::hstring name);

It is recommended to define this as a constant, that is, a read-only variable, for runtime safety reasons.

if (auto inputCallback = args->InputCallback())
{
return [inputCallback = std::move(inputCallback)](wchar_t ch) -> bool {
const auto c16 = static_cast<char16_t>(ch);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const auto c16 = static_cast<char16_t>(ch);
const auto c16 = static_cast<char16_t>(static_cast<unsigned char>(ch));

I mentioned that this syntax might cause garbled characters in multilingual support or emojis......

else
{
// Do we ever get here (= uninitialized terminal)? If so: How?
assert(false);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting this assertion is like burying your head in the sand, it may seem like the problem is solved, but it will actually trigger a series of chain reactions.


std::function<bool(wchar_t)> Terminal::EnterTmuxControl()
{
return _pfnEnterTmuxControl ? _pfnEnterTmuxControl() : nullptr;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is written quite meticulously.

Comment thread src/host/outputStream.cpp
// Not implemented for conhost.
}

std::function<bool(wchar_t)> ConhostInternalGetSet::EnterTmuxControl()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you add a comment to this section, indicating that the logic of this function needs further verification.

std::function<bool(wchar_t)> EnterTmuxControl() override
{
Log::Comment(L"EnterTmuxControl MOCK called...");
return nullptr;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nullptr;
return 1;

If you need to return an error message, you can define the return value as 1 instead of a null pointer.


ITermDispatch::StringHandler AdaptDispatch::EnterTmuxControl(const VTParameters parameters)
{
if (parameters.size() != 1 || parameters.at(0).value() != 1000)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (parameters.size() != 1 || parameters.at(0).value() != 1000)
const int TmuxControlModeId = 1000;
if (parameters.size() != 1 || parameters.at(0).value() != TmuxControlModeld)

To improve the maintainability of the source code, it is recommended to store 1000 as a variable.

@joexue

Copy link
Copy Markdown
Author

I have tested this briefly and here are the basic bugs:

  • Cannot resize the panes
  • Alt + (+ button) click always splits the current pane vertically, no way to split pane horizontally.

It works for me to split both vertically and horizontally, at least before Leonard Hecker (@lhecker) change. It calculates automatically, try split several times. Thank you for your testing.

@joexue

Copy link
Copy Markdown
Author

Hi team,
AI stuff cause the terminal and command line interface usage increases significantly, and most our job on server, each terminal give a ssh and keep it is a bit annoying. Tmux control is a very good solution for this. Many my colleagues love this functionality and they have to use my private build binary. They hope this functionality can be included in official build. Is there possibility to speed up this? Thanks.

@iDarshan

Copy link
Copy Markdown

I have tested this briefly and here are the basic bugs:

  • Cannot resize the panes
  • Alt + (+ button) click always splits the current pane vertically, no way to split pane horizontally.

It works for me to split both vertically and horizontally, at least before Leonard Hecker (@lhecker) change. It calculates automatically, try split several times. Thank you for your testing.

Joe Xue (@joexue) How do you split horizontally? What is the step? I have attached the screenshot for splitting from + button. Also, how do you resize the pan?
{B683DD87-E715-4C64-B4F9-8A1B12DD892A}

@joexue

Copy link
Copy Markdown
Author

I have tested this briefly and here are the basic bugs:

  • Cannot resize the panes
  • Alt + (+ button) click always splits the current pane vertically, no way to split pane horizontally.

It works for me to split both vertically and horizontally, at least before Leonard Hecker (@lhecker) change. It calculates automatically, try split several times. Thank you for your testing.

Joe Xue (@joexue) How do you split horizontally? What is the step? I have attached the screenshot for splitting from + button. Also, how do you resize the pan? {B683DD87-E715-4C64-B4F9-8A1B12DD892A}

Hi Darshan Prajapati (@iDarshan)
Everything are same as windows terminal are doing for now.
1 resize, focus on the panel and use Alt+Shift+Arrow keys
2 split, focus on the panel and right click the tab title and choose the “split tab” from menu. It will split by calculating the width and height, I.e. if width > height then vertical split else horizontal split.
I use defined shortcuts to split by intensional means. In the settings->actions you will find “split pane, split right” and “ split pane, split down”. If you cannot find it , define them yourself.
Thanks for using it.

@iDarshan

Copy link
Copy Markdown

One more thing Joe Xue (@joexue). I see that once I am in tmux control mode, Terminal is not showing space key as input. So if I type ls test it shows lstest. Then if I do back space, it will even delete prompt characters. So seems like something wrong with space character input.

@iDarshan

Copy link
Copy Markdown

It's been a year since this PR was opened. When will this get merged?

@zhangweize9-cyber

Copy link
Copy Markdown

Why is it still on indefinite hold? What stage is it stuck at, exactly?

@zadjii-msft

Copy link
Copy Markdown
Member

Joe Xue (@joexue) mind merging main into this? looks like the branch went stale

@joexue

Copy link
Copy Markdown
Author

Joe Xue (Joe Xue (@joexue)) mind merging main into this? looks like the branch went stale

Done, thanks

@lhecker

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@lhecker

Copy link
Copy Markdown
Member

Apologies for the delays. The PR doesn't integrate well into our existing architecture and we technically really need to rework how panes are represented. (They shouldn't own the UI content, but rather represent the layout only. This way, the tmux extension could re-layout the entire pane tree cheaply, and we only re-layout the XAML UI lazily later.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Extensibility A feature that would ideally be fulfilled by us having an extension model. Area-Interop Communication between processes Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Product-Terminal The new Windows Terminal.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for tmux Control Mode

10 participants