Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/host/consoleInformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Comment on lines +188 to +197
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if I've just misunderstood how you're supposed to use the windows clipboard, but I couldn't get it working from a background thread, and this technique worked for me. I don't know if maybe there's a better way to do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, reading doesn't require a HWND but writing does, to my knowledge.

}
}

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:
Expand Down
4 changes: 2 additions & 2 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions src/host/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class CONSOLE_INFORMATION :

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);
Expand Down Expand Up @@ -160,6 +162,7 @@ class CONSOLE_INFORMATION :
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;
Expand Down
16 changes: 16 additions & 0 deletions src/interactivity/win32/Clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Comment on lines +27 to +41
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The content of this method is copied directly from StoreSelectionToClipboard, but there's really not much to it, so I didn't think there was any point in trying to share the code somehow.


// Arguments:
// - fAlsoCopyFormatting - Place colored HTML & RTF text onto the clipboard as well as the usual plain text.
// Return Value:
Expand Down
2 changes: 2 additions & 0 deletions src/interactivity/win32/CustomWindowMessages.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@
#define CM_SET_KEYBOARD_LAYOUT (WM_USER+19)
#endif

#define CM_UPDATE_CLIPBOARD (WM_USER+20)

// clang-format on
1 change: 1 addition & 0 deletions src/interactivity/win32/clipboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions src/interactivity/win32/windowproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading