This commit is contained in:
Leonard Hecker 2025-12-04 19:00:29 +01:00
parent d2dc2092a6
commit 55abb0ffdf
5 changed files with 788 additions and 1166 deletions

View File

@ -3612,17 +3612,20 @@ namespace winrt::TerminalApp::implementation
if constexpr (Feature_TmuxControl::IsEnabled())
{
control.EnterTmuxControl([this](auto&&, auto&& args) {
if (!_tmuxControl)
{
_tmuxControl = std::make_unique<TmuxControl>(*this);
}
if (!_tmuxControl)
{
_tmuxControl = std::make_unique<TmuxControl>(*this);
}
if (_tmuxControl->AcquireSingleUseLock())
control.EnterTmuxControl([tmuxControl = _tmuxControl.get()](auto&& sender, auto&& args) {
if (auto control = sender.try_as<TermControl>())
{
args.InputCallback([this](auto&& str) {
_tmuxControl->FeedInput(winrt_array_to_wstring_view(str));
});
if (tmuxControl->AcquireSingleUseLock(std::move(control)))
{
args.InputCallback([tmuxControl](auto&& str) {
tmuxControl->FeedInput(winrt_array_to_wstring_view(str));
});
}
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
public:
TmuxControl(TerminalPage& page);
bool AcquireSingleUseLock() noexcept;
bool AcquireSingleUseLock(winrt::Microsoft::Terminal::Control::TermControl control) noexcept;
void FeedInput(std::wstring_view str);
bool TabIsTmuxControl(const winrt::com_ptr<Tab>& tab);
void SplitPane(const winrt::com_ptr<Tab>& tab, winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction);
@ -32,191 +32,37 @@ namespace winrt::TerminalApp::implementation
ATTACHED,
};
enum class CommandState
enum class ResponseInfoType
{
READY,
WAITING,
Ignore,
DiscoverWindows,
DiscoverPanes,
ListWindow,
ListPanes,
CapturePane,
};
enum class EventType
struct ResponseInfo
{
BEGIN,
END,
ERR,
ATTACH,
DETACH,
LAYOUT_CHANGED,
NOTHING,
OUTPUT,
RESPONSE,
SESSION_CHANGED,
UNLINKED_WINDOW_CLOSE,
WINDOW_ADD,
WINDOW_CLOSE,
WINDOW_PANE_CHANGED,
WINDOW_RENAMED,
};
struct Event
{
EventType type{ EventType::NOTHING };
int64_t sessionId{ -1 };
int64_t windowId{ -1 };
int64_t paneId{ -1 };
std::wstring response;
};
// Command structs
struct Command
{
public:
virtual std::wstring GetCommand() = 0;
virtual bool ResultHandler(const std::wstring& /*result*/, TmuxControl& /*tmux*/) { return true; };
};
struct AttachDone : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
};
struct CapturePane : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
int64_t paneId{ -1 };
til::CoordType cursorX{ 0 };
til::CoordType cursorY{ 0 };
til::CoordType history{ 0 };
};
struct DiscoverPanes : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
int64_t sessionId{ -1 };
int64_t windowId{ -1 };
bool newWindow{ false };
};
struct DiscoverWindows : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
int64_t sessionId{ -1 };
};
struct KillPane : public Command
{
public:
std::wstring GetCommand() override;
int64_t paneId{ -1 };
};
struct KillWindow : public Command
{
public:
std::wstring GetCommand() override;
int64_t windowId{ -1 };
};
struct ListPanes : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
int64_t windowId{ -1 };
til::CoordType history{ 2000 };
};
struct ListWindow : public Command
{
public:
std::wstring GetCommand() override;
bool ResultHandler(const std::wstring& result, TmuxControl& tmux) override;
int64_t windowId{ -1 };
int64_t sessionId{ -1 };
};
struct NewWindow : public Command
{
public:
std::wstring GetCommand() override;
};
struct ResizePane : public Command
{
public:
std::wstring GetCommand() override;
til::CoordType width{ 0 };
til::CoordType height{ 0 };
int64_t paneId{ -1 };
};
struct ResizeWindow : public Command
{
public:
std::wstring GetCommand() override;
til::CoordType width{ 0 };
til::CoordType height{ 0 };
int64_t windowId{ -1 };
};
struct SelectWindow : public Command
{
public:
std::wstring GetCommand() override;
int64_t windowId{ -1 };
};
struct SelectPane : public Command
{
public:
std::wstring GetCommand() override;
int64_t paneId{ -1 };
};
struct SendKey : public Command
{
public:
std::wstring GetCommand() override;
int64_t paneId{ -1 };
std::wstring keys;
wchar_t key{ '\0' };
};
struct SetOption : public Command
{
public:
std::wstring GetCommand() override;
std::wstring option;
};
struct SplitPane : public Command
{
public:
std::wstring GetCommand() override;
int64_t paneId{ -1 };
winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction{ winrt::Microsoft::Terminal::Settings::Model::SplitDirection::Left };
ResponseInfoType type;
union
{
struct
{
} discoverWindows;
struct
{
} discoverPanes;
struct
{
} listWindow;
struct
{
} listPanes;
struct
{
} capturePane;
} data;
};
// Layout structs
@ -275,90 +121,86 @@ namespace winrt::TerminalApp::implementation
};
// Private methods
void _AttachSession();
void _DetachSession();
void _SetupProfile();
void _CreateNewTabMenu();
void _parseLine(std::wstring_view str);
void _handleAttach();
void _handleDetach();
void _handleOutput(int64_t paneId, const std::wstring_view result);
void _handleWindowRenamed(int64_t windowId, const std::wstring_view name);
void _handleWindowClose(int64_t windowId);
void _handleWindowPaneChanged(int64_t windowId, int64_t paneId);
void _handleResponse(std::wstring_view result);
void _sendSetOption(std::wstring_view option);
void _sendDiscoverWindows(int64_t sessionId);
void _handleResponseDiscoverWindows(std::wstring_view response);
void _sendDiscoverPanes(int64_t sessionId, int64_t windowId, bool newWindow);
void _handleResponseDiscoverPanes(std::wstring_view response);
void _sendListWindow(int64_t sessionId, int64_t windowId);
void _handleResponseListWindow(std::wstring_view response);
void _sendListPanes(int64_t windowId, til::CoordType history);
void _handleResponseListPanes(std::wstring_view response);
void _sendCapturePane(int64_t paneId, til::CoordType cursorX, til::CoordType cursorY, til::CoordType history);
void _handleResponseCapturePane(std::wstring_view response);
void _sendNewWindow();
void _sendKillWindow(int64_t windowId);
void _sendKillPane(int64_t paneId);
void _sendSplitPane(std::shared_ptr<Pane> pane, winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction);
void _sendSelectWindow(int64_t windowId);
void _sendSelectPane(int64_t paneId);
void _sendResizeWindow(int64_t windowId, til::CoordType width, til::CoordType height);
void _sendResizePane(int64_t paneId, til::CoordType width, til::CoordType height);
void _sendSendKey(int64_t paneId, const std::wstring_view keys);
float _ComputeSplitSize(til::CoordType newSize, til::CoordType originSize, winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction) const;
winrt::com_ptr<Tab> _GetTab(int64_t windowId) const;
winrt::com_ptr<Tab> _getTab(int64_t windowId) const;
void _SendOutput(int64_t paneId, const std::wstring& text);
void _Output(int64_t paneId, const std::wstring& result);
void _CloseWindow(int64_t windowId);
void _RenameWindow(int64_t windowId, const std::wstring& name);
void _NewWindowFinalize(int64_t windowId, int64_t paneId, const std::wstring& windowName);
void _SplitPaneFinalize(int64_t windowId, int64_t paneId);
std::shared_ptr<Pane> _NewPane(int64_t windowId, int64_t paneId);
bool _SyncPaneState(std::vector<TmuxPane> panes, til::CoordType history);
bool _SyncWindowState(std::vector<TmuxWindow> windows);
void _EventHandler(const Event& e);
void _Parse(const std::wstring& buffer);
bool _Advance(wchar_t ch);
void _deliverOutputToPane(int64_t paneId, const std::wstring_view text);
void _newWindowFinalize(int64_t windowId, int64_t paneId, const std::wstring_view windowName);
std::shared_ptr<Pane> _newPane(int64_t windowId, int64_t paneId);
// Tmux command methods
void _AttachDone();
void _CapturePane(int64_t paneId, til::CoordType cursorX, til::CoordType cursorY, til::CoordType history);
void _DiscoverPanes(int64_t sessionId, int64_t windowId, bool newWindow);
void _DiscoverWindows(int64_t sessionId);
void _KillPane(int64_t paneId);
void _KillWindow(int64_t windowId);
void _ListWindow(int64_t sessionId, int64_t windowId);
void _ListPanes(int64_t windowId, til::CoordType history);
void _NewWindow();
void _OpenNewTerminalViaDropdown();
void _ResizePane(int64_t paneId, til::CoordType width, til::CoordType height);
void _ResizeWindow(int64_t windowId, til::CoordType width, til::CoordType height);
void _SelectPane(int64_t paneId);
void _SelectWindow(int64_t windowId);
void _SendKey(int64_t paneId, const std::wstring keys);
void _SetOption(const std::wstring& option);
void _SplitPane(std::shared_ptr<Pane> pane, winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction);
void _CommandHandler(const std::wstring& result);
void _SendCommand(std::unique_ptr<Command> cmd);
void _ScheduleCommand();
void _sendIgnoreResponse(wil::zwstring_view cmd);
void _sendWithResponseInfo(wil::zwstring_view cmd, ResponseInfo info);
// Private variables
TerminalPage& _page; // Non-owning, because TerminalPage owns us
State _state{ State::INIT };
CommandState _cmdState{ CommandState::READY };
Event _event;
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _newTabMenu;
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
TerminalApp::Tab _controlTab{ nullptr };
winrt::Windows::System::DispatcherQueue _dispatcherQueue{ nullptr };
std::vector<wchar_t> _lineBuffer;
std::wstring _responseBuffer;
winrt::event_token _detachKeyDownRevoker;
winrt::event_token _windowSizeChangedRevoker;
winrt::event_token _newTabClickRevoker;
::winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _newTabMenu;
std::vector<wchar_t> _dcsBuffer;
std::deque<std::unique_ptr<TmuxControl::Command>> _cmdQueue;
std::deque<ResponseInfo> _commandQueue;
std::unordered_map<int64_t, AttachedPane> _attachedPanes;
std::unordered_map<int64_t, winrt::com_ptr<Tab>> _attachedWindows;
std::unordered_map<int64_t, std::wstring> _outputBacklog;
int64_t _sessionId{ -1 };
int64_t _sessionId = -1;
int64_t _activePaneId = -1;
int64_t _activeWindowId = -1;
til::CoordType _terminalWidth{ 0 };
til::CoordType _terminalHeight{ 0 };
til::CoordType _terminalWidth = 0;
til::CoordType _terminalHeight = 0;
float _fontWidth{ 0 };
float _fontHeight{ 0 };
float _fontWidth = 0;
float _fontHeight = 0;
::winrt::Windows::UI::Xaml::Thickness _thickness{ 0, 0, 0, 0 };
winrt::Windows::UI::Xaml::Thickness _thickness{ 0, 0, 0, 0 };
std::pair<std::shared_ptr<Pane>, winrt::Microsoft::Terminal::Settings::Model::SplitDirection> _splittingPane{ nullptr, winrt::Microsoft::Terminal::Settings::Model::SplitDirection::Right };
int64_t _activePaneId{ -1 };
int64_t _activeWindowId{ -1 };
std::function<void(const std::wstring_view string)> _Print;
std::atomic<bool> _inUse{ false };
bool _insideOutputBlock = false;
};
}

View File

@ -331,6 +331,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand });
_revokers.WindowSizeChanged = _core.WindowSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWindowSizeChanged });
_revokers.WriteToClipboard = _core.WriteToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWriteToClipboard });
_revokers.EnterTmuxControl = _core.EnterTmuxControl(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleEnterTmuxControl });
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
@ -4010,6 +4011,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void TermControl::_bubbleEnterTmuxControl(const IInspectable&, Control::EnterTmuxControlEventArgs args)
{
EnterTmuxControl.raise(*this, std::move(args));
}
til::CoordType TermControl::_calculateSearchScrollOffset() const
{
auto result = 0;

View File

@ -218,6 +218,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::StringSentEventArgs> StringSent;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<IInspectable, Control::WindowSizeChangedEventArgs> WindowSizeChanged;
til::typed_event<IInspectable, Control::EnterTmuxControlEventArgs> EnterTmuxControl;
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
// Those attach the handler to the core directly, and will explode if
@ -232,7 +233,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(WriteToClipboard, IInspectable, Control::WriteToClipboardEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(EnterTmuxControl, IInspectable, Control::EnterTmuxControlEventArgs);
// clang-format on
@ -439,6 +439,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args);
winrt::fire_and_forget _bubbleWindowSizeChanged(const IInspectable& sender, Control::WindowSizeChangedEventArgs args);
void _bubbleEnterTmuxControl(const IInspectable& sender, Control::EnterTmuxControlEventArgs args);
til::CoordType _calculateSearchScrollOffset() const;
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
@ -473,6 +474,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand;
Control::ControlCore::RefreshQuickFixUI_revoker RefreshQuickFixUI;
Control::ControlCore::WindowSizeChanged_revoker WindowSizeChanged;
Control::ControlCore::EnterTmuxControl_revoker EnterTmuxControl;
// These are set up in _InitializeTerminal
Control::ControlCore::RendererWarning_revoker RendererWarning;