This commit is contained in:
Leonard Hecker 2025-12-06 16:46:11 +01:00
parent 835467888e
commit 962461df32
2 changed files with 185 additions and 162 deletions

View File

@ -24,20 +24,6 @@ using namespace winrt::Windows::UI::Core;
static const til::CoordType PaneBorderSize = 2; static const til::CoordType PaneBorderSize = 2;
static const til::CoordType StaticMenuCount = 4; // "Separator" "Settings" "Command Palette" "About" static const til::CoordType StaticMenuCount = 4; // "Separator" "Settings" "Command Palette" "About"
static std::wstring_view trim_newline(std::wstring_view str)
{
auto end = str.end();
if (end != str.begin() && end[-1] == L'\n')
{
--end;
if (end != str.begin() && end[-1] == L'\r')
{
--end;
}
}
return { str.begin(), end };
}
static std::wstring_view split_line(std::wstring_view& remaining) static std::wstring_view split_line(std::wstring_view& remaining)
{ {
auto lf = remaining.find(L'\n'); auto lf = remaining.find(L'\n');
@ -50,8 +36,9 @@ static std::wstring_view split_line(std::wstring_view& remaining)
--end; --end;
} }
const auto line = til::safe_slice_abs(remaining, 0, end);
remaining = til::safe_slice_abs(remaining, lf + 1, std::wstring_view::npos); remaining = til::safe_slice_abs(remaining, lf + 1, std::wstring_view::npos);
return til::safe_slice_abs(remaining, 0, end); return line;
} }
static std::wstring_view tokenize_field(std::wstring_view& remaining) static std::wstring_view tokenize_field(std::wstring_view& remaining)
@ -174,8 +161,13 @@ namespace winrt::TerminalApp::implementation
return; return;
} }
// Otherwise, parse the completed line. // Strip of any remaining CR. We already removed the LF after the find() call.
_parseLine(trim_newline({ _lineBuffer.begin(), _lineBuffer.end() })); if (!_lineBuffer.empty() && _lineBuffer.back() == L'\r')
{
_lineBuffer.pop_back();
}
_parseLine(std::move(_lineBuffer));
_lineBuffer.clear(); _lineBuffer.clear();
// Move past the line we just processed. // Move past the line we just processed.
@ -192,7 +184,8 @@ namespace winrt::TerminalApp::implementation
--end; --end;
} }
_parseLine(til::safe_slice_abs(str, 0, end)); const auto line = til::safe_slice_abs(str, 0, end);
_parseLine(std::wstring{ line });
str = til::safe_slice_abs(str, idx + 1, std::wstring_view::npos); str = til::safe_slice_abs(str, idx + 1, std::wstring_view::npos);
idx = str.find(L'\n'); idx = str.find(L'\n');
@ -201,7 +194,7 @@ namespace winrt::TerminalApp::implementation
// If there's any leftover partial line, stash it for later. // If there's any leftover partial line, stash it for later.
if (!str.empty()) if (!str.empty())
{ {
_lineBuffer.insert(_lineBuffer.end(), str.begin(), str.end()); _lineBuffer.append(str);
} }
} }
@ -220,7 +213,7 @@ namespace winrt::TerminalApp::implementation
} }
} }
if (_controlTab.TabViewIndex() == tab->TabViewIndex()) if (_controlTab->TabViewIndex() == tab->TabViewIndex())
{ {
return true; return true;
} }
@ -260,14 +253,20 @@ namespace winrt::TerminalApp::implementation
return; return;
} }
void TmuxControl::_parseLine(std::wstring_view remaining) safe_void_coroutine TmuxControl::_parseLine(std::wstring line)
{ {
remaining = trim_newline(remaining); if (line.empty())
if (remaining.empty())
{ {
return; co_return;
} }
co_await wil::resume_foreground(_dispatcherQueue);
OutputDebugStringW(L"<<< ");
OutputDebugStringW(line.c_str());
OutputDebugStringW(L"\n");
std::wstring_view remaining{ line };
const auto type = tokenize_field(remaining); const auto type = tokenize_field(remaining);
// Are we inside a %begin ... %end block? Anything until %end or %error // Are we inside a %begin ... %end block? Anything until %end or %error
@ -286,22 +285,30 @@ namespace winrt::TerminalApp::implementation
_handleResponse(_responseBuffer); _handleResponse(_responseBuffer);
} }
_responseBuffer.clear(); _responseBuffer.clear();
_insideOutputBlock = false;
} }
else if (til::equals(type, L"%error")) else if (til::equals(type, L"%error"))
{ {
// TODO: Where should we show this error?
OutputDebugStringW(_responseBuffer.c_str());
// In theory our commands should not result in errors. // In theory our commands should not result in errors.
assert(false); assert(false);
// TODO: Where should we show this error?
_responseBuffer += L"\r\n";
OutputDebugStringW(_responseBuffer.c_str());
_responseBuffer.clear(); _responseBuffer.clear();
_insideOutputBlock = false;
} }
else else
{ {
if (!_responseBuffer.empty()) if (_responseBuffer.empty())
{ {
_responseBuffer += L"\r\n"; // Note that at this point `remaining` will not be the whole `line` anymore.
_responseBuffer = std::move(line);
}
else
{
_responseBuffer.append(L"\r\n");
_responseBuffer.append(remaining);
} }
_responseBuffer += remaining;
} }
} }
// Otherwise, we check for the, presumably, most common output type first: %output. // Otherwise, we check for the, presumably, most common output type first: %output.
@ -381,7 +388,7 @@ namespace winrt::TerminalApp::implementation
_state = State::ATTACHING; _state = State::ATTACHING;
{ {
const auto settings{ CascadiaSettings::LoadDefaults() }; const auto settings = CascadiaSettings::LoadDefaults();
_profile = settings.ProfileDefaults(); _profile = settings.ProfileDefaults();
if (const auto terminalTab{ _page._GetFocusedTabImpl() }) if (const auto terminalTab{ _page._GetFocusedTabImpl() })
{ {
@ -419,7 +426,7 @@ namespace winrt::TerminalApp::implementation
_detachKeyDownRevoker = _control.KeyDown([this](auto, auto& e) { _detachKeyDownRevoker = _control.KeyDown([this](auto, auto& e) {
if (e.Key() == VirtualKey::Q) if (e.Key() == VirtualKey::Q)
{ {
_control.RawWriteString(L"detach\n"); _sendIgnoreResponse(L"detach\n");
} }
e.Handled(true); e.Handled(true);
}); });
@ -439,12 +446,12 @@ namespace winrt::TerminalApp::implementation
}); });
// Dynamically insert the "Tmux Control Tab" menu item into flyout menu // Dynamically insert the "Tmux Control Tab" menu item into flyout menu
auto tabRow = _page.TabRow(); const auto tabRow = _page.TabRow();
auto tabRowImpl = winrt::get_self<implementation::TabRowControl>(tabRow); const auto tabRowImpl = winrt::get_self<TabRowControl>(tabRow);
auto newTabButton = tabRowImpl->NewTabButton(); const auto newTabButton = tabRowImpl->NewTabButton();
const auto flyout = newTabButton.Flyout().as<Controls::MenuFlyout>();
auto menuCount = newTabButton.Flyout().try_as<Controls::MenuFlyout>().Items().Size(); const auto menuCount = flyout.Items().Size();
newTabButton.Flyout().try_as<Controls::MenuFlyout>().Items().InsertAt(menuCount - StaticMenuCount, _newTabMenu); flyout.Items().InsertAt(menuCount - StaticMenuCount, _newTabMenu);
// Register new tab button click handler for tmux control // Register new tab button click handler for tmux control
_newTabClickRevoker = newTabButton.Click([this](auto&&, auto&&) { _newTabClickRevoker = newTabButton.Click([this](auto&&, auto&&) {
@ -454,7 +461,7 @@ namespace winrt::TerminalApp::implementation
} }
}); });
_controlTab = _page._GetFocusedTab(); _controlTab = _page._GetTabImpl(_page._GetFocusedTab());
} }
void TmuxControl::_handleDetach() void TmuxControl::_handleDetach()
@ -510,13 +517,10 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleWindowRenamed(int64_t windowId, const std::wstring_view name) void TmuxControl::_handleWindowRenamed(int64_t windowId, const std::wstring_view name)
{ {
auto tab = _getTab(windowId); if (const auto tab = _getTab(windowId))
if (tab == nullptr)
{ {
return; tab->SetTabText(winrt::hstring{ name });
} }
tab->SetTabText(winrt::hstring{ name });
} }
void TmuxControl::_handleWindowClose(int64_t windowId) void TmuxControl::_handleWindowClose(int64_t windowId)
@ -530,8 +534,6 @@ namespace winrt::TerminalApp::implementation
const auto t = search->second; const auto t = search->second;
_attachedWindows.erase(search); _attachedWindows.erase(search);
t->Shutdown();
// Remove all attached panes in this window // Remove all attached panes in this window
for (auto p = _attachedPanes.begin(); p != _attachedPanes.end();) for (auto p = _attachedPanes.begin(); p != _attachedPanes.end();)
{ {
@ -541,10 +543,11 @@ namespace winrt::TerminalApp::implementation
} }
else else
{ {
p++; ++p;
} }
} }
t->Shutdown();
_page._RemoveTab(*t); _page._RemoveTab(*t);
} }
@ -644,6 +647,8 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseDiscoverWindows(std::wstring_view response) void TmuxControl::_handleResponseDiscoverWindows(std::wstring_view response)
{ {
OutputDebugStringW(L"_handleResponseDiscoverWindows\n");
while (!response.empty()) while (!response.empty())
{ {
auto line = split_line(response); auto line = split_line(response);
@ -652,6 +657,10 @@ namespace winrt::TerminalApp::implementation
{ {
_sendResizeWindow(id.value, _terminalWidth, _terminalHeight); _sendResizeWindow(id.value, _terminalWidth, _terminalHeight);
} }
else
{
assert(false);
}
} }
// _sendDiscoverWindows() gets called with _sessionId as its parameter. // _sendDiscoverWindows() gets called with _sessionId as its parameter.
@ -671,14 +680,20 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseDiscoverNewWindow(std::wstring_view response) void TmuxControl::_handleResponseDiscoverNewWindow(std::wstring_view response)
{ {
OutputDebugStringW(L"_handleResponseDiscoverNewWindow\n");
const auto windowId = tokenize_identifier(response); const auto windowId = tokenize_identifier(response);
const auto paneId = tokenize_identifier(response); const auto paneId = tokenize_identifier(response);
const auto windowName = response; const auto windowName = response;
if (windowId.type == IdentifierType::Pane && paneId.type == IdentifierType::Pane) if (windowId.type == IdentifierType::Window && paneId.type == IdentifierType::Pane)
{ {
_newWindowFinalize(windowId.value, paneId.value, windowName); _newWindowFinalize(windowId.value, paneId.value, windowName);
} }
else
{
assert(false);
}
} }
void TmuxControl::_sendDiscoverPanes(int64_t sessionId) void TmuxControl::_sendDiscoverPanes(int64_t sessionId)
@ -692,6 +707,8 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseDiscoverPanes(std::wstring_view response) void TmuxControl::_handleResponseDiscoverPanes(std::wstring_view response)
{ {
OutputDebugStringW(L"_handleResponseDiscoverPanes\n");
std::unordered_set<int64_t> panes; std::unordered_set<int64_t> panes;
while (!response.empty()) while (!response.empty())
{ {
@ -702,6 +719,10 @@ namespace winrt::TerminalApp::implementation
{ {
panes.insert(id.value); panes.insert(id.value);
} }
else
{
assert(false);
}
} }
for (auto p = _attachedPanes.begin(); p != _attachedPanes.end();) for (auto p = _attachedPanes.begin(); p != _attachedPanes.end();)
@ -729,18 +750,28 @@ namespace winrt::TerminalApp::implementation
std::vector<TmuxControl::TmuxWindowLayout> TmuxControl::_parseLayout(std::wstring_view remaining) std::vector<TmuxControl::TmuxWindowLayout> TmuxControl::_parseLayout(std::wstring_view remaining)
{ {
std::vector<TmuxControl::TmuxWindowLayout> result; std::vector<TmuxWindowLayout> result;
std::vector<TmuxWindowLayout> stack; std::vector<size_t> stack;
TmuxWindowLayout l;
// caff,120x29,0,0,2 stack.push_back(0);
// 5dc9,120x29,0,0{60x29,0,0,2,59x29,61,0[59x14,61,0,3,59x14,61,15,4]}
// Example layouts:
// * single pane:
// cafd,120x29,0,0,0
// * single horizontal split:
// 813e,120x29,0,0{60x29,0,0,0,59x29,61,0,1}
// * double horizontal split:
// 04d9,120x29,0,0{60x29,0,0,0,29x29,61,0,1,29x29,91,0,2}
// * double horizontal split + single vertical split in the middle pane:
// 773d,120x29,0,0{60x29,0,0,0,29x29,61,0[29x14,61,0,1,29x14,61,15,3],29x29,91,0,2}
remaining = L"773d,120x29,0,0{60x29,0,0,0,29x29,61,0[29x14,61,0,1,29x14,61,15,3],29x29,91,0,2}";
// Strip off the layout hash // Strip off the layout hash
const auto comma = remaining.find(L','); const auto comma = remaining.find(L',');
if (comma == std::wstring_view::npos) if (comma == std::wstring_view::npos)
{ {
return result; assert(false);
return {};
} }
remaining = remaining.substr(comma + 1); remaining = remaining.substr(comma + 1);
@ -759,7 +790,24 @@ namespace winrt::TerminalApp::implementation
{ {
// Failed to collect enough args? Error. // Failed to collect enough args? Error.
assert(false); assert(false);
return result; return {};
}
// If we're looking at a push/pop operation, break out. This is important
// for the latter, because nested layouts may end in `]]]`, etc.
sep = remaining[0];
if (sep == L'[' || sep == L']' || sep == L'{' || sep == L'}')
{
remaining = remaining.substr(1);
break;
}
// Skip 1 separator. Technically we should validate their correct position here, but meh.
if (sep == L',' || sep == L'x')
{
remaining = remaining.substr(1);
// We don't need to revalidate `remaining.empty()`,
// because parse_signed will return nullopt for empty strings.
} }
const auto end = std::min(remaining.size(), remaining.find_first_of(L",x[]{}")); const auto end = std::min(remaining.size(), remaining.find_first_of(L",x[]{}"));
@ -768,78 +816,55 @@ namespace winrt::TerminalApp::implementation
{ {
// Not an integer? Error. // Not an integer? Error.
assert(false); assert(false);
return result; return {};
} }
args[arg_count++] = *val; args[arg_count++] = *val;
sep = (end < remaining.size()) ? remaining[end] : L'\0'; remaining = remaining.substr(end);
remaining = remaining.substr(std::min(remaining.size(), end + 1));
if (sep == L'[' || sep == L']' || sep == L'{' || sep == L'}')
{
break;
}
} }
switch (sep) switch (sep)
{ {
case L'[': case L'[':
case L'{': case L'{':
{
if (arg_count != 4) if (arg_count != 4)
{ {
assert(false); assert(false);
return result; return {};
} }
const auto p = TmuxPaneLayout{ stack.push_back(result.size());
result.push_back(TmuxWindowLayout{
.type = sep == L'[' ? TmuxLayoutType::SPLIT_VERTICAL : TmuxLayoutType::SPLIT_HORIZONTAL,
.width = (til::CoordType)args[0], .width = (til::CoordType)args[0],
.height = (til::CoordType)args[1], .height = (til::CoordType)args[1],
.left = (til::CoordType)args[2], });
.top = (til::CoordType)args[3],
};
l.panes.push_back(p);
stack.push_back(l);
// New one
l.type = sep == L'[' ? TmuxLayoutType::SPLIT_VERTICAL : TmuxLayoutType::SPLIT_HORIZONTAL;
l.panes.clear();
l.panes.push_back(p);
break; break;
}
case L']': case L']':
case L'}': case L'}':
{
auto id = l.panes.back().id;
l.panes.pop_back();
l.panes.front().id = id;
result.insert(result.begin(), l);
l = stack.back();
l.panes.back().id = id;
stack.pop_back(); stack.pop_back();
break; break;
}
default: default:
{
if (arg_count != 5) if (arg_count != 5)
{ {
assert(false); assert(false);
return result; return {};
} }
const auto p = TmuxPaneLayout{ if (result.empty())
{
result.push_back(TmuxWindowLayout{
.type = TmuxLayoutType::SINGLE_PANE,
});
}
result[stack.back()].panes.push_back(TmuxPaneLayout{
.id = args[4],
.width = (til::CoordType)args[0], .width = (til::CoordType)args[0],
.height = (til::CoordType)args[1], .height = (til::CoordType)args[1],
.left = (til::CoordType)args[2], });
.top = (til::CoordType)args[3],
.id = args[4],
};
l.panes.push_back(p);
break; break;
} }
}
} }
return result; return result;
@ -847,12 +872,8 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseListWindow(std::wstring_view response) void TmuxControl::_handleResponseListWindow(std::wstring_view response)
{ {
std::wstring line; OutputDebugStringW(L"_handleResponseListWindow\n");
std::wregex REG_WINDOW{ L"^\\$(\\d+) @(\\d+) (\\d+) (\\d+) (\\d+) ([\\da-fA-F]{4}),(\\S+) (\\S+) (\\d+)$" };
std::vector<TmuxWindow> windows;
// $1 @1 80 24 2000 1 5dc9,120x29,0,0{60x29,0,0,2,59x29,61,0[59x14,61,0,3,59x14,61,15,4]} fish
// $2 @2 120 29 2000 1 caff,120x29,0,0,2 fish
while (!response.empty()) while (!response.empty())
{ {
auto line = split_line(response); auto line = split_line(response);
@ -872,105 +893,92 @@ namespace winrt::TerminalApp::implementation
!historyLimit || !historyLimit ||
!windowActive) !windowActive)
{ {
assert(false);
continue; continue;
} }
windows.push_back(TmuxWindow{ const auto layout = _parseLayout(windowLayout);
.sessionId = sessionId.value,
.windowId = windowId.value,
.width = (til::CoordType)*windowWidth,
.height = (til::CoordType)*windowHeight,
.history = (til::CoordType)*historyLimit,
.active = (bool)*windowActive,
.name = std::wstring{ windowName },
.layout = _parseLayout(windowLayout),
});
}
_state = State::ATTACHED;
for (auto& w : windows)
{
auto direction = SplitDirection::Left;
std::shared_ptr<Pane> rootPane{ nullptr }; std::shared_ptr<Pane> rootPane{ nullptr };
std::unordered_map<int64_t, std::shared_ptr<Pane>> attachedPanes; std::unordered_map<int64_t, std::shared_ptr<Pane>> attachedPanes;
for (auto& l : w.layout) for (auto& l : layout)
{ {
til::CoordType rootSize; til::CoordType rootSize;
auto& panes = l.panes; const auto& rootPaneLayout = l.panes.at(0);
auto& p = panes.at(0); auto direction = SplitDirection::Right;
switch (l.type) switch (l.type)
{ {
case TmuxLayoutType::SINGLE_PANE: case TmuxLayoutType::SINGLE_PANE:
rootPane = _newPane(w.windowId, p.id); rootPane = _newPane(windowId.value, rootPaneLayout.id);
continue; continue;
case TmuxLayoutType::SPLIT_HORIZONTAL: case TmuxLayoutType::SPLIT_HORIZONTAL:
direction = SplitDirection::Left; direction = SplitDirection::Right;
rootSize = p.width; rootSize = rootPaneLayout.width;
break; break;
case TmuxLayoutType::SPLIT_VERTICAL: case TmuxLayoutType::SPLIT_VERTICAL:
direction = SplitDirection::Up; direction = SplitDirection::Down;
rootSize = p.height; rootSize = rootPaneLayout.height;
break; break;
} }
auto search = attachedPanes.find(p.id);
std::shared_ptr<Pane> targetPane{ nullptr }; std::shared_ptr<Pane> targetPane{ nullptr };
auto targetPaneId = p.id;
if (search == attachedPanes.end()) if (const auto search = attachedPanes.find(rootPaneLayout.id); search == attachedPanes.end())
{ {
targetPane = _newPane(w.windowId, p.id); targetPane = _newPane(windowId.value, rootPaneLayout.id);
if (rootPane == nullptr) if (!rootPane)
{ {
rootPane = targetPane; rootPane = targetPane;
} }
attachedPanes.insert({ p.id, targetPane }); attachedPanes.insert({ rootPaneLayout.id, targetPane });
} }
else else
{ {
targetPane = search->second; targetPane = search->second;
} }
for (size_t i = 1; i < panes.size(); i++) for (size_t i = 1; i < l.panes.size(); i++)
{ {
// Create and attach // Create and attach
auto& p = panes.at(i); auto& p = l.panes.at(i);
auto pane = _newPane(w.windowId, p.id); auto pane = _newPane(windowId.value, p.id);
attachedPanes.insert({ p.id, pane }); attachedPanes.insert({ p.id, pane });
float splitSize; float splitSize;
if (direction == SplitDirection::Left) if (direction == SplitDirection::Left)
{ {
auto paneSize = panes.at(i).width; splitSize = _ComputeSplitSize(p.width, rootSize, direction);
splitSize = _ComputeSplitSize(paneSize, rootSize, direction); rootSize -= (p.width + 1);
rootSize -= (paneSize + 1);
} }
else else
{ {
auto paneSize = panes.at(i).height; splitSize = _ComputeSplitSize(p.height, rootSize, direction);
splitSize = _ComputeSplitSize(paneSize, rootSize, direction); rootSize -= (p.height + 1);
rootSize -= (paneSize + 1);
} }
targetPane = targetPane->AttachPane(pane, direction, splitSize); targetPane = targetPane->AttachPane(pane, direction, splitSize);
attachedPanes.erase(targetPaneId); attachedPanes.erase(p.id);
attachedPanes.insert({ targetPaneId, targetPane }); attachedPanes.insert({ p.id, targetPane });
targetPane->Closed([this, targetPaneId](auto&&, auto&&) { targetPane->Closed([this, id = p.id](auto&&, auto&&) {
_sendKillPane(targetPaneId); _sendKillPane(id);
}); });
} }
} }
auto tab = _page._GetTabImpl(_page._CreateNewTabFromPane(rootPane)); auto tab = _page._GetTabImpl(_page._CreateNewTabFromPane(rootPane));
_attachedWindows.emplace(w.windowId, tab); _attachedWindows.emplace(windowId.value, tab);
auto windowId = w.windowId;
tab->CloseRequested([this, windowId](auto&&, auto&&) { tab->CloseRequested([this, id = windowId.value](auto&&, auto&&) {
_sendKillWindow(windowId); _sendKillWindow(id);
}); });
tab->SetTabText(winrt::hstring{ w.name }); tab->SetTabText(winrt::hstring{ windowName });
_sendListPanes(w.windowId, w.history); _sendListPanes(windowId.value, *historyLimit);
} }
_state = State::ATTACHED;
} }
void TmuxControl::_sendListPanes(int64_t windowId, til::CoordType history) void TmuxControl::_sendListPanes(int64_t windowId, til::CoordType history)
@ -989,6 +997,8 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseListPanes(const ResponseInfo& info, std::wstring_view response) void TmuxControl::_handleResponseListPanes(const ResponseInfo& info, std::wstring_view response)
{ {
OutputDebugStringW(L"_handleResponseListPanes\n");
std::vector<TmuxPane> panes; std::vector<TmuxPane> panes;
while (!response.empty()) while (!response.empty())
@ -1006,6 +1016,10 @@ namespace winrt::TerminalApp::implementation
.cursorY = (til::CoordType)*cursorY, .cursorY = (til::CoordType)*cursorY,
}); });
} }
else
{
assert(false);
}
} }
for (auto& p : panes) for (auto& p : panes)
@ -1036,6 +1050,8 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_handleResponseCapturePane(const ResponseInfo& info, std::wstring response) void TmuxControl::_handleResponseCapturePane(const ResponseInfo& info, std::wstring response)
{ {
OutputDebugStringW(L"_handleResponseCapturePane\n");
fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"\033[{};{}H"), info.data.capturePane.cursorY + 1, info.data.capturePane.cursorX + 1); fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"\033[{};{}H"), info.data.capturePane.cursorY + 1, info.data.capturePane.cursorX + 1);
_deliverOutputToPane(info.data.capturePane.paneId, response); _deliverOutputToPane(info.data.capturePane.paneId, response);
} }
@ -1214,6 +1230,8 @@ namespace winrt::TerminalApp::implementation
return; return;
} }
OutputDebugStringW(fmt::format(FMT_COMPILE(L"_deliverOutputToPane({}, {})\n"), paneId, text).c_str());
std::wstring out; std::wstring out;
auto it = text.begin(); auto it = text.begin();
const auto end = text.end(); const auto end = text.end();
@ -1273,8 +1291,7 @@ namespace winrt::TerminalApp::implementation
return; return;
} }
auto& result = search->second; _deliverOutputToPane(paneId, search->second);
_deliverOutputToPane(paneId, result);
_outputBacklog.erase(search); _outputBacklog.erase(search);
} }
@ -1344,6 +1361,9 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_sendIgnoreResponse(wil::zwstring_view cmd) void TmuxControl::_sendIgnoreResponse(wil::zwstring_view cmd)
{ {
OutputDebugStringW(L">>> ");
OutputDebugStringW(cmd.c_str());
_control.RawWriteString(cmd); _control.RawWriteString(cmd);
_commandQueue.push_back(ResponseInfo{ _commandQueue.push_back(ResponseInfo{
.type = ResponseInfoType::Ignore, .type = ResponseInfoType::Ignore,
@ -1352,6 +1372,9 @@ namespace winrt::TerminalApp::implementation
void TmuxControl::_sendWithResponseInfo(wil::zwstring_view cmd, ResponseInfo info) void TmuxControl::_sendWithResponseInfo(wil::zwstring_view cmd, ResponseInfo info)
{ {
OutputDebugStringW(L">>> ");
OutputDebugStringW(cmd.c_str());
_control.RawWriteString(cmd); _control.RawWriteString(cmd);
_commandQueue.push_back(info); _commandQueue.push_back(info);
} }

View File

@ -71,16 +71,16 @@ namespace winrt::TerminalApp::implementation
struct TmuxPaneLayout struct TmuxPaneLayout
{ {
int64_t id;
til::CoordType width; til::CoordType width;
til::CoordType height; til::CoordType height;
til::CoordType left;
til::CoordType top;
int64_t id;
}; };
struct TmuxWindowLayout struct TmuxWindowLayout
{ {
TmuxLayoutType type = TmuxLayoutType::SINGLE_PANE; TmuxLayoutType type = TmuxLayoutType::SINGLE_PANE;
til::CoordType width;
til::CoordType height;
std::vector<TmuxPaneLayout> panes; std::vector<TmuxPaneLayout> panes;
}; };
@ -93,7 +93,6 @@ namespace winrt::TerminalApp::implementation
til::CoordType history = 0; til::CoordType history = 0;
bool active = false; bool active = false;
std::wstring name; std::wstring name;
std::wstring layoutChecksum;
std::vector<TmuxWindowLayout> layout; std::vector<TmuxWindowLayout> layout;
}; };
@ -117,8 +116,9 @@ namespace winrt::TerminalApp::implementation
}; };
static std::vector<TmuxControl::TmuxWindowLayout> _parseLayout(std::wstring_view remaining); static std::vector<TmuxControl::TmuxWindowLayout> _parseLayout(std::wstring_view remaining);
static std::vector<TmuxControl::TmuxWindowLayout> _ParseTmuxWindowLayout(std::wstring layout);
void _parseLine(std::wstring_view str); safe_void_coroutine _parseLine(std::wstring str);
void _handleAttach(); void _handleAttach();
void _handleDetach(); void _handleDetach();
@ -166,14 +166,14 @@ namespace winrt::TerminalApp::implementation
// Private variables // Private variables
TerminalPage& _page; // Non-owning, because TerminalPage owns us TerminalPage& _page; // Non-owning, because TerminalPage owns us
winrt::Windows::System::DispatcherQueue _dispatcherQueue{ nullptr };
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::com_ptr<Tab> _controlTab{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
State _state{ State::INIT }; State _state{ State::INIT };
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _newTabMenu; 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 _lineBuffer;
std::wstring _responseBuffer; std::wstring _responseBuffer;
winrt::event_token _detachKeyDownRevoker; winrt::event_token _detachKeyDownRevoker;