mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-18 18:56:25 +00:00
Compare commits
14 Commits
dev/cazamo
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17cdf292b5 | ||
|
|
ea192b2d94 | ||
|
|
39601d4703 | ||
|
|
9833cbf8ba | ||
|
|
ff8fe85d72 | ||
|
|
45813925dd | ||
|
|
fc37791900 | ||
|
|
d0b9c0a0b0 | ||
|
|
441ec83514 | ||
|
|
39316fb266 | ||
|
|
ed93677edf | ||
|
|
d6d3be40cc | ||
|
|
39822d1a5e | ||
|
|
1395d17e07 |
@@ -185,7 +185,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto termControl{ _senderOrActiveControl(sender) })
|
||||
{
|
||||
termControl.SendInput(realArgs.Input());
|
||||
const auto broadcastGroup{ _getBroadcastGroupFromControl(termControl) };
|
||||
_writeInputStringToBroadcastGroup(broadcastGroup, realArgs.Input(), WriteInputStringType::Raw);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -452,8 +453,11 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_PasteText();
|
||||
args.Handled(true);
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
_PasteFromClipboardHandler(control, nullptr);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
|
||||
@@ -3044,14 +3044,15 @@ void Pane::BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl&
|
||||
}
|
||||
|
||||
void Pane::BroadcastString(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl,
|
||||
const winrt::hstring& text)
|
||||
const winrt::hstring& text,
|
||||
const winrt::Microsoft::Terminal::Control::WriteInputStringType type)
|
||||
{
|
||||
WalkTree([&](const auto& pane) {
|
||||
if (const auto& termControl{ pane->GetTerminalControl() })
|
||||
{
|
||||
if (termControl != sourceControl && !termControl.ReadOnly())
|
||||
{
|
||||
termControl.RawWriteString(text);
|
||||
termControl.WriteInputString(text, type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
void EnableBroadcast(bool enabled);
|
||||
void BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
|
||||
void BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const wchar_t vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers);
|
||||
void BroadcastString(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const winrt::hstring& text);
|
||||
void BroadcastString(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const winrt::hstring& text, const winrt::Microsoft::Terminal::Control::WriteInputStringType type);
|
||||
|
||||
void UpdateResources(const PaneResources& resources);
|
||||
|
||||
|
||||
@@ -929,4 +929,12 @@
|
||||
<data name="InvalidRegex" xml:space="preserve">
|
||||
<value>An invalid regular expression was found.</value>
|
||||
</data>
|
||||
<data name="DragFileCaption" xml:space="preserve">
|
||||
<value>Paste path to file</value>
|
||||
<comment>The displayed caption for dragging a file onto a terminal.</comment>
|
||||
</data>
|
||||
<data name="DragTextCaption" xml:space="preserve">
|
||||
<value>Paste text</value>
|
||||
<comment>The displayed caption for dragging text onto a terminal.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -2174,7 +2174,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// Always clear out old ones, just in case.
|
||||
events.KeySent.revoke();
|
||||
events.CharSent.revoke();
|
||||
events.StringSent.revoke();
|
||||
|
||||
if (newIsBroadcasting)
|
||||
{
|
||||
@@ -2215,17 +2214,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
events.StringSent = termControl.StringSent(winrt::auto_revoke, [weakThis](auto&& sender, auto&& e) {
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_tabStatus.IsInputBroadcastActive())
|
||||
{
|
||||
tab->_rootPane->BroadcastString(sender.try_as<TermControl>(),
|
||||
e.Text());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Tab::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
|
||||
|
||||
@@ -186,7 +186,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// These events literally only apply if the content is a TermControl.
|
||||
winrt::Microsoft::Terminal::Control::TermControl::KeySent_revoker KeySent;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::CharSent_revoker CharSent;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::StringSent_revoker StringSent;
|
||||
|
||||
winrt::TerminalApp::TerminalPaneContent::RestartTerminalRequested_revoker RestartTerminalRequested;
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@ using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
@@ -1964,6 +1965,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler });
|
||||
|
||||
term.DragOver({ this, &TerminalPage::_ControlDragOverHandler });
|
||||
term.Drop({ this, &TerminalPage::_ControlDragDropHandler });
|
||||
|
||||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
@@ -2945,9 +2949,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Sends the text back to the TermControl through the event's
|
||||
// `HandleClipboardData` member function.
|
||||
// - Does some of this in a background thread, as to not hang/crash the UI thread.
|
||||
// Arguments:
|
||||
// - eventArgs: the PasteFromClipboard event sent from the TermControl
|
||||
safe_void_coroutine TerminalPage::_PasteFromClipboardHandler(const IInspectable sender, const PasteFromClipboardEventArgs eventArgs)
|
||||
safe_void_coroutine TerminalPage::_PasteFromClipboardHandler(const IInspectable sender, const IInspectable /* args */)
|
||||
try
|
||||
{
|
||||
// The old Win32 clipboard API as used below is somewhere in the order of 300-1000x faster than
|
||||
@@ -2955,8 +2957,19 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto weakThis = get_weak();
|
||||
const auto dispatcher = Dispatcher();
|
||||
const auto globalSettings = _settings.GlobalSettings();
|
||||
const auto bracketedPaste = eventArgs.BracketedPasteEnabled();
|
||||
const auto sourceId = sender.try_as<ControlInteractivity>().Id();
|
||||
const auto control = sender.as<TermControl>();
|
||||
const auto broadcastGroup = _getBroadcastGroupFromControl(control);
|
||||
// Used to determine whether to emit empty pastes and strip extra whitespace
|
||||
const auto anyHasBracketedPaste = std::any_of(std::begin(broadcastGroup), std::end(broadcastGroup), [](auto&& content) {
|
||||
const auto control{ content.GetTermControl() };
|
||||
return control && !control.ReadOnly() && control.BracketedPasteEnabled();
|
||||
});
|
||||
// Used to determine whether to warn on multi-line paste
|
||||
// If none lack bracketed paste, we can skip the warning.
|
||||
const auto anyHasUnbracketedPaste = !anyHasBracketedPaste || std::any_of(std::begin(broadcastGroup), std::end(broadcastGroup), [](auto&& content) {
|
||||
const auto control{ content.GetTermControl() };
|
||||
return control && !control.ReadOnly() && !control.BracketedPasteEnabled();
|
||||
});
|
||||
|
||||
// GetClipboardData might block for up to 30s for delay-rendered contents.
|
||||
co_await winrt::resume_background();
|
||||
@@ -2967,8 +2980,11 @@ namespace winrt::TerminalApp::implementation
|
||||
text = clipboard::read();
|
||||
}
|
||||
|
||||
if (!bracketedPaste && globalSettings.TrimPaste())
|
||||
if (!anyHasBracketedPaste && globalSettings.TrimPaste())
|
||||
{
|
||||
// Warning - when broadcast is enabled, this will trim the paste for all receivers.
|
||||
// Until we propagate this decision-making elsewhere, this is safer; broadcast will not auto-submit commands in other panes.
|
||||
// Tracked in GH#20164
|
||||
text = winrt::hstring{ Utils::TrimPaste(text) };
|
||||
}
|
||||
|
||||
@@ -2976,7 +2992,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Bracketed Paste provides an application a way to know whether the
|
||||
// user pasted, even if there was no applicable content on it. This
|
||||
// behavior is observed in GNOME Terminal, among others.
|
||||
if (!bracketedPaste && text.empty())
|
||||
if (!anyHasBracketedPaste && text.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
@@ -2988,7 +3004,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// NOTE that this is unsafe, because a shell that doesn't support bracketed paste
|
||||
// will allow an attacker to enable the mode, not realize that, and then accept
|
||||
// the paste as if it was a series of legitimate commands. See GH#13014.
|
||||
warnMultiLine = !bracketedPaste;
|
||||
warnMultiLine = anyHasUnbracketedPaste;
|
||||
break;
|
||||
case WarnAboutMultiLinePaste::Always:
|
||||
warnMultiLine = true;
|
||||
@@ -3058,30 +3074,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// This will end up calling ConptyConnection::WriteInput which calls WriteFile which may block for
|
||||
// an indefinite amount of time. Avoid freezes and deadlocks by running this on a background thread.
|
||||
assert(!dispatcher.HasThreadAccess());
|
||||
eventArgs.HandleClipboardData(text);
|
||||
|
||||
// GH#18821: If broadcast input is active, paste the same text into all other
|
||||
// panes on the tab. We do this here (rather than re-reading the
|
||||
// clipboard per-pane) so that only one paste warning is shown.
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (const auto strongThis = weakThis.get())
|
||||
{
|
||||
if (const auto& tab{ strongThis->_GetFocusedTabImpl() })
|
||||
{
|
||||
if (tab->TabStatus().IsInputBroadcastActive())
|
||||
{
|
||||
tab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (const auto control = pane->GetTerminalControl())
|
||||
{
|
||||
if (control.ContentId() != sourceId && !control.ReadOnly())
|
||||
{
|
||||
control.RawWriteString(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_writeInputStringToBroadcastGroup(broadcastGroup, text, WriteInputStringType::Clipboard);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
@@ -3436,16 +3429,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Paste text from the Windows Clipboard to the focused terminal
|
||||
void TerminalPage::_PasteText()
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
control.PasteTextFromClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Called when the settings button is clicked. ShellExecutes the settings
|
||||
// file, as to open it in the default editor for .json files. Does this in
|
||||
@@ -5827,4 +5810,326 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
return profileMenuItemFlyout;
|
||||
}
|
||||
|
||||
static void _translatePathInPlace(std::wstring& fullPath, PathTranslationStyle translationStyle)
|
||||
{
|
||||
static constexpr wil::zwstring_view s_pathPrefixes[] = {
|
||||
{},
|
||||
/* WSL */ L"/mnt/",
|
||||
/* Cygwin */ L"/cygdrive/",
|
||||
/* MSYS2 */ L"/",
|
||||
/* MinGW */ {},
|
||||
};
|
||||
static constexpr wil::zwstring_view sSingleQuoteEscape = L"'\\''";
|
||||
static constexpr auto cchSingleQuoteEscape = sSingleQuoteEscape.size();
|
||||
|
||||
if (translationStyle == PathTranslationStyle::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// All of the other path translation modes current result in /-delimited paths
|
||||
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
|
||||
|
||||
// Escape single quotes, assuming translated paths are always quoted by a pair of single quotes.
|
||||
size_t pos = 0;
|
||||
while ((pos = fullPath.find(L'\'', pos)) != std::wstring::npos)
|
||||
{
|
||||
// ' -> '\'' (for POSIX shell)
|
||||
fullPath.replace(pos, 1, sSingleQuoteEscape);
|
||||
// Arithmetic overflow cannot occur here.
|
||||
pos += cchSingleQuoteEscape;
|
||||
}
|
||||
|
||||
if (translationStyle == PathTranslationStyle::MinGW)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
|
||||
{
|
||||
// C:/foo/bar -> Cc/foo/bar
|
||||
fullPath.at(1) = til::tolower_ascii(fullPath.at(0));
|
||||
// Cc/foo/bar -> [PREFIX]c/foo/bar
|
||||
fullPath.replace(0, 1, s_pathPrefixes[static_cast<int>(translationStyle)]);
|
||||
}
|
||||
else if (translationStyle == PathTranslationStyle::WSL)
|
||||
{
|
||||
// Stripping the UNC name and distribution prefix only applies to WSL.
|
||||
static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" };
|
||||
for (auto prefix : wslPathPrefixes)
|
||||
{
|
||||
if (til::starts_with(fullPath, prefix))
|
||||
{
|
||||
if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos)
|
||||
{
|
||||
// //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar
|
||||
fullPath.erase(0, idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// //wsl.localhost/Ubuntu-18.04 -> /
|
||||
fullPath = L"/";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handle the DragOver event. We'll signal that the drag operation we
|
||||
// support is the "copy" operation, and we'll also customize the
|
||||
// appearance of the drag-drop UI, by removing the preview and setting a
|
||||
// custom caption. For more information, see
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the DragOver event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_ControlDragOverHandler(const Windows::Foundation::IInspectable& /*sender*/,
|
||||
const DragEventArgs& e)
|
||||
{
|
||||
// We can only handle drag/dropping StorageItems (files) and plain Text
|
||||
// currently. If the format on the clipboard is anything else, returning
|
||||
// early here will prevent the drag/drop from doing anything.
|
||||
if (!(e.DataView().Contains(StandardDataFormats::StorageItems()) ||
|
||||
e.DataView().Contains(StandardDataFormats::Text())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
|
||||
e.AcceptedOperation(DataPackageOperation::Copy);
|
||||
|
||||
// Sets custom UI text
|
||||
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DragTextCaption"));
|
||||
}
|
||||
|
||||
// Sets if the caption is visible
|
||||
e.DragUIOverride().IsCaptionVisible(true);
|
||||
// Sets if the dragged content is visible
|
||||
e.DragUIOverride().IsContentVisible(false);
|
||||
// Sets if the glyph is visible
|
||||
e.DragUIOverride().IsGlyphVisible(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Async handler for the "Drop" event. If a file was dropped onto our
|
||||
// root, we'll try to get the path of the file dropped onto us, and write
|
||||
// the full path of the file to our terminal connection. Like conhost, if
|
||||
// the path contains a space, we'll wrap the path in quotes.
|
||||
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
||||
// write all the paths to the terminal, separated by spaces.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
safe_void_coroutine TerminalPage::_ControlDragDropHandler(Windows::Foundation::IInspectable sender,
|
||||
DragEventArgs e)
|
||||
{
|
||||
auto dispatcher = Dispatcher();
|
||||
auto weak = get_weak();
|
||||
const auto control = sender.as<TermControl>();
|
||||
const auto broadcastGroup = _getBroadcastGroupFromControl(control);
|
||||
|
||||
if (_hostingHwnd)
|
||||
{
|
||||
SetForegroundWindow(*_hostingHwnd);
|
||||
}
|
||||
|
||||
if (e.DataView().Contains(StandardDataFormats::ApplicationLink()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto link{ co_await e.DataView().GetApplicationLinkAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_writeInputStringToBroadcastGroup(broadcastGroup, link.AbsoluteUri(), WriteInputStringType::Clipboard);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::WebLink()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto link{ co_await e.DataView().GetWebLinkAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_writeInputStringToBroadcastGroup(broadcastGroup, link.AbsoluteUri(), WriteInputStringType::Clipboard);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto text{ co_await e.DataView().GetTextAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_writeInputStringToBroadcastGroup(broadcastGroup, text, WriteInputStringType::Clipboard);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
// StorageItem must be last. Some applications put hybrid data format items
|
||||
// in a drop message and we'll eat a crash when we request them.
|
||||
// Those applications usually include Text as well, so having storage items
|
||||
// last makes sure we'll hit text before getting to them.
|
||||
else if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
|
||||
try
|
||||
{
|
||||
items = co_await e.DataView().GetStorageItemsAsync();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (items && items.Size() > 0)
|
||||
{
|
||||
std::vector<std::wstring> fullPaths;
|
||||
|
||||
// GH#14628: Workaround for GetStorageItemsAsync() only returning 16 items
|
||||
// at most when dragging and dropping from archives (zip, 7z, rar, etc.)
|
||||
if (items.Size() == 16 && e.DataView().Contains(winrt::hstring{ L"FileDrop" }))
|
||||
{
|
||||
auto fileDropData = co_await e.DataView().GetDataAsync(winrt::hstring{ L"FileDrop" });
|
||||
if (fileDropData != nullptr)
|
||||
{
|
||||
auto stream = fileDropData.as<IRandomAccessStream>();
|
||||
stream.Seek(0);
|
||||
|
||||
const uint32_t streamSize = gsl::narrow_cast<uint32_t>(stream.Size());
|
||||
const Buffer buf(streamSize);
|
||||
const auto buffer = co_await stream.ReadAsync(buf, streamSize, InputStreamOptions::None);
|
||||
|
||||
const HGLOBAL hGlobal = buffer.data();
|
||||
const auto count = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0);
|
||||
fullPaths.reserve(count);
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
std::wstring path;
|
||||
path.resize(wil::max_path_length);
|
||||
const auto charsCopied = DragQueryFileW(static_cast<HDROP>(hGlobal), i, path.data(), wil::max_path_length);
|
||||
|
||||
if (charsCopied > 0)
|
||||
{
|
||||
path.resize(charsCopied);
|
||||
fullPaths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fullPaths.reserve(items.Size());
|
||||
for (const auto& item : items)
|
||||
{
|
||||
fullPaths.emplace_back(item.Path());
|
||||
}
|
||||
}
|
||||
|
||||
const auto strong = weak.get();
|
||||
if (!strong)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// TODO(DH) the fuckin' delimiter from DragDropDelimiter
|
||||
std::unordered_map<PathTranslationStyle, winrt::hstring> translatedPaths;
|
||||
for (auto&& target : broadcastGroup)
|
||||
{
|
||||
const auto profile{ target.GetProfile() };
|
||||
auto translationStyle{ profile.PathTranslationStyle() };
|
||||
const auto broadcastTargetControl{ target.GetTermControl() };
|
||||
if (broadcastTargetControl.ReadOnly())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [it, isNew] = translatedPaths.try_emplace(translationStyle, winrt::hstring{});
|
||||
if (isNew)
|
||||
{
|
||||
std::wstring allPathsString;
|
||||
for (auto fullPath : fullPaths)
|
||||
{
|
||||
// Join the paths with spaces
|
||||
if (!allPathsString.empty())
|
||||
{
|
||||
allPathsString += L" ";
|
||||
}
|
||||
|
||||
_translatePathInPlace(fullPath, translationStyle);
|
||||
|
||||
// All translated paths get quotes, and all strings spaces get quotes; all translated paths get single quotes
|
||||
const auto quotesNeeded = translationStyle != PathTranslationStyle::None || fullPath.find(L' ') != std::wstring::npos;
|
||||
const auto quotesChar = translationStyle != PathTranslationStyle::None ? L'\'' : L'"';
|
||||
|
||||
// Append fullPath and also wrap it in quotes if needed
|
||||
if (quotesNeeded)
|
||||
{
|
||||
allPathsString.push_back(quotesChar);
|
||||
}
|
||||
allPathsString.append(fullPath);
|
||||
if (quotesNeeded)
|
||||
{
|
||||
allPathsString.push_back(quotesChar);
|
||||
}
|
||||
}
|
||||
it->second = winrt::hstring{ allPathsString };
|
||||
}
|
||||
|
||||
broadcastTargetControl.WriteInputString(it->second, WriteInputStringType::Clipboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TerminalPage::broadcast_group TerminalPage::_getBroadcastGroupFromControl(const TermControl& control)
|
||||
{
|
||||
TerminalPage::broadcast_group contents;
|
||||
auto controlContent{ TerminalPaneContent::ContentFromControl(control) };
|
||||
if (controlContent)
|
||||
{
|
||||
contents.emplace_back(controlContent);
|
||||
}
|
||||
|
||||
if (const auto& tab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (tab->TabStatus().IsInputBroadcastActive())
|
||||
{
|
||||
tab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (auto content = pane->GetContent(); content && content != controlContent)
|
||||
{
|
||||
if (const auto termContent{ content.try_as<TerminalPaneContent>() })
|
||||
{
|
||||
contents.emplace_back(*termContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
void TerminalPage::_writeInputStringToBroadcastGroup(const TerminalPage::broadcast_group& broadcastGroup, const winrt::hstring text, WriteInputStringType type)
|
||||
{
|
||||
for (auto&& content : broadcastGroup)
|
||||
{
|
||||
auto nextControl{ content.GetTermControl() };
|
||||
if (!nextControl.ReadOnly())
|
||||
{
|
||||
nextControl.WriteInputString(text, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Control::KeyChord& keyChord);
|
||||
|
||||
safe_void_coroutine _PasteFromClipboardHandler(const IInspectable sender,
|
||||
const Microsoft::Terminal::Control::PasteFromClipboardEventArgs eventArgs);
|
||||
const IInspectable eventArgs);
|
||||
|
||||
safe_void_coroutine _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs);
|
||||
static bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
|
||||
@@ -437,6 +437,9 @@ namespace winrt::TerminalApp::implementation
|
||||
safe_void_coroutine _ControlNoticeRaisedHandler(const IInspectable sender, const Microsoft::Terminal::Control::NoticeEventArgs eventArgs);
|
||||
void _ShowControlNoticeDialog(const winrt::hstring& title, const winrt::hstring& message);
|
||||
|
||||
safe_void_coroutine _ControlDragDropHandler(Windows::Foundation::IInspectable sender, Windows::UI::Xaml::DragEventArgs e);
|
||||
void _ControlDragOverHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::DragEventArgs& e);
|
||||
|
||||
safe_void_coroutine _LaunchSettings(const Microsoft::Terminal::Settings::Model::SettingsTarget target);
|
||||
|
||||
void _TabDragStarted(const IInspectable& sender, const IInspectable& eventArgs);
|
||||
@@ -571,6 +574,10 @@ namespace winrt::TerminalApp::implementation
|
||||
void _activePaneChanged(winrt::TerminalApp::Tab tab, Windows::Foundation::IInspectable args);
|
||||
safe_void_coroutine _doHandleSuggestions(Microsoft::Terminal::Settings::Model::SuggestionsArgs realArgs);
|
||||
|
||||
using broadcast_group = til::small_vector<TerminalApp::TerminalPaneContent, 1>;
|
||||
broadcast_group _getBroadcastGroupFromControl(const Microsoft::Terminal::Control::TermControl& control);
|
||||
void _writeInputStringToBroadcastGroup(const broadcast_group& broadcastGroup, const winrt::hstring text, Microsoft::Terminal::Control::WriteInputStringType type);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);
|
||||
|
||||
@@ -20,6 +20,8 @@ using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
til::shared_mutex<std::unordered_map<void*, winrt::weak_ref<TerminalApp::implementation::TerminalPaneContent>>> TerminalPaneContent::_controlToContentMap{};
|
||||
|
||||
TerminalPaneContent::TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const std::shared_ptr<TerminalSettingsCache>& cache,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control) :
|
||||
@@ -28,6 +30,9 @@ namespace winrt::TerminalApp::implementation
|
||||
_profile{ profile }
|
||||
{
|
||||
_setupControlEvents();
|
||||
|
||||
auto map{ _controlToContentMap.lock() };
|
||||
map->insert_or_assign(winrt::get_abi(control), get_weak());
|
||||
}
|
||||
|
||||
void TerminalPaneContent::_setupControlEvents()
|
||||
@@ -72,6 +77,11 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_control.Close();
|
||||
|
||||
{
|
||||
auto map{ _controlToContentMap.lock() };
|
||||
map->erase(winrt::get_abi(_control));
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -371,4 +381,14 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return _control.CharacterDimensions();
|
||||
}
|
||||
|
||||
TerminalApp::TerminalPaneContent TerminalPaneContent::ContentFromControl(const winrt::Microsoft::Terminal::Control::TermControl& control)
|
||||
{
|
||||
const auto map{ _controlToContentMap.lock_shared() };
|
||||
if (auto found{ map->find(winrt::get_abi(control)) }; found != map->end())
|
||||
{
|
||||
return *found->second.get();
|
||||
}
|
||||
return { nullptr };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +55,13 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
til::typed_event<TerminalApp::TerminalPaneContent, winrt::Windows::Foundation::IInspectable> RestartTerminalRequested;
|
||||
|
||||
static TerminalApp::TerminalPaneContent ContentFromControl(const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
|
||||
// See BasicPaneEvents for most generic event definitions
|
||||
|
||||
private:
|
||||
static til::shared_mutex<std::unordered_map<void*, winrt::weak_ref<TerminalApp::implementation::TerminalPaneContent>>> _controlToContentMap;
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <winrt/Windows.Globalization.h>
|
||||
#include <winrt/Windows.Graphics.Display.h>
|
||||
#include <winrt/Windows.System.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.UI.Core.h>
|
||||
#include <winrt/Windows.UI.Input.h>
|
||||
#include <winrt/Windows.UI.Text.h>
|
||||
@@ -88,6 +89,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
#include <til/mutex.h>
|
||||
#include <til/winrt.h>
|
||||
|
||||
#include <SafeDispatcherTimer.h>
|
||||
|
||||
@@ -498,7 +498,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - wstr: the string of characters to write to the terminal connection.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ControlCore::SendInput(const std::wstring_view wstr)
|
||||
void ControlCore::_sendInput(const std::wstring_view wstr)
|
||||
{
|
||||
if (wstr.empty())
|
||||
{
|
||||
@@ -554,7 +554,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
SendInput(*out);
|
||||
_sendInput(*out);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -712,7 +712,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
SendInput(*out);
|
||||
_sendInput(*out);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -731,7 +731,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
SendInput(*out);
|
||||
_sendInput(*out);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1451,24 +1451,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Pre-process text pasted (presumably from the clipboard)
|
||||
// before sending it over the terminal's connection.
|
||||
void ControlCore::PasteText(const winrt::hstring& hstr)
|
||||
void ControlCore::WriteInputString(const std::wstring_view& str, WriteInputStringType type)
|
||||
{
|
||||
using namespace ::Microsoft::Console::Utils;
|
||||
|
||||
auto filtered = FilterStringForPaste(hstr, CarriageReturnNewline | ControlCodes);
|
||||
if (BracketedPasteEnabled())
|
||||
switch (type)
|
||||
{
|
||||
filtered.insert(0, L"\x1b[200~");
|
||||
filtered.append(L"\x1b[201~");
|
||||
case WriteInputStringType::Clipboard:
|
||||
{
|
||||
using namespace ::Microsoft::Console::Utils;
|
||||
|
||||
auto filtered = FilterStringForPaste(str, CarriageReturnNewline | ControlCodes);
|
||||
if (BracketedPasteEnabled())
|
||||
{
|
||||
filtered.insert(0, L"\x1b[200~");
|
||||
filtered.append(L"\x1b[201~");
|
||||
}
|
||||
|
||||
// It's important to not hold the terminal lock while calling this function as sending the data may take a long time.
|
||||
_sendInput(filtered);
|
||||
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
_terminal->TrySnapOnInput();
|
||||
return;
|
||||
}
|
||||
case WriteInputStringType::Raw:
|
||||
_sendInput(str);
|
||||
return;
|
||||
}
|
||||
|
||||
// It's important to not hold the terminal lock while calling this function as sending the data may take a long time.
|
||||
SendInput(filtered);
|
||||
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
_terminal->TrySnapOnInput();
|
||||
}
|
||||
|
||||
FontInfo ControlCore::GetFont() const
|
||||
@@ -2197,7 +2207,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Sending input requires that we're unlocked, because
|
||||
// writing the input pipe may block indefinitely.
|
||||
const auto suspension = _terminal->SuspendLock();
|
||||
SendInput(buffer);
|
||||
_sendInput(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
til::color ForegroundColor() const;
|
||||
til::color BackgroundColor() const;
|
||||
|
||||
void SendInput(std::wstring_view wstr);
|
||||
void PasteText(const winrt::hstring& hstr);
|
||||
void WriteInputString(const std::wstring_view& str, WriteInputStringType type);
|
||||
bool CopySelectionToClipboard(bool singleLine, bool withControlSequences, const CopyFormat formats);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
@@ -320,6 +319,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void _handleControlC();
|
||||
void _sendInputToConnection(std::wstring_view wstr);
|
||||
void _sendInput(std::wstring_view wstr);
|
||||
|
||||
#pragma region TerminalCoreCallbacks
|
||||
void _terminalWarningBell();
|
||||
|
||||
@@ -68,6 +68,14 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean SearchRegexInvalid;
|
||||
};
|
||||
|
||||
enum WriteInputStringType
|
||||
{
|
||||
// Text which is to be passed through unmodified.
|
||||
Raw,
|
||||
// Text which is to be treated as clipboard input, stripped and formatted according to the application's wishes.
|
||||
Clipboard,
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SelectionColor
|
||||
{
|
||||
SelectionColor();
|
||||
@@ -128,8 +136,7 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean SendCharEvent(Char ch,
|
||||
Int16 scanCode,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers);
|
||||
void SendInput(String text);
|
||||
void PasteText(String text);
|
||||
void WriteInputString(String text, WriteInputStringType type);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
Boolean ToggleBlockSelection();
|
||||
|
||||
@@ -242,14 +242,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - Initiate a paste operation.
|
||||
void ControlInteractivity::RequestPasteTextFromClipboard()
|
||||
{
|
||||
auto args = winrt::make<PasteFromClipboardEventArgs>(
|
||||
[core = _core](const winrt::hstring& wstr) {
|
||||
core->PasteText(wstr);
|
||||
},
|
||||
_core->BracketedPasteEnabled());
|
||||
|
||||
// send paste event up to TermApp
|
||||
PasteFromClipboard.raise(*this, std::move(args));
|
||||
PasteFromClipboard.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void AttachToNewControl();
|
||||
|
||||
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
til::typed_event<IInspectable, Control::PasteFromClipboardEventArgs> PasteFromClipboard;
|
||||
til::typed_event<IInspectable, IInspectable> PasteFromClipboard;
|
||||
til::typed_event<IInspectable, Control::ScrollPositionChangedArgs> ScrollPositionChanged;
|
||||
til::typed_event<IInspectable, Control::ContextMenuRequestedEventArgs> ContextMenuRequested;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> PasteFromClipboard;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Closed;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "TitleChangedEventArgs.g.cpp"
|
||||
#include "ContextMenuRequestedEventArgs.g.cpp"
|
||||
#include "WriteToClipboardEventArgs.g.cpp"
|
||||
#include "PasteFromClipboardEventArgs.g.cpp"
|
||||
#include "OpenHyperlinkEventArgs.g.cpp"
|
||||
#include "NoticeEventArgs.g.cpp"
|
||||
#include "ScrollPositionChangedArgs.g.cpp"
|
||||
@@ -18,6 +17,5 @@
|
||||
#include "CompletionsChangedEventArgs.g.cpp"
|
||||
#include "KeySentEventArgs.g.cpp"
|
||||
#include "CharSentEventArgs.g.cpp"
|
||||
#include "StringSentEventArgs.g.cpp"
|
||||
#include "SearchMissingCommandEventArgs.g.cpp"
|
||||
#include "WindowSizeChangedEventArgs.g.cpp"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "TitleChangedEventArgs.g.h"
|
||||
#include "ContextMenuRequestedEventArgs.g.h"
|
||||
#include "WriteToClipboardEventArgs.g.h"
|
||||
#include "PasteFromClipboardEventArgs.g.h"
|
||||
#include "OpenHyperlinkEventArgs.g.h"
|
||||
#include "NoticeEventArgs.g.h"
|
||||
#include "ScrollPositionChangedArgs.g.h"
|
||||
@@ -18,7 +17,6 @@
|
||||
#include "CompletionsChangedEventArgs.g.h"
|
||||
#include "KeySentEventArgs.g.h"
|
||||
#include "CharSentEventArgs.g.h"
|
||||
#include "StringSentEventArgs.g.h"
|
||||
#include "SearchMissingCommandEventArgs.g.h"
|
||||
#include "WindowSizeChangedEventArgs.g.h"
|
||||
|
||||
@@ -83,24 +81,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
std::string _rtf;
|
||||
};
|
||||
|
||||
struct PasteFromClipboardEventArgs : public PasteFromClipboardEventArgsT<PasteFromClipboardEventArgs>
|
||||
{
|
||||
public:
|
||||
PasteFromClipboardEventArgs(std::function<void(const hstring&)> clipboardDataHandler, bool bracketedPasteEnabled) :
|
||||
m_clipboardDataHandler(clipboardDataHandler),
|
||||
_BracketedPasteEnabled{ bracketedPasteEnabled } {}
|
||||
|
||||
void HandleClipboardData(hstring value)
|
||||
{
|
||||
m_clipboardDataHandler(value);
|
||||
};
|
||||
|
||||
WINRT_PROPERTY(bool, BracketedPasteEnabled, false);
|
||||
|
||||
private:
|
||||
std::function<void(const hstring&)> m_clipboardDataHandler;
|
||||
};
|
||||
|
||||
struct OpenHyperlinkEventArgs : public OpenHyperlinkEventArgsT<OpenHyperlinkEventArgs>
|
||||
{
|
||||
public:
|
||||
@@ -232,15 +212,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Core::ControlKeyStates, Modifiers);
|
||||
};
|
||||
|
||||
struct StringSentEventArgs : public StringSentEventArgsT<StringSentEventArgs>
|
||||
{
|
||||
public:
|
||||
StringSentEventArgs(const winrt::hstring& text) :
|
||||
_Text(text) {}
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, Text);
|
||||
};
|
||||
|
||||
struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT<SearchMissingCommandEventArgs>
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -68,12 +68,6 @@ namespace Microsoft.Terminal.Control
|
||||
byte[] Rtf { get; }; // UTF-8, as required by "Rich Text Format"
|
||||
}
|
||||
|
||||
runtimeclass PasteFromClipboardEventArgs
|
||||
{
|
||||
void HandleClipboardData(String data);
|
||||
Boolean BracketedPasteEnabled { get; };
|
||||
}
|
||||
|
||||
runtimeclass OpenHyperlinkEventArgs
|
||||
{
|
||||
OpenHyperlinkEventArgs(String uri);
|
||||
@@ -149,11 +143,6 @@ namespace Microsoft.Terminal.Control
|
||||
Microsoft.Terminal.Core.ControlKeyStates Modifiers { get; };
|
||||
}
|
||||
|
||||
runtimeclass StringSentEventArgs
|
||||
{
|
||||
String Text { get; };
|
||||
}
|
||||
|
||||
runtimeclass SearchMissingCommandEventArgs
|
||||
{
|
||||
String MissingCommand { get; };
|
||||
|
||||
@@ -137,14 +137,6 @@
|
||||
<value>Find</value>
|
||||
<comment>The placeholder text in the search box control.</comment>
|
||||
</data>
|
||||
<data name="DragFileCaption" xml:space="preserve">
|
||||
<value>Paste path to file</value>
|
||||
<comment>The displayed caption for dragging a file onto a terminal.</comment>
|
||||
</data>
|
||||
<data name="DragTextCaption" xml:space="preserve">
|
||||
<value>Paste text</value>
|
||||
<comment>The displayed caption for dragging text onto a terminal.</comment>
|
||||
</data>
|
||||
<data name="SearchBox_CaseSensitivity.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Case Sensitivity</value>
|
||||
<comment>The name of the case sensitivity button on the search box control for accessibility.</comment>
|
||||
|
||||
@@ -23,8 +23,6 @@ using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::ViewManagement;
|
||||
using namespace winrt::Windows::UI::Input;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
|
||||
// The minimum delay between updates to the scroll bar's values.
|
||||
// The updates are throttled to limit power usage.
|
||||
@@ -89,72 +87,6 @@ static Microsoft::Console::TSF::Handle& GetTSFHandle()
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
static void _translatePathInPlace(std::wstring& fullPath, PathTranslationStyle translationStyle)
|
||||
{
|
||||
static constexpr wil::zwstring_view s_pathPrefixes[] = {
|
||||
{},
|
||||
/* WSL */ L"/mnt/",
|
||||
/* Cygwin */ L"/cygdrive/",
|
||||
/* MSYS2 */ L"/",
|
||||
/* MinGW */ {},
|
||||
};
|
||||
static constexpr wil::zwstring_view sSingleQuoteEscape = L"'\\''";
|
||||
static constexpr auto cchSingleQuoteEscape = sSingleQuoteEscape.size();
|
||||
|
||||
if (translationStyle == PathTranslationStyle::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// All of the other path translation modes current result in /-delimited paths
|
||||
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
|
||||
|
||||
// Escape single quotes, assuming translated paths are always quoted by a pair of single quotes.
|
||||
size_t pos = 0;
|
||||
while ((pos = fullPath.find(L'\'', pos)) != std::wstring::npos)
|
||||
{
|
||||
// ' -> '\'' (for POSIX shell)
|
||||
fullPath.replace(pos, 1, sSingleQuoteEscape);
|
||||
// Arithmetic overflow cannot occur here.
|
||||
pos += cchSingleQuoteEscape;
|
||||
}
|
||||
|
||||
if (translationStyle == PathTranslationStyle::MinGW)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
|
||||
{
|
||||
// C:/foo/bar -> Cc/foo/bar
|
||||
fullPath.at(1) = til::tolower_ascii(fullPath.at(0));
|
||||
// Cc/foo/bar -> [PREFIX]c/foo/bar
|
||||
fullPath.replace(0, 1, s_pathPrefixes[static_cast<int>(translationStyle)]);
|
||||
}
|
||||
else if (translationStyle == PathTranslationStyle::WSL)
|
||||
{
|
||||
// Stripping the UNC name and distribution prefix only applies to WSL.
|
||||
static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" };
|
||||
for (auto prefix : wslPathPrefixes)
|
||||
{
|
||||
if (til::starts_with(fullPath, prefix))
|
||||
{
|
||||
if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos)
|
||||
{
|
||||
// //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar
|
||||
fullPath.erase(0, idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// //wsl.localhost/Ubuntu-18.04 -> /
|
||||
fullPath = L"/";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TsfDataProvider::TsfDataProvider(TermControl* termControl) noexcept :
|
||||
_termControl{ termControl }
|
||||
{
|
||||
@@ -238,7 +170,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
return;
|
||||
}
|
||||
core->SendInput(text);
|
||||
core->WriteInputString(text, WriteInputStringType::Raw);
|
||||
}
|
||||
|
||||
::Microsoft::Console::Render::Renderer* TsfDataProvider::GetRenderer()
|
||||
@@ -912,19 +844,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - wstr: the string of characters to write to the terminal connection.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::SendInput(const winrt::hstring& wstr)
|
||||
void TermControl::WriteInputString(const winrt::hstring& wstr, WriteInputStringType type)
|
||||
{
|
||||
// Dismiss any previewed input.
|
||||
PreviewInput(hstring{});
|
||||
|
||||
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
|
||||
if (StringSent)
|
||||
{
|
||||
StringSent.raise(*this, winrt::make<StringSentEventArgs>(wstr));
|
||||
}
|
||||
|
||||
RawWriteString(wstr);
|
||||
_core.WriteInputString(wstr, type);
|
||||
}
|
||||
|
||||
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
|
||||
{
|
||||
_core.ClearBuffer(clearType);
|
||||
@@ -1524,11 +1450,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _core.SendCharEvent(character, scanCode, modifiers);
|
||||
}
|
||||
|
||||
void TermControl::RawWriteString(const winrt::hstring& text)
|
||||
{
|
||||
_core.SendInput(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.
|
||||
@@ -1675,7 +1596,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// If it encounters a string that isn't, cppwinrt will abort().
|
||||
// It should already be null-terminated, but let's make sure to not crash.
|
||||
buf[buf_len] = L'\0';
|
||||
_core.SendInput(std::wstring_view{ &buf[0], buf_len });
|
||||
_core.WriteInputString(std::wstring_view{ &buf[0], buf_len }, WriteInputStringType::Raw);
|
||||
}
|
||||
|
||||
s = {};
|
||||
@@ -3069,230 +2990,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Async handler for the "Drop" event. If a file was dropped onto our
|
||||
// root, we'll try to get the path of the file dropped onto us, and write
|
||||
// the full path of the file to our terminal connection. Like conhost, if
|
||||
// the path contains a space, we'll wrap the path in quotes.
|
||||
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
||||
// write all the paths to the terminal, separated by the configured delimiter.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
safe_void_coroutine TermControl::_DragDropHandler(Windows::Foundation::IInspectable /*sender*/,
|
||||
DragEventArgs e)
|
||||
{
|
||||
if (_IsClosing())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (const auto hwnd = reinterpret_cast<HWND>(OwningHwnd()))
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
const auto weak = get_weak();
|
||||
|
||||
if (e.DataView().Contains(StandardDataFormats::ApplicationLink()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto link{ co_await e.DataView().GetApplicationLinkAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_pasteTextWithBroadcast(link.AbsoluteUri());
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::WebLink()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto link{ co_await e.DataView().GetWebLinkAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_pasteTextWithBroadcast(link.AbsoluteUri());
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto text{ co_await e.DataView().GetTextAsync() };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
_pasteTextWithBroadcast(text);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
// StorageItem must be last. Some applications put hybrid data format items
|
||||
// in a drop message and we'll eat a crash when we request them.
|
||||
// Those applications usually include Text as well, so having storage items
|
||||
// last makes sure we'll hit text before getting to them.
|
||||
else if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
|
||||
try
|
||||
{
|
||||
items = co_await e.DataView().GetStorageItemsAsync();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (items && items.Size() > 0)
|
||||
{
|
||||
std::vector<std::wstring> fullPaths;
|
||||
|
||||
// GH#14628: Workaround for GetStorageItemsAsync() only returning 16 items
|
||||
// at most when dragging and dropping from archives (zip, 7z, rar, etc.)
|
||||
if (items.Size() == 16 && e.DataView().Contains(winrt::hstring{ L"FileDrop" }))
|
||||
{
|
||||
auto fileDropData = co_await e.DataView().GetDataAsync(winrt::hstring{ L"FileDrop" });
|
||||
if (fileDropData != nullptr)
|
||||
{
|
||||
auto stream = fileDropData.as<IRandomAccessStream>();
|
||||
stream.Seek(0);
|
||||
|
||||
const uint32_t streamSize = gsl::narrow_cast<uint32_t>(stream.Size());
|
||||
const Buffer buf(streamSize);
|
||||
const auto buffer = co_await stream.ReadAsync(buf, streamSize, InputStreamOptions::None);
|
||||
|
||||
const HGLOBAL hGlobal = buffer.data();
|
||||
const auto count = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0);
|
||||
fullPaths.reserve(count);
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
std::wstring path;
|
||||
path.resize(wil::max_path_length);
|
||||
const auto charsCopied = DragQueryFileW(static_cast<HDROP>(hGlobal), i, path.data(), wil::max_path_length);
|
||||
|
||||
if (charsCopied > 0)
|
||||
{
|
||||
path.resize(charsCopied);
|
||||
fullPaths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fullPaths.reserve(items.Size());
|
||||
for (const auto& item : items)
|
||||
{
|
||||
fullPaths.emplace_back(item.Path());
|
||||
}
|
||||
}
|
||||
|
||||
const auto strong = weak.get();
|
||||
if (!strong)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
std::wstring allPathsString;
|
||||
const auto delimiter{ _core.Settings().DragDropDelimiter() };
|
||||
for (auto& fullPath : fullPaths)
|
||||
{
|
||||
// Join the paths with the delimiter
|
||||
if (!allPathsString.empty())
|
||||
{
|
||||
allPathsString += delimiter;
|
||||
}
|
||||
|
||||
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
|
||||
_translatePathInPlace(fullPath, translationStyle);
|
||||
|
||||
// All translated paths get quotes, and all strings spaces get quotes; all translated paths get single quotes
|
||||
const auto quotesNeeded = translationStyle != PathTranslationStyle::None || fullPath.find(L' ') != std::wstring::npos;
|
||||
const auto quotesChar = translationStyle != PathTranslationStyle::None ? L'\'' : L'"';
|
||||
|
||||
// Append fullPath and also wrap it in quotes if needed
|
||||
if (quotesNeeded)
|
||||
{
|
||||
allPathsString.push_back(quotesChar);
|
||||
}
|
||||
allPathsString.append(fullPath);
|
||||
if (quotesNeeded)
|
||||
{
|
||||
allPathsString.push_back(quotesChar);
|
||||
}
|
||||
}
|
||||
|
||||
_pasteTextWithBroadcast(winrt::hstring{ allPathsString });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Paste this text, and raise a StringSent, to potentially broadcast this
|
||||
// text to other controls in the app. For certain interactions, like
|
||||
// drag/dropping a file, we want to act like we "pasted" the text (even if
|
||||
// the text didn't come from the clipboard). This lets those interactions
|
||||
// broadcast as well.
|
||||
void TermControl::_pasteTextWithBroadcast(const winrt::hstring& text)
|
||||
{
|
||||
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
|
||||
if (StringSent)
|
||||
{
|
||||
StringSent.raise(*this, winrt::make<StringSentEventArgs>(text));
|
||||
}
|
||||
_core.PasteText(text);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handle the DragOver event. We'll signal that the drag operation we
|
||||
// support is the "copy" operation, and we'll also customize the
|
||||
// appearance of the drag-drop UI, by removing the preview and setting a
|
||||
// custom caption. For more information, see
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the DragOver event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_DragOverHandler(const Windows::Foundation::IInspectable& /*sender*/,
|
||||
const DragEventArgs& e)
|
||||
{
|
||||
if (_IsClosing())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We can only handle drag/dropping StorageItems (files) and plain Text
|
||||
// currently. If the format on the clipboard is anything else, returning
|
||||
// early here will prevent the drag/drop from doing anything.
|
||||
if (!(e.DataView().Contains(StandardDataFormats::StorageItems()) ||
|
||||
e.DataView().Contains(StandardDataFormats::Text())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
|
||||
e.AcceptedOperation(DataPackageOperation::Copy);
|
||||
|
||||
// Sets custom UI text
|
||||
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
|
||||
}
|
||||
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DragTextCaption"));
|
||||
}
|
||||
|
||||
// Sets if the caption is visible
|
||||
e.DragUIOverride().IsCaptionVisible(true);
|
||||
// Sets if the dragged content is visible
|
||||
e.DragUIOverride().IsContentVisible(false);
|
||||
// Sets if the glyph is visible
|
||||
e.DragUIOverride().IsGlyphVisible(false);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Checks if the uri is valid and sends an event if so
|
||||
// Arguments:
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ResetFontSize();
|
||||
winrt::Windows::Foundation::Size GetFontSize() const;
|
||||
|
||||
void SendInput(const winrt::hstring& input);
|
||||
void WriteInputString(const winrt::hstring& wstr, WriteInputStringType type);
|
||||
void ClearBuffer(Control::ClearBufferType clearType);
|
||||
|
||||
void ToggleShaderEffects();
|
||||
@@ -179,7 +179,6 @@ 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 ShowContextMenu();
|
||||
bool OpenQuickFixMenu();
|
||||
@@ -213,7 +212,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
til::typed_event<> WarningBell;
|
||||
til::typed_event<IInspectable, Control::KeySentEventArgs> KeySent;
|
||||
til::typed_event<IInspectable, Control::CharSentEventArgs> CharSent;
|
||||
til::typed_event<IInspectable, Control::StringSentEventArgs> StringSent;
|
||||
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
|
||||
til::typed_event<IInspectable, Control::WindowSizeChangedEventArgs> WindowSizeChanged;
|
||||
|
||||
@@ -229,7 +227,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
|
||||
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(PasteFromClipboard, IInspectable, IInspectable);
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -378,9 +376,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
|
||||
safe_void_coroutine _DragDropHandler(Windows::Foundation::IInspectable sender, Windows::UI::Xaml::DragEventArgs e);
|
||||
void _DragOverHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::DragEventArgs& e);
|
||||
|
||||
safe_void_coroutine _HyperlinkHandler(Windows::Foundation::IInspectable sender, Control::OpenHyperlinkEventArgs e);
|
||||
|
||||
void _BellLightOff(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
|
||||
@@ -429,8 +424,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
winrt::Windows::Foundation::Point _toPosInDips(const Core::Point terminalCellPos);
|
||||
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
|
||||
|
||||
void _pasteTextWithBroadcast(const winrt::hstring& text);
|
||||
|
||||
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
|
||||
void _showContextMenuAt(const winrt::Windows::Foundation::Point& controlRelativePos);
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, WriteToClipboardEventArgs> WriteToClipboard;
|
||||
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> PasteFromClipboard;
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
|
||||
@@ -80,7 +80,6 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, KeySentEventArgs> KeySent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CharSentEventArgs> CharSent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> StringSent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
|
||||
|
||||
|
||||
@@ -127,10 +126,9 @@ namespace Microsoft.Terminal.Control
|
||||
void ResetFontSize();
|
||||
|
||||
void ToggleShaderEffects();
|
||||
void SendInput(String input);
|
||||
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 WriteInputString(String text, WriteInputStringType type);
|
||||
|
||||
void BellLightOn();
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
AllowFocusOnInteraction="True"
|
||||
Background="Transparent"
|
||||
CharacterReceived="_CharacterHandler"
|
||||
DragOver="_DragOverHandler"
|
||||
Drop="_DragDropHandler"
|
||||
GotFocus="_GotFocusHandler"
|
||||
IsTabStop="True"
|
||||
KeyUp="_KeyUpHandler"
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include <winrt/Windows.ui.xaml.markup.h>
|
||||
#include <winrt/Windows.ui.xaml.shapes.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.UI.Xaml.Shapes.h>
|
||||
|
||||
|
||||
@@ -153,9 +153,9 @@ public:
|
||||
// _core.TitleChanged({ get_weak(), &TermControl::_bubbleTitleChanged });
|
||||
#define BUBBLED_FORWARDED_TYPED_EVENT(name, sender, args) \
|
||||
TYPED_EVENT(name, sender, args) \
|
||||
void _bubble##name(const sender& s, const args& a) \
|
||||
void _bubble##name(const sender&, const args& a) \
|
||||
{ \
|
||||
_##name##Handlers(s, a); \
|
||||
_##name##Handlers(*this, a); \
|
||||
}
|
||||
|
||||
// Use this macro to quick implement both the getter and setter for a property.
|
||||
|
||||
Reference in New Issue
Block a user