Compare commits

...

11 Commits

Author SHA1 Message Date
Leonard Hecker
f144427898 Merge remote-tracking branch 'origin/main' into dev/lhecker/18928-wip 2025-12-15 21:13:32 +01:00
Leonard Hecker
5f5a26fe9d wip 2025-12-15 21:08:59 +01:00
Leonard Hecker
cb79ab47fe wip 2025-12-15 21:08:35 +01:00
Leonard Hecker
671eb19ab8 wip 2025-12-11 17:46:30 +01:00
Leonard Hecker
500f4132b7 wip 2025-12-07 18:06:11 +01:00
Leonard Hecker
962461df32 wip 2025-12-06 16:46:11 +01:00
Leonard Hecker
835467888e wip 2025-12-05 15:10:32 +01:00
Leonard Hecker
55abb0ffdf wip 2025-12-04 19:00:29 +01:00
Leonard Hecker
d2dc2092a6 wip 2025-12-03 23:05:01 +01:00
Leonard Hecker
a804faedda wip 2025-12-02 23:47:24 +01:00
Leonard Hecker
ef23e6676c wip 2025-12-02 13:25:15 +01:00
66 changed files with 2292 additions and 94 deletions

View File

@@ -284,6 +284,15 @@ namespace winrt::TerminalApp::implementation
const auto& activeTab{ _senderOrFocusedTab(sender) };
if constexpr (Feature_TmuxControl::IsEnabled())
{
//Tmux control takes over
if (_tmuxControl && _tmuxControl->TabIsTmuxControl(activeTab))
{
return _tmuxControl->SplitPane(activeTab, realArgs.SplitDirection());
}
}
_SplitPane(activeTab,
realArgs.SplitDirection(),
// This is safe, we're already filtering so the value is (0, 1)

View File

@@ -120,9 +120,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
return ConnectionState::Failed;
}
void DebugTapConnection::_OutputHandler(const std::wstring_view str)
void DebugTapConnection::_OutputHandler(const winrt::array_view<const char16_t> str)
{
auto output = til::visualize_control_codes(str);
auto output = til::visualize_control_codes(winrt_array_to_wstring_view(str));
// To make the output easier to read, we introduce a line break whenever
// an LF control is encountered. But at this point, the LF would have
// been converted to U+240A (␊), so that's what we need to search for.
@@ -130,7 +130,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
{
output.insert(++lfPos, L"\r\n");
}
TerminalOutput.raise(output);
TerminalOutput.raise(winrt_wstring_to_array_view(output));
}
// Called by the DebugInputTapConnection to print user input
@@ -138,7 +138,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
{
auto clean{ til::visualize_control_codes(str) };
auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) };
TerminalOutput.raise(formatted);
TerminalOutput.raise(winrt_wstring_to_array_view(formatted));
}
// Wire us up so that we can forward input through

View File

@@ -31,7 +31,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
private:
void _PrintInput(const std::wstring_view data);
void _OutputHandler(const std::wstring_view str);
void _OutputHandler(const winrt::array_view<const char16_t> str);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::TerminalOutput_revoker _outputRevoker;
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::StateChanged_revoker _stateChangedRevoker;

View File

@@ -1306,10 +1306,10 @@ void Pane::UpdateSettings(const CascadiaSettings& settings)
// - splitType: How the pane should be attached
// Return Value:
// - the new reference to the child created from the current pane.
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitDirection splitType)
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitDirection splitType, const float splitSize)
{
// Splice the new pane into the tree
const auto [first, _] = _Split(splitType, .5, pane);
const auto [first, _] = _Split(splitType, splitSize, pane);
// If the new pane has a child that was the focus, re-focus it
// to steal focus from the currently focused pane.
@@ -2298,8 +2298,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
_firstChild->Closed(_firstClosedToken);
_secondChild->Closed(_secondClosedToken);
// If we are not a leaf we should create a new pane that contains our children
auto first = std::make_shared<Pane>(_firstChild, _secondChild, _splitState, _desiredSplitPosition);
_firstChild = first;
_firstChild = std::make_shared<Pane>(_firstChild, _secondChild, _splitState, _desiredSplitPosition);
}
else
{

View File

@@ -131,7 +131,8 @@ public:
void Close();
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);
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
int GetLeafPaneCount() const noexcept;

View File

@@ -982,4 +982,10 @@
<data name="InvalidRegex" xml:space="preserve">
<value>An invalid regular expression was found.</value>
</data>
</root>
<data name="TmuxControlInfo" xml:space="preserve">
<value>Running in tmux control mode; Press 'q' to detach:</value>
</data>
<data name="NewTmuxControlTab.Text" xml:space="preserve">
<value>Tmux Control Tab</value>
</data>
</root>

View File

@@ -933,6 +933,13 @@ namespace winrt::TerminalApp::implementation
return res;
}
void Tab::Close()
{
ASSERT_UI_THREAD();
Closed.raise(nullptr, nullptr);
}
// Method Description:
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
void Tab::Shutdown()

View File

@@ -61,6 +61,7 @@ namespace winrt::TerminalApp::implementation
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
void UpdateTitle();
void Close();
void Shutdown();
void ClosePane();

View File

@@ -22,6 +22,7 @@ namespace TerminalApp
UInt32 TabViewNumTabs;
void Focus(Windows.UI.Xaml.FocusState focusState);
void Close();
void Shutdown();
void SetDispatch(ShortcutActionDispatch dispatch);

View File

@@ -411,13 +411,11 @@ namespace winrt::TerminalApp::implementation
auto actions = t->BuildStartupActions(BuildStartupKind::None);
_AddPreviouslyClosedPaneOrTab(std::move(actions));
_RemoveTab(tab);
tab.Close();
}
// Method Description:
// - Removes the tab (both TerminalControl and XAML)
// Arguments:
// - tab: the tab to remove
// Removes the tab (both TerminalControl and XAML).
// NOTE: Don't call this directly, but rather `tab.Close()`.
void TerminalPage::_RemoveTab(const winrt::TerminalApp::Tab& tab)
{
uint32_t tabIndex{};

View File

@@ -173,6 +173,9 @@
<ClInclude Include="SettingsPaneContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TmuxControl.h">
<DependentUpon>TerminalPage.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Toast.h" />
<ClInclude Include="TerminalSettingsCache.h" />
<ClInclude Include="SuggestionsControl.h">
@@ -285,6 +288,9 @@
<ClCompile Include="SettingsPaneContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TmuxControl.cpp">
<DependentUpon>TerminalPage.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="Toast.cpp" />
<ClCompile Include="TerminalSettingsCache.cpp" />

View File

@@ -3,6 +3,7 @@
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natstepfilter" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
@@ -33,8 +34,7 @@
</ClCompile>
<ClCompile Include="Toast.cpp" />
<ClCompile Include="LanguageProfileNotifier.cpp" />
<ClCompile Include="Monarch.cpp" />
<ClCompile Include="Peasant.cpp" />
<ClCompile Include="TerminalSettingsCache.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -60,14 +60,13 @@
<ClInclude Include="fzf/fzf.h">
<Filter>fzf</Filter>
</ClInclude>
<ClInclude Include="fzf/LICENSE">
<Filter>fzf</Filter>
</ClInclude>
<ClInclude Include="Toast.h" />
<ClInclude Include="LanguageProfileNotifier.h" />
<ClInclude Include="WindowsPackageManagerFactory.h" />
<ClInclude Include="Monarch.h" />
<ClInclude Include="Peasant.h" />
<ClInclude Include="TerminalSettingsCache.h" />
<ClInclude Include="TmuxControl.h">
<Filter>controls</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="AppLogic.idl">
@@ -90,7 +89,8 @@
<Midl Include="TerminalWindow.idl" />
<Midl Include="TaskbarState.idl" />
<Midl Include="IPaneContent.idl" />
<Midl Include="Monarch.idl" />
<Midl Include="Remoting.idl" />
<Midl Include="HighlightedTextControl.idl" />
</ItemGroup>
<ItemGroup>
<Page Include="MinMaxCloseControl.xaml">
@@ -123,6 +123,7 @@
<Page Include="AboutDialog.xaml" />
<Page Include="SuggestionsControl.xaml" />
<Page Include="SnippetsPaneContent.xaml" />
<Page Include="MarkdownPaneContent.xaml" />
</ItemGroup>
<ItemGroup>
<Filter Include="app">
@@ -155,4 +156,4 @@
<Filter>app</Filter>
</ApplicationDefinition>
</ItemGroup>
</Project>
</Project>

View File

@@ -5,10 +5,14 @@
#include "pch.h"
#include "TerminalPage.h"
#include <TerminalThemeHelpers.h>
#include <Utils.h>
#include <TerminalCore/ControlKeyStates.hpp>
#include <TerminalThemeHelpers.h>
#include <til/hash.h>
#include <Utils.h>
#include "../../types/inc/ColorFix.hpp"
#include "../../types/inc/utils.hpp"
#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h"
#include "App.h"
#include "DebugTapConnection.h"
#include "MarkdownPaneContent.h"
@@ -18,9 +22,7 @@
#include "SnippetsPaneContent.h"
#include "TabRowControl.h"
#include "TerminalSettingsCache.h"
#include "../../types/inc/ColorFix.hpp"
#include "../../types/inc/utils.hpp"
#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h"
#include "TmuxControl.h"
#include "LaunchPositionRequest.g.cpp"
#include "RenameWindowRequestedArgs.g.cpp"
@@ -404,6 +406,15 @@ namespace winrt::TerminalApp::implementation
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
if constexpr (Feature_TmuxControl::IsEnabled())
{
// tmux control takes over
if (page->_tmuxControl && page->_tmuxControl->TabIsTmuxControl(page->_GetFocusedTabImpl()))
{
return;
}
}
page->_OpenNewTerminalViaDropdown(NewTerminalArgs());
}
});
@@ -1429,6 +1440,15 @@ namespace winrt::TerminalApp::implementation
}
if (altPressed && !debugTap)
{
// tmux control panes don't share tab with other panes
if constexpr (Feature_TmuxControl::IsEnabled())
{
if (_tmuxControl && _tmuxControl->TabIsTmuxControl(_GetFocusedTabImpl()))
{
return;
}
}
this->_SplitPane(_GetFocusedTabImpl(),
SplitDirection::Automatic,
0.5f,
@@ -2525,6 +2545,15 @@ namespace winrt::TerminalApp::implementation
return false;
}
if constexpr (Feature_TmuxControl::IsEnabled())
{
//Tmux control tab doesn't support to drag
if (_tmuxControl && _tmuxControl->TabIsTmuxControl(tab))
{
return false;
}
}
// If there was a windowId in the action, try to move it to the
// specified window instead of moving it in our tab row.
const auto windowId{ args.Window() };
@@ -3581,6 +3610,26 @@ namespace winrt::TerminalApp::implementation
original->SetActive();
}
if constexpr (Feature_TmuxControl::IsEnabled())
{
if (!_tmuxControl)
{
_tmuxControl = std::make_shared<TmuxControl>(*this);
}
control.EnterTmuxControl([tmuxControl = _tmuxControl.get()](auto&& sender, auto&& args) {
if (auto control = sender.try_as<TermControl>())
{
if (tmuxControl->AcquireSingleUseLock(std::move(control)))
{
args.InputCallback([tmuxControl](auto&& str) {
tmuxControl->FeedInput(winrt_array_to_wstring_view(str));
});
}
}
});
}
return resultPane;
}
@@ -5000,9 +5049,10 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_adjustProcessPriority() const
{
// Windowing is single-threaded, so this will not cause a race condition.
static bool supported{ true };
static uint64_t s_lastUpdateHash{ 0 };
static bool s_supported{ true };
if (!supported || !_hostingHwnd.has_value())
if (!s_supported || !_hostingHwnd.has_value())
{
return;
}
@@ -5066,11 +5116,20 @@ namespace winrt::TerminalApp::implementation
}
const auto count{ gsl::narrow_cast<DWORD>(it - processes.begin()) };
const auto hash = til::hash((void*)processes.data(), count * sizeof(HANDLE));
if (hash == s_lastUpdateHash)
{
return;
}
s_lastUpdateHash = hash;
const auto hr = TerminalTrySetWindowAssociatedProcesses(_hostingHwnd.value(), count, count ? processes.data() : nullptr);
if (S_FALSE == hr)
{
// Don't bother trying again or logging. The wrapper tells us it's unsupported.
supported = false;
s_supported = false;
return;
}
@@ -5468,6 +5527,15 @@ namespace winrt::TerminalApp::implementation
tabImpl.copy_from(winrt::get_self<Tab>(tabBase));
if (tabImpl)
{
if constexpr (Feature_TmuxControl::IsEnabled())
{
//Tmux control tab doesn't support to drag
if (_tmuxControl && _tmuxControl->TabIsTmuxControl(tabImpl))
{
return;
}
}
// First: stash the tab we started dragging.
// We're going to be asked for this.
_stashed.draggedTab = tabImpl;

View File

@@ -12,6 +12,7 @@
#include "RenameWindowRequestedArgs.g.h"
#include "RequestMoveContentArgs.g.h"
#include "LaunchPositionRequest.g.h"
#include "TmuxControl.h"
#include "Toast.h"
#include "WindowsPackageManagerFactory.h"
@@ -256,6 +257,7 @@ namespace winrt::TerminalApp::implementation
std::vector<std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>> _previouslyClosedPanesAndTabs{};
uint32_t _systemRowsToScroll{ DefaultRowsToScroll };
std::shared_ptr<TmuxControl> _tmuxControl{ nullptr };
// use a weak reference to prevent circular dependency with AppLogic
winrt::weak_ref<winrt::TerminalApp::IDialogPresenter> _dialogPresenter;
@@ -580,6 +582,7 @@ namespace winrt::TerminalApp::implementation
friend class TerminalAppLocalTests::TabTests;
friend class TerminalAppLocalTests::SettingsTests;
friend class TmuxControl;
};
}

View File

@@ -68,6 +68,8 @@ namespace winrt::TerminalApp::implementation
{
_removeControlEvents();
_control.Close();
// Clear out our media player callbacks, and stop any playing media. This
// will prevent the callback from being triggered after we've closed, and
// also make sure that our sound stops when we're closed.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <til/mutex.h>
#include "Pane.h"
namespace winrt::TerminalApp::implementation
{
struct TerminalPage;
class TmuxControl : public std::enable_shared_from_this<TmuxControl>
{
public:
TmuxControl(TerminalPage& page);
bool AcquireSingleUseLock(winrt::Microsoft::Terminal::Control::TermControl control) noexcept;
bool TabIsTmuxControl(const winrt::com_ptr<Tab>& tab);
void SplitPane(const winrt::com_ptr<Tab>& tab, winrt::Microsoft::Terminal::Settings::Model::SplitDirection direction);
void FeedInput(std::wstring_view str);
private:
enum class State
{
Init,
Attaching,
Attached,
};
enum class ResponseInfoType
{
Ignore,
DiscoverNewWindow,
DiscoverWindows,
CapturePane,
DiscoverPanes,
};
struct ResponseInfo
{
ResponseInfoType type;
union
{
struct
{
int64_t paneId;
} capturePane;
} data;
};
enum class TmuxLayoutType
{
// A single leaf pane
Pane,
// Indicates the start of a horizontal split layout
PushHorizontal,
// Indicates the start of a vertical split layout
PushVertical,
// Indicates the end of the most recent split layout
Pop,
};
struct TmuxLayout
{
TmuxLayoutType type = TmuxLayoutType::Pane;
// Only set for: Pane, PushHorizontal, PushVertical
til::CoordType width = 0;
// Only set for: Pane, PushHorizontal, PushVertical
til::CoordType height = 0;
// Only set for: Pane
int64_t id = -1;
};
// AttachedPane should not need to be copied. Anything else would be a mistake.
// But if we added a constructor to it, we could not use designated initializers anymore.
// This marker makes it possible.
struct MoveOnlyMarker
{
MoveOnlyMarker() = default;
MoveOnlyMarker(MoveOnlyMarker&&) = default;
MoveOnlyMarker& operator=(MoveOnlyMarker&&) = default;
MoveOnlyMarker(const MoveOnlyMarker&) = delete;
MoveOnlyMarker& operator=(const MoveOnlyMarker&) = delete;
};
struct AttachedPane
{
int64_t windowId = -1;
int64_t paneId = -1;
winrt::Microsoft::Terminal::TerminalConnection::TmuxConnection connection{ nullptr };
winrt::Microsoft::Terminal::Control::TermControl control{ nullptr };
std::wstring outputBacklog;
bool initialized = false;
bool ignoreOutput = false;
[[msvc::no_unique_address]] MoveOnlyMarker moveOnlyMarker;
};
safe_void_coroutine _parseLine(std::wstring line);
void _handleAttach(); // A special case of _handleResponse()
void _handleDetach();
void _handleSessionChanged(int64_t sessionId);
void _handleWindowAdd(int64_t windowId);
void _handleWindowRenamed(int64_t windowId, winrt::hstring name);
void _handleWindowClose(int64_t windowId);
void _handleWindowPaneChanged(int64_t windowId, int64_t paneId);
void _handleLayoutChange(int64_t windowId, std::wstring_view layout);
void _handleResponse(std::wstring_view result);
void _sendSetOption(std::wstring_view option);
void _sendDiscoverWindows(int64_t sessionId);
void _handleResponseDiscoverWindows(std::wstring_view response);
void _sendDiscoverNewWindow(int64_t windowId);
void _handleResponseDiscoverNewWindow(std::wstring_view response);
void _sendCapturePane(int64_t paneId, til::CoordType history);
void _handleResponseCapturePane(const ResponseInfo& info, std::wstring_view response);
void _sendDiscoverPanes(int64_t windowId);
void _handleResponseDiscoverPanes(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);
void _sendIgnoreResponse(wil::zwstring_view cmd);
void _sendWithResponseInfo(wil::zwstring_view cmd, ResponseInfo info);
std::shared_ptr<Pane> _layoutCreateRecursive(int64_t windowId, std::wstring_view& remaining, TmuxLayout parent);
std::wstring_view _layoutStripHash(std::wstring_view str);
TmuxLayout _layoutParseNextToken(std::wstring_view& remaining);
void _deliverOutputToPane(int64_t paneId, const std::wstring_view text);
winrt::com_ptr<Tab> _getTab(int64_t windowId) const;
void _newTab(int64_t windowId, winrt::hstring name, std::shared_ptr<Pane> pane);
std::pair<AttachedPane&, std::shared_ptr<Pane>> _newPane(int64_t windowId, int64_t paneId);
void _openNewTerminalViaDropdown();
TerminalPage& _page; // Non-owning, because TerminalPage owns us
winrt::Windows::System::DispatcherQueue _dispatcherQueue{ nullptr };
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _newTabMenu;
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;
bool _inUse = false;
std::wstring _lineBuffer;
std::wstring _responseBuffer;
bool _insideOutputBlock = false;
winrt::event_token _detachKeyDownRevoker;
winrt::event_token _windowSizeChangedRevoker;
winrt::event_token _newTabClickRevoker;
std::deque<ResponseInfo> _commandQueue;
std::unordered_map<int64_t, AttachedPane> _attachedPanes;
std::unordered_map<int64_t, winrt::com_ptr<Tab>> _attachedWindows;
int64_t _sessionId = -1;
int64_t _activePaneId = -1;
int64_t _activeWindowId = -1;
til::CoordType _terminalWidth = 0;
til::CoordType _terminalHeight = 0;
winrt::Windows::UI::Xaml::Thickness _thickness{ 0, 0, 0, 0 };
float _fontWidth = 0;
float _fontHeight = 0;
std::pair<std::shared_ptr<Pane>, winrt::Microsoft::Terminal::Settings::Model::SplitDirection> _splittingPane{
nullptr,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection::Right,
};
};
}

View File

@@ -94,9 +94,15 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - helper that will write an unterminated string (generally, from a resource) to the output stream.
// Arguments:
// - str: the string to write.
void AzureConnection::_WriteStringWithNewline(std::wstring str)
{
str.append(L"\r\n");
TerminalOutput.raise(winrt_wstring_to_array_view(str));
}
void AzureConnection::_WriteStringWithNewline(const std::wstring_view str)
{
TerminalOutput.raise(str + L"\r\n");
TerminalOutput.raise(winrt_wstring_to_array_view(str + L"\r\n"));
}
// Method description:
@@ -112,7 +118,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
catch (const std::exception& runtimeException)
{
// This also catches the AzureException, which has a .what()
TerminalOutput.raise(_colorize(91, til::u8u16(std::string{ runtimeException.what() })));
TerminalOutput.raise(winrt_wstring_to_array_view(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))));
}
catch (...)
{
@@ -162,13 +168,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_currentInputMode = mode;
TerminalOutput.raise(L"> \x1b[92m"); // Make prompted user input green
TerminalOutput.raise(winrt_wstring_to_array_view(L"> \x1b[92m")); // Make prompted user input green
_inputEvent.wait(inputLock, [this, mode]() {
return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing);
});
TerminalOutput.raise(L"\x1b[m");
TerminalOutput.raise(winrt_wstring_to_array_view(L"\x1b[m"));
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
@@ -211,19 +217,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_userInput.size() > 0)
{
_userInput.pop_back();
TerminalOutput.raise(L"\x08 \x08"); // overstrike the character with a space
TerminalOutput.raise(winrt_wstring_to_array_view(L"\x08 \x08")); // overstrike the character with a space
}
}
else
{
TerminalOutput.raise(data); // echo back
TerminalOutput.raise(winrt_wstring_to_array_view(data)); // echo back
switch (_currentInputMode)
{
case InputMode::Line:
if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN)
{
TerminalOutput.raise(L"\r\n"); // we probably got a \r, so we need to advance to the next line.
TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n")); // we probably got a \r, so we need to advance to the next line.
_currentInputMode = InputMode::None; // toggling the mode indicates completion
_inputEvent.notify_one();
break;
@@ -429,7 +435,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
// Pass the output to our registered event handlers
TerminalOutput.raise(_u16Str);
TerminalOutput.raise(winrt_wstring_to_array_view(_u16Str));
break;
}
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
@@ -772,7 +778,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto shellType = _ParsePreferredShellType(settingsResponse);
_WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
const auto socketUri = _GetTerminal(shellType);
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n"));
//// Step 8: connecting to said terminal
{

View File

@@ -67,6 +67,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant;
void _writeInput(const std::wstring_view str);
void _WriteStringWithNewline(std::wstring str);
void _WriteStringWithNewline(const std::wstring_view str);
void _WriteCaughtExceptionRecord();
winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr, const winrt::Windows::Foundation::Uri referer = nullptr);

View File

@@ -477,31 +477,28 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto hr = wil::ResultFromCaughtException();
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
const auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline);
TerminalOutput.raise(failureText);
auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline);
// If the path was invalid, let's present an informative message to the user
if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
{
const auto badPathText = RS_fmt(L"BadPathText", _startingDirectory);
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(badPathText);
failureText.append(L"\r\n");
failureText.append(RS_fmt(L"BadPathText", _startingDirectory));
}
// If the requested action requires elevation, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED))
{
const auto elevationText = RS_(L"ElevationRequired");
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(elevationText);
failureText.append(L"\r\n");
failureText.append(RS_(L"ElevationRequired"));
}
// If the requested executable was not found, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
const auto fileNotFoundText = RS_(L"FileNotFound");
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(fileNotFoundText);
failureText.append(L"\r\n");
failureText.append(RS_(L"FileNotFound"));
}
TerminalOutput.raise(winrt_wstring_to_array_view(failureText));
_transitionToState(ConnectionState::Failed);
// Tear down any state we may have accumulated.
@@ -520,7 +517,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto msg1 = RS_fmt(L"ProcessExited", _formatStatus(status));
const auto msg2 = RS_(L"CtrlDToClose");
const auto msg = fmt::format(FMT_COMPILE(L"\r\n{}\r\n{}\r\n"), msg1, msg2);
TerminalOutput.raise(msg);
TerminalOutput.raise(winrt_wstring_to_array_view(msg));
}
CATCH_LOG();
}
@@ -792,7 +789,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
try
{
TerminalOutput.raise(wstr);
TerminalOutput.raise(winrt_wstring_to_array_view(wstr));
}
CATCH_LOG();
}

View File

@@ -34,7 +34,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
prettyPrint << wch;
}
}
TerminalOutput.raise(prettyPrint.str());
TerminalOutput.raise(winrt_wstring_to_array_view(prettyPrint.str()));
}
void EchoConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept

View File

@@ -13,7 +13,7 @@ namespace Microsoft.Terminal.TerminalConnection
Failed
};
delegate void TerminalOutputHandler(String output);
delegate void TerminalOutputHandler(Char[] output);
interface ITerminalConnection
{

View File

@@ -33,6 +33,9 @@
<ClInclude Include="EchoConnection.h">
<DependentUpon>EchoConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TmuxConnection.h">
<DependentUpon>TmuxConnection.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CTerminalHandoff.cpp" />
@@ -52,6 +55,9 @@
<ClCompile Include="ConptyConnection.cpp">
<DependentUpon>ConptyConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TmuxConnection.cpp">
<DependentUpon>TmuxConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
@@ -60,6 +66,7 @@
<Midl Include="ConptyConnection.idl" />
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="TmuxConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw">
@@ -99,4 +106,4 @@
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
</Project>
</Project>

View File

@@ -19,6 +19,7 @@
<ClCompile Include="AzureConnection.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="CTerminalHandoff.cpp" />
<ClCompile Include="TmuxConnection.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -27,6 +28,7 @@
<ClInclude Include="AzureClientID.h" />
<ClInclude Include="CTerminalHandoff.h" />
<ClInclude Include="BaseTerminalConnection.h" />
<ClInclude Include="TmuxConnection.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="ITerminalConnection.idl" />
@@ -34,6 +36,7 @@
<Midl Include="AzureConnection.idl" />
<Midl Include="ConptyConnection.idl" />
<Midl Include="ConnectionInformation.idl" />
<Midl Include="TmuxConnection.idl" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
@@ -42,4 +45,4 @@
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
</ItemGroup>
</Project>
</Project>

View File

@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TmuxConnection.h"
#include <sstream>
#include "TmuxConnection.g.cpp"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
TmuxConnection::TmuxConnection() noexcept = default;
void TmuxConnection::Initialize(const Windows::Foundation::Collections::ValueSet&) const noexcept
{
}
void TmuxConnection::Start() noexcept
{
}
void TmuxConnection::WriteInput(const winrt::array_view<const char16_t> buffer)
{
TerminalInput.raise(buffer);
}
void TmuxConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept
{
}
void TmuxConnection::Close() noexcept
{
StateChanged.raise(*this, nullptr);
}
winrt::guid TmuxConnection::SessionId() const noexcept
{
return {};
}
ConnectionState TmuxConnection::State() const noexcept
{
return ConnectionState::Connected;
}
void TmuxConnection::WriteOutput(const winrt::array_view<const char16_t> wstr)
{
if (!wstr.empty())
{
TerminalOutput.raise(wstr);
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "TmuxConnection.g.h"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct TmuxConnection : TmuxConnectionT<TmuxConnection>
{
TmuxConnection() noexcept;
// ---- ITerminalConnection methods ----
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) const noexcept;
void Start() noexcept;
void WriteInput(const winrt::array_view<const char16_t> buffer);
void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept;
til::event<TerminalOutputHandler> TerminalOutput;
til::typed_event<ITerminalConnection, IInspectable> StateChanged;
winrt::guid SessionId() const noexcept;
ConnectionState State() const noexcept;
// ---- TmuxConnection methods ----
void WriteOutput(const winrt::array_view<const char16_t> wstr);
til::event<TerminalOutputHandler> TerminalInput;
};
}
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
{
BASIC_FACTORY(TmuxConnection);
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection
{
[default_interface]
runtimeclass TmuxConnection : ITerminalConnection
{
TmuxConnection();
void WriteOutput(Char[] data);
event TerminalOutputHandler TerminalInput;
};
}

View File

@@ -10,13 +10,12 @@
#include <DefaultSettings.h>
#include <unicode.hpp>
#include <utils.hpp>
#include <WinUser.h>
#include "EventArgs.h"
#include "../../renderer/atlas/AtlasEngine.h"
#include "../../renderer/base/renderer.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
#include "../../terminal/adapter/adaptDispatch.hpp"
#include "../../types/inc/CodepointWidthDetector.hpp"
#include "../../types/inc/utils.hpp"
@@ -141,6 +140,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnWindowSizeChanged = [this](auto&& PH1, auto&& PH2) { _terminalWindowSizeChanged(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2)); };
_terminal->SetWindowSizeChangedCallback(pfnWindowSizeChanged);
_terminal->SetEnterTmuxControlCallback([this]() -> std::function<bool(wchar_t)> {
const auto args = winrt::make_self<EnterTmuxControlEventArgs>();
EnterTmuxControl.raise(*this, *args);
if (auto inputCallback = args->InputCallback())
{
return [inputCallback = std::move(inputCallback)](wchar_t ch) -> bool {
const auto c16 = static_cast<char16_t>(ch);
inputCallback({ &c16, 1 });
return true;
};
}
return nullptr;
});
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -1461,6 +1474,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->TrySnapOnInput();
}
void ControlCore::InjectTextAtCursor(const winrt::hstring& text)
{
if (text.empty())
{
return;
}
const auto lock = _terminal->LockForWriting();
std::wstring_view remaining{ text };
// Process one line at a time
for (;;)
{
// Get the (CR)LF position
const auto lf = std::min(remaining.size(), remaining.find(L'\n'));
// Strip off the CR
auto lineEnd = lf;
if (lineEnd != 0 && remaining[lineEnd - 1] == L'\r')
{
lineEnd -= 1;
}
// Split into line and whatever comes after
const auto line = remaining.substr(0, lineEnd);
remaining = remaining.substr(std::min(remaining.size(), lf + 1));
// This will not just print the line but also handle delay wrap, etc.
_terminal->GetAdaptDispatch().PrintString(line);
if (remaining.empty())
{
break;
}
_terminal->GetAdaptDispatch().LineFeed(DispatchTypes::LineFeedType::DependsOnMode);
}
}
FontInfo ControlCore::GetFont() const
{
return _actualFont;
@@ -1570,6 +1622,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal->GetViewport().Height();
}
// Function Description:
// - Gets the width of the terminal in lines of text. This is just the
// width of the viewport.
// Return Value:
// - The width of the terminal in lines of text
int ControlCore::ViewWidth() const
{
const auto lock = _terminal->LockForReading();
return _terminal->GetViewport().Width();
}
// Function Description:
// - Gets the height of the terminal in lines of text. This includes the
// history AND the viewport.
@@ -2238,13 +2301,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Info, RS_(L"TermControlReadOnly"));
RaiseNotice.raise(*this, std::move(noticeArgs));
}
void ControlCore::_connectionOutputHandler(const hstring& hstr)
void ControlCore::_connectionOutputHandler(const winrt::array_view<const char16_t> str)
{
try
{
{
const auto lock = _terminal->LockForWriting();
_terminal->Write(hstr);
_terminal->Write(winrt_array_to_wstring_view(str));
}
if (!_pendingResponses.empty())

View File

@@ -23,6 +23,7 @@
#include "../../buffer/out/search.h"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../../renderer/inc/FontInfoDesired.hpp"
#include "../../terminal/adapter/ITermDispatch.hpp"
namespace Microsoft::Console::Render::Atlas
{
@@ -124,6 +125,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SendInput(std::wstring_view wstr);
void PasteText(const winrt::hstring& hstr);
void InjectTextAtCursor(const winrt::hstring& text);
bool CopySelectionToClipboard(bool singleLine, bool withControlSequences, const CopyFormat formats);
void SelectAll();
void ClearSelection();
@@ -172,6 +174,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
int ScrollOffset();
int ViewHeight() const;
int ViewWidth() const;
int BufferHeight() const;
bool HasSelection() const;
@@ -295,10 +298,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<> RefreshQuickFixUI;
til::typed_event<IInspectable, Control::WindowSizeChangedEventArgs> WindowSizeChanged;
til::typed_event<IInspectable, Control::EnterTmuxControlEventArgs> EnterTmuxControl;
til::typed_event<> CloseTerminalRequested;
til::typed_event<> RestartTerminalRequested;
til::typed_event<> Attached;
// clang-format on
@@ -349,7 +351,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _raiseReadOnlyWarning();
void _updateAntiAliasingMode();
void _connectionOutputHandler(const hstring& hstr);
void _connectionOutputHandler(winrt::array_view<const char16_t> str);
void _connectionStateChangedHandler(const TerminalConnection::ITerminalConnection&, const Windows::Foundation::IInspectable&);
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
void _setOpacity(const float opacity, const bool focused = true);

View File

@@ -67,6 +67,10 @@ namespace Microsoft.Terminal.Control
Boolean SearchRegexInvalid;
};
delegate Boolean TmuxDCSHandler(Char ch);
delegate void PrintHandler(String str);
delegate TmuxDCSHandler TmuxDCSHandlerProducer(PrintHandler print);
[default_interface] runtimeclass SelectionColor
{
SelectionColor();
@@ -128,6 +132,7 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Core.ControlKeyStates modifiers);
void SendInput(String text);
void PasteText(String text);
void InjectTextAtCursor(String text);
void SelectAll();
void ClearSelection();
Boolean ToggleBlockSelection();
@@ -198,6 +203,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
event Windows.Foundation.TypedEventHandler<Object, Object> RefreshQuickFixUI;
event Windows.Foundation.TypedEventHandler<Object, WindowSizeChangedEventArgs> WindowSizeChanged;
event Windows.Foundation.TypedEventHandler<Object, EnterTmuxControlEventArgs> EnterTmuxControl;
// These events are always called from the UI thread (bugs aside)
event Windows.Foundation.TypedEventHandler<Object, FontSizeChangedArgs> FontSizeChanged;

View File

@@ -21,3 +21,4 @@
#include "StringSentEventArgs.g.cpp"
#include "SearchMissingCommandEventArgs.g.cpp"
#include "WindowSizeChangedEventArgs.g.cpp"
#include "EnterTmuxControlEventArgs.g.cpp"

View File

@@ -21,6 +21,7 @@
#include "StringSentEventArgs.g.h"
#include "SearchMissingCommandEventArgs.g.h"
#include "WindowSizeChangedEventArgs.g.h"
#include "EnterTmuxControlEventArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@@ -265,6 +266,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(int32_t, Width);
WINRT_PROPERTY(int32_t, Height);
};
struct EnterTmuxControlEventArgs : public EnterTmuxControlEventArgsT<EnterTmuxControlEventArgs>
{
til::property<TmuxControlInputCallback> InputCallback;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@@ -159,4 +159,11 @@ namespace Microsoft.Terminal.Control
Int32 Width;
Int32 Height;
}
delegate void TmuxControlInputCallback(Char[] input);
runtimeclass EnterTmuxControlEventArgs
{
void InputCallback(TmuxControlInputCallback callback);
}
}

View File

@@ -40,6 +40,7 @@ namespace Microsoft.Terminal.Control
Int32 ScrollOffset { get; };
Int32 ViewHeight { get; };
Int32 ViewWidth { get; };
Int32 BufferHeight { get; };
Boolean HasSelection { get; };

View File

@@ -330,6 +330,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 });
@@ -1506,6 +1507,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.SendInput(text);
}
void TermControl::InjectTextAtCursor(const winrt::hstring& text)
{
_core.InjectTextAtCursor(text);
}
// Method Description:
// - Manually handles key events for certain keys that can't be passed to us
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
@@ -2667,6 +2673,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.ViewHeight();
}
int TermControl::ViewWidth() const
{
return _core.ViewWidth();
}
int TermControl::BufferHeight() const
{
return _core.BufferHeight();
@@ -2866,7 +2877,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else
{
// Do we ever get here (= uninitialized terminal)? If so: How?
assert(false);
// Yes, we can get here, when do Pane._Split, it need to call _SetupEntranceAnimation^M
// which need the control's size, while this size can only be available when the control^M
// is initialized.^M
return { 10, 10 };
}
}
@@ -4002,6 +4015,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

@@ -97,6 +97,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
int ScrollOffset() const;
int ViewHeight() const;
int ViewWidth() const;
int BufferHeight() const;
bool HasSelection() const;
@@ -182,6 +183,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool RawWriteKeyEvent(const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
bool RawWriteChar(const wchar_t character, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void RawWriteString(const winrt::hstring& text);
void InjectTextAtCursor(const winrt::hstring& text);
void ShowContextMenu();
bool OpenQuickFixMenu();
@@ -217,6 +219,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
@@ -437,6 +440,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);
@@ -471,6 +475,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;

View File

@@ -74,6 +74,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
event Windows.Foundation.TypedEventHandler<Object, WindowSizeChangedEventArgs> WindowSizeChanged;
event Windows.Foundation.TypedEventHandler<Object, EnterTmuxControlEventArgs> EnterTmuxControl;
event Windows.Foundation.TypedEventHandler<Object, CompletionsChangedEventArgs> CompletionsChanged;
@@ -130,6 +131,7 @@ namespace Microsoft.Terminal.Control
Boolean RawWriteKeyEvent(UInt16 vkey, UInt16 scanCode, Microsoft.Terminal.Core.ControlKeyStates modifiers, Boolean keyDown);
Boolean RawWriteChar(Char character, UInt16 scanCode, Microsoft.Terminal.Core.ControlKeyStates modifiers);
void RawWriteString(String text);
void InjectTextAtCursor(String text);
void BellLightOn();

View File

@@ -51,6 +51,7 @@ void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Re
_mainBuffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, true, &renderer);
auto dispatch = std::make_unique<AdaptDispatch>(*this, &renderer, _renderSettings, _terminalInput);
_adaptDispatch = dispatch.get();
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
_stateMachine = std::make_unique<StateMachine>(std::move(engine));
}
@@ -1039,6 +1040,12 @@ bool Terminal::IsFocused() const noexcept
return _focused;
}
AdaptDispatch& Microsoft::Terminal::Core::Terminal::GetAdaptDispatch() noexcept
{
_assertLocked();
return *_adaptDispatch;
}
RenderSettings& Terminal::GetRenderSettings() noexcept
{
_assertLocked();
@@ -1270,6 +1277,11 @@ void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::f
_pfnSearchMissingCommand.swap(pfn);
}
void Terminal::SetEnterTmuxControlCallback(std::function<std::function<bool(wchar_t)>()> pfn) noexcept
{
_pfnEnterTmuxControl = std::move(pfn);
}
void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function<void()> pfn) noexcept
{
_pfnClearQuickFix.swap(pfn);

View File

@@ -116,6 +116,7 @@ public:
int ViewEndIndex() const noexcept;
bool IsFocused() const noexcept;
::Microsoft::Console::VirtualTerminal::AdaptDispatch& GetAdaptDispatch() noexcept;
RenderSettings& GetRenderSettings() noexcept;
const RenderSettings& GetRenderSettings() const noexcept;
@@ -153,14 +154,12 @@ public:
void ShowWindow(bool showOrHide) override;
void UseAlternateScreenBuffer(const TextAttribute& attrs) override;
void UseMainScreenBuffer() override;
bool IsVtInputEnabled() const noexcept override;
void NotifyBufferRotation(const int delta) override;
void NotifyShellIntegrationMark() override;
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(const std::wstring_view command) override;
std::function<bool(wchar_t)> EnterTmuxControl() override;
#pragma endregion
@@ -230,6 +229,7 @@ public:
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
void SetSearchMissingCommandCallback(std::function<void(std::wstring_view, const til::CoordType)> pfn) noexcept;
void SetEnterTmuxControlCallback(std::function<std::function<bool(wchar_t)>()> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
@@ -338,10 +338,12 @@ private:
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
std::function<void(std::wstring_view, const til::CoordType)> _pfnSearchMissingCommand;
std::function<std::function<bool(wchar_t)>()> _pfnEnterTmuxControl;
std::function<void()> _pfnClearQuickFix;
std::function<void(int32_t, int32_t)> _pfnWindowSizeChanged;
RenderSettings _renderSettings;
::Microsoft::Console::VirtualTerminal::AdaptDispatch* _adaptDispatch;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
::Microsoft::Console::VirtualTerminal::TerminalInput _terminalInput;

View File

@@ -364,6 +364,11 @@ void Terminal::SearchMissingCommand(const std::wstring_view command)
}
}
std::function<bool(wchar_t)> Terminal::EnterTmuxControl()
{
return _pfnEnterTmuxControl ? _pfnEnterTmuxControl() : nullptr;
}
void Terminal::NotifyBufferRotation(const int delta)
{
// Update our selection, so it doesn't move as the buffer is cycled

View File

@@ -352,6 +352,7 @@ namespace winrt::Microsoft::Terminal::Settings
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
_AllowVtClipboardWrite = profile.AllowVtClipboardWrite();
_PathTranslationStyle = profile.PathTranslationStyle();
_AllowTmuxControl = profile.AllowTmuxControl();
}
// Method Description:

View File

@@ -92,6 +92,7 @@ namespace winrt::Microsoft::Terminal::Settings
SIMPLE_OVERRIDABLE_SETTING(bool, Elevate, false);
SIMPLE_OVERRIDABLE_SETTING(IEnvironmentVariableMapView, EnvironmentVariables, nullptr);
SIMPLE_OVERRIDABLE_SETTING(bool, ReloadEnvironmentVariables, true);
SIMPLE_OVERRIDABLE_SETTING(bool, AllowTmuxControl, false);
public:
// TerminalApp overrides these when duplicating a session

View File

@@ -28,10 +28,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
PreviewConnection::PreviewConnection() noexcept = default;
void PreviewConnection::Start() noexcept
void PreviewConnection::Start()
{
// Send the preview text
TerminalOutput.raise(fmt::format(PreviewText, _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain));
const auto prompt = _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain;
const auto text = fmt::format(FMT_COMPILE(PreviewText), prompt);
TerminalOutput.raise(winrt_wstring_to_array_view(text));
}
void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept
@@ -50,7 +51,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
}
void PreviewConnection::DisplayPowerlineGlyphs(bool d) noexcept
void PreviewConnection::DisplayPowerlineGlyphs(bool d)
{
if (_displayPowerlineGlyphs != d)
{

View File

@@ -22,12 +22,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
PreviewConnection() noexcept;
void Initialize(const Windows::Foundation::Collections::ValueSet& settings) noexcept;
void Start() noexcept;
void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer);
void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept;
void DisplayPowerlineGlyphs(bool d) noexcept;
void DisplayPowerlineGlyphs(bool d);
winrt::guid SessionId() const noexcept { return {}; }
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; }

View File

@@ -188,6 +188,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_parsedPadding = StringToXamlThickness(_profile.Padding());
_defaultAppearanceViewModel.IsDefault(true);
if constexpr (Feature_TmuxControl::IsEnabled())
{
TmuxControlEnabled(true);
}
}
void ProfileViewModel::_UpdateBuiltInIcons()

View File

@@ -171,9 +171,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions);
OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle);
OBSERVABLE_PROJECTED_SETTING(_profile, AllowTmuxControl);
WINRT_PROPERTY(bool, IsBaseLayer, false);
WINRT_PROPERTY(bool, FocusDeleteButton, false);
WINRT_PROPERTY(bool, TmuxControlEnabled, false);
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable>, IconTypes);
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);

View File

@@ -114,6 +114,7 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean UsingBuiltInIcon { get; };
Boolean UsingEmojiIcon { get; };
Boolean UsingImageIcon { get; };
Boolean TmuxControlEnabled;
String IconPath;
EnumEntry CurrentBuiltInIcon;
@@ -162,5 +163,6 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowTmuxControl);
}
}

View File

@@ -77,6 +77,16 @@
<TextBox Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind Profile.AnswerbackMessage, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Allow Tmux Control -->
<local:SettingContainer x:Uid="Profile_AllowTmuxControl"
ClearSettingValue="{x:Bind Profile.ClearAllowTmuxControl}"
HasSettingValue="{x:Bind Profile.HasAllowTmuxControl, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AllowTmuxControlOverrideSource, Mode=OneWay}"
Visibility="{x:Bind Profile.TmuxControlEnabled}">
<ToggleSwitch IsOn="{x:Bind Profile.AllowTmuxControl, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
</StackPanel>
</Grid>
</Page>

View File

@@ -568,6 +568,10 @@
<value>Always on top</value>
<comment>Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled).</comment>
</data>
<data name="Profile_AllowTmuxControl.Header" xml:space="preserve">
<value>Allow Tmux Control</value>
<comment>Header for a control to toggle tmux control.</comment>
</data>
<data name="Profile_ForceVTInput.Header" xml:space="preserve">
<value>Use the legacy input encoding</value>
<comment>Header for a control to toggle legacy input encoding for the terminal.</comment>

View File

@@ -106,6 +106,7 @@ Author(s):
X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \
X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
X(bool, AllowTmuxControl, "AllowTmuxControl", false) \
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None)
// Intentionally omitted Profile settings:

View File

@@ -91,6 +91,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtChecksumReport);
INHERITABLE_PROFILE_SETTING(Boolean, AllowKeypadMode);
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
INHERITABLE_PROFILE_SETTING(Boolean, AllowTmuxControl);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
}

View File

@@ -95,7 +95,7 @@
<!-- For ALL build types-->
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LinkIncremental>false</LinkIncremental>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>

View File

@@ -14,7 +14,7 @@
<name>Feature_EditableUnfocusedAppearance</name>
<description>The unfocused appearance section in profiles in the SUI that allows users to create and edit unfocused appearances.</description>
<stage>AlwaysEnabled</stage>
<alwaysDisabledReleaseTokens/>
<alwaysDisabledReleaseTokens />
</feature>
<feature>
@@ -184,7 +184,19 @@
<name>Feature_DebugModeUI</name>
<description>Enables UI access to the debug mode setting</description>
<stage>AlwaysEnabled</stage>
<alwaysDisabledReleaseTokens/>
<alwaysDisabledReleaseTokens />
</feature>
<feature>
<name>Feature_TmuxControl</name>
<description>Enables Tmux Control</description>
<id>3656</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
<brandingToken>Preview</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View File

@@ -427,7 +427,14 @@ void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, un
{
// Not implemented for conhost.
}
void ConhostInternalGetSet::SearchMissingCommand(std::wstring_view /*missingCommand*/)
{
// Not implemented for conhost.
}
std::function<bool(wchar_t)> ConhostInternalGetSet::EnterTmuxControl()
{
// Not implemented for conhost.
return {};
}

View File

@@ -71,6 +71,7 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(std::wstring_view missingCommand) override;
std::function<bool(wchar_t)> EnterTmuxControl() override;
private:
Microsoft::Console::IIoProvider& _io;

View File

@@ -168,7 +168,41 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
template<typename T, typename Traits>
bool equals(const std::basic_string_view<T, Traits>& lhs, const std::basic_string_view<T, Traits>& rhs) noexcept
{
return lhs.size() == rhs.size() && __builtin_memcmp(lhs.data(), rhs.data(), lhs.size() * sizeof(T)) == 0;
// MSVC can only optimize 1 pattern into a bunch of simple comparison instructions:
// size1 == size2 && memcmp(data1, data2, size1/2) == 0
// If you introduce a `* sizeof(T)` into the size parameter to memcmp,
// it'll refuse to inline the memcmp call, resulting in much worse codegen.
// As a trade-off we multiply both sizes by sizeof(T) in advance.
// The extra addition instruction is comparatively very cheap.
const auto ls = lhs.size() * sizeof(T);
const auto rs = rhs.size() * sizeof(T);
return ls == rs && __builtin_memcmp(lhs.data(), rhs.data(), ls) == 0;
}
inline bool equals(const std::string_view& lhs, const std::string_view& rhs) noexcept
{
return equals<>(lhs, rhs);
}
inline bool equals(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept
{
return equals<>(lhs, rhs);
}
// An extra overload that undoes the quirk in the main equals() implementation.
// It's not really needed, except for being neat. This function in particular
// is often the best, easy way to do something like:
// match str {
// "foo" => ...,
// "bar" => ...,
// }
// with:
// if (til::equals(str, "foo")) { ... }
// else if (til::equals(str, "bar")) { ... }
template<typename T, typename Traits, size_t N>
constexpr bool equals(const std::basic_string_view<T, Traits>& lhs, const T (&rhs)[N]) noexcept
{
return lhs.size() == (N - 1) && __builtin_memcmp(lhs.data(), rhs, (N - 1) * sizeof(T)) == 0;
}
// Just like _memicmp, but without annoying locales.

View File

@@ -60,32 +60,192 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
template<typename ArgsT>
struct event
{
event<ArgsT>() = default;
explicit operator bool() const noexcept { return static_cast<bool>(_handlers); }
winrt::event_token operator()(const ArgsT& handler) { return _handlers.add(handler); }
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
operator bool() const noexcept { return bool(_handlers); }
template<typename... Arg>
void operator()(winrt::event_token token) { _handlers.remove(token); }
void raise(auto&&... args)
{
_handlers(std::forward<decltype(args)>(args)...);
}
private:
winrt::event<ArgsT> _handlers;
};
template<typename SenderT = winrt::Windows::Foundation::IInspectable, typename ArgsT = winrt::Windows::Foundation::IInspectable>
struct typed_event
using typed_event = til::event<winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>>;
// Unlike winrt::event, this event will only call handlers once at most.
// It's otherwise a copy of winrt::event's implementation.
template<typename ArgsT>
struct fused_event
{
typed_event<SenderT, ArgsT>() = default;
winrt::event_token operator()(const winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>& handler) { return _handlers.add(handler); }
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
operator bool() const noexcept { return bool(_handlers); }
template<typename... Arg>
void raise(Arg const&... args)
using delegate_type = ArgsT;
using delegate_array = winrt::com_ptr<winrt::impl::event_array<delegate_type>>;
fused_event() = default;
fused_event(const fused_event&) = delete;
fused_event& operator=(const fused_event&) = delete;
fused_event(fused_event&& other)
{
_handlers(std::forward<decltype(args)>(args)...);
const winrt::slim_lock_guard change_guard{ other.m_change };
if (!other.m_targets)
{
return;
}
const winrt::slim_lock_guard swap_guard{ other.m_swap };
m_targets = std::move(other.m_targets);
}
winrt::event<winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>> _handlers;
fused_event& operator=(fused_event&& other)
{
if (this != &other)
{
const winrt::slim_lock_guard other_change_guard{ other.m_change };
const winrt::slim_lock_guard other_swap_guard{ other.m_swap };
const winrt::slim_lock_guard self_change_guard{ m_change };
const winrt::slim_lock_guard self_swap_guard{ m_swap };
m_targets = std::move(other.m_targets);
}
return *this;
}
explicit operator bool() const noexcept
{
return m_targets != nullptr;
}
winrt::event_token operator()(const delegate_type& delegate)
{
return add_agile(winrt::impl::make_agile_delegate(delegate));
}
void operator()(const winrt::event_token token)
{
// Extends life of old targets array to release delegates outside of lock.
delegate_array temp_targets;
{
const winrt::slim_lock_guard change_guard{ m_change };
if (!m_targets)
{
return;
}
uint32_t available_slots = m_targets->size() - 1;
delegate_array new_targets;
bool removed = false;
if (available_slots == 0)
{
if (get_token(*m_targets->begin()) == token)
{
removed = true;
}
}
else
{
new_targets = winrt::impl::make_event_array<delegate_type>(available_slots);
auto new_iterator = new_targets->begin();
for (delegate_type const& element : *m_targets)
{
if (!removed && token == get_token(element))
{
removed = true;
continue;
}
if (available_slots == 0)
{
WINRT_ASSERT(!removed);
break;
}
*new_iterator = element;
++new_iterator;
--available_slots;
}
}
if (removed)
{
const winrt::slim_lock_guard swap_guard{ m_swap };
temp_targets = std::exchange(m_targets, std::move(new_targets));
}
}
}
template<typename... Arg>
void raise(const Arg&... args)
{
delegate_array temp_targets;
{
const winrt::slim_lock_guard change_guard{ m_change };
if (!m_targets)
{
return;
}
const winrt::slim_lock_guard swap_guard{ m_swap };
temp_targets = std::move(m_targets);
}
if (temp_targets)
{
for (const auto& element : *temp_targets)
{
if (!winrt::impl::invoke(element, args...))
{
operator()(get_token(element));
}
}
}
}
private:
WINRT_IMPL_NOINLINE winrt::event_token add_agile(delegate_type delegate)
{
winrt::event_token token;
// Extends life of old targets array to release delegates outside of lock.
delegate_array temp_targets;
{
const winrt::slim_lock_guard change_guard{ m_change };
const auto size = !m_targets ? 0 : m_targets->size();
auto new_targets = winrt::impl::make_event_array<delegate_type>(size + 1);
if (m_targets)
{
std::copy_n(m_targets->begin(), m_targets->size(), new_targets->begin());
}
new_targets->back() = std::move(delegate);
token = get_token(new_targets->back());
const winrt::slim_lock_guard swap_guard{ m_swap };
temp_targets = std::exchange(m_targets, std::move(new_targets));
}
return token;
}
winrt::event_token get_token(delegate_type const& delegate) const noexcept
{
return winrt::event_token{ reinterpret_cast<int64_t>(WINRT_IMPL_EncodePointer(winrt::get_abi(delegate))) };
}
delegate_array m_targets;
winrt::slim_mutex m_swap;
winrt::slim_mutex m_change;
};
#endif
#ifdef WINRT_Windows_UI_Xaml_Data_H

View File

@@ -161,7 +161,7 @@ namespace Microsoft::Console::Render
TimerHandle _cursorBlinker;
uint64_t _cursorBufferMutationId = 0;
uint64_t _cursorCursorMutationId = 0; // Stupid name, but it's _cursor related and stores the cursor mutation id.
til::enumset<InhibitionSource, uint8_t> _cursorVisibilityInhibitors;
til::enumset<InhibitionSource, uint8_t> _cursorVisibilityInhibitors{ InhibitionSource::Host };
til::enumset<InhibitionSource, uint8_t> _cursorBlinkingInhibitors;
bool _cursorBlinkerOn = false;

View File

@@ -192,6 +192,8 @@ public:
virtual void PlaySounds(const VTParameters parameters) = 0; // DECPS
virtual void SetOptionalFeatures(const til::enumset<OptionalFeature> features) = 0;
virtual StringHandler EnterTmuxControl(const VTParameters parameters) = 0; // tmux -CC
};
inline Microsoft::Console::VirtualTerminal::ITermDispatch::~ITermDispatch() = default;
#pragma warning(pop)

View File

@@ -90,5 +90,6 @@ namespace Microsoft::Console::VirtualTerminal
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
virtual void SearchMissingCommand(const std::wstring_view command) = 0;
virtual std::function<bool(wchar_t)> EnterTmuxControl() = 0;
};
}

View File

@@ -4760,3 +4760,12 @@ void AdaptDispatch::SetOptionalFeatures(const til::enumset<OptionalFeature> feat
{
_optionalFeatures = features;
}
ITermDispatch::StringHandler AdaptDispatch::EnterTmuxControl(const VTParameters parameters)
{
if (parameters.size() != 1 || parameters.at(0).value() != 1000)
{
return nullptr;
}
return _api.EnterTmuxControl();
}

View File

@@ -190,6 +190,8 @@ namespace Microsoft::Console::VirtualTerminal
void SetOptionalFeatures(const til::enumset<OptionalFeature> features) noexcept override;
StringHandler EnterTmuxControl(const VTParameters parameters) override; // tmux -CC
private:
enum class Mode
{

View File

@@ -179,6 +179,8 @@ public:
void PlaySounds(const VTParameters /*parameters*/) override{}; // DECPS
void SetOptionalFeatures(const til::enumset<OptionalFeature> /*features*/) override{};
StringHandler EnterTmuxControl(const VTParameters /*parameters*/) override { return nullptr; }; // tmux -CC
};
#pragma warning(default : 26440) // Restore "can be declared noexcept" warning

View File

@@ -224,6 +224,12 @@ public:
Log::Comment(L"SearchMissingCommand MOCK called...");
}
std::function<bool(wchar_t)> EnterTmuxControl() override
{
Log::Comment(L"EnterTmuxControl MOCK called...");
return nullptr;
}
void PrepData()
{
PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter.

View File

@@ -724,6 +724,9 @@ IStateMachineEngine::StringHandler OutputStateMachineEngine::ActionDcsDispatch(c
case DcsActionCodes::DECRSPS_RestorePresentationState:
handler = _dispatch->RestorePresentationState(parameters.at(0));
break;
case DcsActionCodes::TMUX_ControlEnter:
handler = _dispatch->EnterTmuxControl(parameters);
break;
default:
handler = nullptr;
break;

View File

@@ -178,6 +178,7 @@ namespace Microsoft::Console::VirtualTerminal
DECRSTS_RestoreTerminalState = VTID("$p"),
DECRQSS_RequestSetting = VTID("$q"),
DECRSPS_RestorePresentationState = VTID("$t"),
TMUX_ControlEnter = VTID("p"),
};
enum Vt52ActionCodes : uint64_t