- Notifications
You must be signed in to change notification settings - Fork 9k
Add support for tmux control mode (#3656) #18928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment has been minimized.
This comment has been minimized.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
| @microsoft-github-policy-service agree |
| whoa |
| This is a long-awaited feature! Thanks @joexue! |
| FYI: I am working with reduced staffing at the moment, so reviews may be slow to come. I apologize for that. |
| 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 @iDarshan |
| Hi @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 😊
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. 😊 |
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.
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?
Sure, let me fix it.
Sure, let me look it.
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.
Sure. Fair enough.
Please provide the idea, I can try to implement it.
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 What do you think, split it or just keep as it is? |
| @carlos-zamora @DHowett @lhecker @zadjii-msft Thanks |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
54c2bac to 4817698 Compare 9b798b3 to eefdfd8 Compare This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
821dbf8 to 69260b3 Compare This comment has been minimized.
This comment has been minimized.
| Anything we can do on this? |
We're just about to release v1.24, and then we can really start to go crazy with new features 🙂 |
Understood, take your time.👌 |
Any updates on getting this merged? |
| What's holding this up at the moment? I've anxiously been sitting on needles waiting for tmux control mode support in Windows Terminal for quite a while now. |
| I began working on this PR late last week. It has a few issues that require fundamental changes to the PR. For instance, we use the |
Definitely good news, thanks for taking care of that |
| control.SetTmuxControlHandlerProducer([this, control](auto print) { | ||
| return _tmuxControl->TmuxControlHandlerProducer(control, print); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to leave some comments on your PR now for things that I found and already corrected in my branch. The problem is that some of the required changes essentially break your PR fundamentally, and I found it difficult to piece it back together. That's why I haven't committed it yet. 😅
In any case, this first one is pretty gnarly. Because your lambda here takes a copy of control and then you store the lambda on control itself, you're leaking the control instance. It'll never be freed.
(It's not possible to fix this by passing control as a weak pointer because this breaks the Close events. I'm still working to fix this.)
| const std::wregex TmuxControl::REG_WINDOW_ADD{ L"^%window-add @(\\d+)$" }; | ||
| const std::wregex TmuxControl::REG_WINDOW_CLOSE{ L"^%window-close @(\\d+)$" }; | ||
| const std::wregex TmuxControl::REG_WINDOW_PANE_CHANGED{ L"^%window-pane-changed @(\\d+) %(\\d+)$" }; | ||
| const std::wregex TmuxControl::REG_WINDOW_RENAMED{ L"^%window-renamed @(\\d+) (\\S+)$" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While regexes are fine for some things, I'm personally not happy with the reliance on them in this PR. I believe we should write proper, classic parsers for the tmux protocol.
| // Tmux control has its own profile, we duplicate it from the control panel | ||
| void TmuxControl::_SetupProfile() | ||
| { | ||
| const auto settings{ CascadiaSettings::LoadDefaults() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, creating an entire settings object for cloning the profile is a bit ugly. I have not changed this however, as we indeed lack the facilities to do it differently.
| out.push_back(c); | ||
| ++it; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ideal performance this should do a chunk-wise translation of the output payload.
| // The pane is not ready it, put int backlog for now | ||
| if (search == _attachedPanes.end()) | ||
| { | ||
| _outputBacklog.insert_or_assign(paneId, text); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we create an _attachedPanes item in advance here, we can store the output backlog in the AttachedPane struct. I think this also makes some other code a bit neater.
| void DummyConnection::WriteInput(const winrt::array_view<const char16_t> buffer) | ||
| { | ||
| const auto data = winrt_array_to_wstring_view(buffer); | ||
| std::wstringstream prettyPrint; | ||
| for (const auto& wch : data) | ||
| { | ||
| prettyPrint << wch; | ||
| } | ||
| TerminalInput.raise(prettyPrint.str()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't quite ideal. To fix this we can simply change TerminalInput to also send Char[] instead of String around. Then this can simple pass the buffer through unmodified.
| | ||
| namespace winrt::Microsoft::Terminal::TerminalConnection::implementation | ||
| { | ||
| DummyConnection::DummyConnection() noexcept = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of making this a dummy connection, this should ideally be a genuine TmuxConnection which raises proper output events. All we need to do for that is save the connection instance in TmuxControl and use it to raise the output events.
| void ControlCore::SendOutput(const std::wstring_view wstr) | ||
| { | ||
| if (wstr.empty()) | ||
| { | ||
| return; | ||
| } | ||
| | ||
| auto lock = _terminal->LockForWriting(); | ||
| _terminal->Write(wstr); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a TmuxConnection then makes this unnecessary.
| PrintString(s); | ||
| _DoLineFeed(page, true, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the PrintHandler is a very clever solution, I don't think that we should do this. It taints the VT parser with even more Windows-Terminal-specific code. It moves us further into a "big ball of mud" direction instead of the opposite of turning our VT parser into a purer, proper parser. I've broken this functionality in my local branch, and I hope we can restore this functionality at some point later.
| void AdaptDispatch::SetTmuxControlHandlerProducer(StringHandlerProducer producer) | ||
| { | ||
| _tmuxControlHandlerProducer = producer; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use the ITerminalApi interface to raise notifications to the hosting terminal (a typical push parser design). If we now have callback functions on top of that, this would make it worse. The correct approach is to add a ITerminalApi function which returns a StringHandler handler.
| } | ||
| | ||
| // From tmux to controller through the dcs. parse it per line. | ||
| bool TmuxControl::_Advance(wchar_t ch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've written #19640 to improve the performance of this PR. I've already modified this function in my branch to process entire std::wstring_views at a time.

Summary of the Pull Request
Let WT supports tmux control mode
References and Relevant Issues
#3656
Detailed Description of the Pull Request / Additional comments
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:
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
tmuxControl Mode #3656