mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Add support for OSC 52 clipboard copy in conhost (#18949)
This adds support for copying to the clipboard in conhost using the OSC 52 escape sequence, extending the original implementation which was for Windows Terminal only. The Windows Terminal implementation was added in PR #5823. Because the clipboard can't be accessed from a background thread, this works by saving the content in a global variable, and then posting a custom message to the main GUI thread, which takes care of the actual copy operation. Validation: I've manually confirmed that tmux copy mode is now able to copy to the system clipboard. Closes #18943
This commit is contained in:
parent
155d8a9ab2
commit
4abc041eb7
@ -13,6 +13,7 @@
|
||||
#include "srvinit.h"
|
||||
|
||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||
#include "../interactivity/win32/CustomWindowMessages.h"
|
||||
#include "../types/inc/convert.hpp"
|
||||
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
@ -179,6 +180,32 @@ void CONSOLE_INFORMATION::SetBracketedPasteMode(const bool enabled) noexcept
|
||||
_bracketedPasteMode = enabled;
|
||||
}
|
||||
|
||||
void CONSOLE_INFORMATION::CopyTextToClipboard(const std::wstring_view text)
|
||||
{
|
||||
const auto window = ServiceLocator::LocateConsoleWindow();
|
||||
if (window)
|
||||
{
|
||||
// The clipboard can only be updated from the main GUI thread, so we
|
||||
// need to post a message to trigger the actual copy operation. But if
|
||||
// the pending clipboard content is already set, a message would have
|
||||
// already been posted, so there's no need to post another one.
|
||||
const auto clipboardMessageSent = _pendingClipboardText.has_value();
|
||||
_pendingClipboardText = text;
|
||||
if (!clipboardMessageSent)
|
||||
{
|
||||
PostMessageW(window->GetWindowHandle(), CM_UPDATE_CLIPBOARD, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::wstring> CONSOLE_INFORMATION::UsePendingClipboardText()
|
||||
{
|
||||
// Once the pending text has been used, we clear the variable to let the
|
||||
// CopyTextToClipboard method know that the last CM_UPDATE_CLIPBOARD message
|
||||
// has been processed, and future updates will require another message.
|
||||
return std::exchange(_pendingClipboardText, {});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Return the active screen buffer of the console.
|
||||
// Arguments:
|
||||
|
||||
@ -280,9 +280,9 @@ unsigned int ConhostInternalGetSet::GetInputCodePage() const
|
||||
// - content - the text to be copied.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view /*content*/)
|
||||
void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view content)
|
||||
{
|
||||
// TODO
|
||||
ServiceLocator::LocateGlobals().getConsoleInformation().CopyTextToClipboard(content);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@ -126,6 +126,8 @@ public:
|
||||
|
||||
bool GetBracketedPasteMode() const noexcept;
|
||||
void SetBracketedPasteMode(const bool enabled) noexcept;
|
||||
void CopyTextToClipboard(const std::wstring_view text);
|
||||
std::optional<std::wstring> UsePendingClipboardText();
|
||||
|
||||
void SetTitle(const std::wstring_view newTitle);
|
||||
void SetTitlePrefix(const std::wstring_view newTitlePrefix);
|
||||
@ -160,6 +162,7 @@ private:
|
||||
SCREEN_INFORMATION* pCurrentScreenBuffer = nullptr;
|
||||
COOKED_READ_DATA* _cookedReadData = nullptr; // non-ownership pointer
|
||||
bool _bracketedPasteMode = false;
|
||||
std::optional<std::wstring> _pendingClipboardText;
|
||||
|
||||
Microsoft::Console::VirtualTerminal::VtIo _vtIo;
|
||||
Microsoft::Console::CursorBlinker _blinker;
|
||||
|
||||
@ -24,6 +24,22 @@ using namespace Microsoft::Console::Types;
|
||||
|
||||
#pragma region Public Methods
|
||||
|
||||
void Clipboard::CopyText(const std::wstring& text)
|
||||
{
|
||||
const auto clipboard = _openClipboard(ServiceLocator::LocateConsoleWindow()->GetWindowHandle());
|
||||
if (!clipboard)
|
||||
{
|
||||
LOG_LAST_ERROR();
|
||||
return;
|
||||
}
|
||||
|
||||
EmptyClipboard();
|
||||
// As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
|
||||
// CF_UNICODETEXT: [...] A null character signals the end of the data.
|
||||
// --> We add +1 to the length. This works because .c_str() is null-terminated.
|
||||
_copyToClipboard(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
// Arguments:
|
||||
// - fAlsoCopyFormatting - Place colored HTML & RTF text onto the clipboard as well as the usual plain text.
|
||||
// Return Value:
|
||||
|
||||
@ -29,4 +29,6 @@
|
||||
#define CM_SET_KEYBOARD_LAYOUT (WM_USER+19)
|
||||
#endif
|
||||
|
||||
#define CM_UPDATE_CLIPBOARD (WM_USER+20)
|
||||
|
||||
// clang-format on
|
||||
|
||||
@ -29,6 +29,7 @@ namespace Microsoft::Console::Interactivity::Win32
|
||||
public:
|
||||
static Clipboard& Instance();
|
||||
|
||||
void CopyText(const std::wstring& text);
|
||||
void Copy(_In_ const bool fAlsoCopyFormatting = false);
|
||||
void Paste();
|
||||
void PasteDrop(HDROP drop);
|
||||
|
||||
@ -773,6 +773,15 @@ static constexpr TsfDataProvider s_tsfDataProvider;
|
||||
}
|
||||
#endif // DBG
|
||||
|
||||
case CM_UPDATE_CLIPBOARD:
|
||||
{
|
||||
if (const auto clipboardText = gci.UsePendingClipboardText())
|
||||
{
|
||||
Clipboard::Instance().CopyText(clipboardText.value());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_CONSOLE_CARET:
|
||||
case EVENT_CONSOLE_UPDATE_REGION:
|
||||
case EVENT_CONSOLE_UPDATE_SIMPLE:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user