Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OSC 52 (copy-to-clipboard) #5823

Merged
14 commits merged into from
Jun 30, 2020
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,7 @@ OSCCT
OSCFG
OSCRCC
OSCSCC
OSCSCB
OSCWT
OSDEPENDSROOT
osfhandle
Expand Down
5 changes: 4 additions & 1 deletion .github/actions/spell-check/patterns/patterns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ Scro\&ll
# selectionInput.cpp
:\\windows\\syste\b
TestUtils::VerifyExpectedString\(tb, L"[^"]+"
hostSm\.ProcessString\(L"[^"]+"
(?:hostSm|mach)\.ProcessString\(L"[^"]+"
\b([A-Za-z])\1{3,}\b
Base64::s_(?:En|De)code\(L"[^"]+"
VERIFY_ARE_EQUAL\(L"[^"]+"
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"
11 changes: 11 additions & 0 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto pfnTerminalCursorPositionChanged = std::bind(&TermControl::_TerminalCursorPositionChanged, this);
_terminal->SetCursorPositionChangedCallback(pfnTerminalCursorPositionChanged);

auto pfnCopyToClipboard = std::bind(&TermControl::_CopyToClipboard, this, std::placeholders::_1);
_terminal->SetCopyToClipboardCallback(pfnCopyToClipboard);

// This event is explicitly revoked in the destructor: does not need weak_ref
auto onReceiveOutputFn = [this](const hstring str) {
_terminal->Write(str);
Expand Down Expand Up @@ -2009,6 +2012,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_titleChangedHandlers(winrt::hstring{ wstr });
}

void TermControl::_CopyToClipboard(const std::wstring_view& wstr)
{
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(wstr),
winrt::hstring(L""),
winrt::hstring(L""));
_clipboardCopyHandlers(*this, *copyArgs);
}

// Method Description:
// - Update the position and size of the scrollbar to match the given
// viewport top, viewport height, and buffer size.
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void _DoResizeUnderLock(const double newWidth, const double newHeight);
void _RefreshSizeUnderLock();
void _TerminalTitleChanged(const std::wstring_view& wstr);
void _CopyToClipboard(const std::wstring_view& wstr);
void _TerminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize);
winrt::fire_and_forget _TerminalCursorPositionChanged();

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalCore/ITerminalApi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ namespace Microsoft::Terminal::Core

virtual bool IsVtInputEnabled() const = 0;

virtual bool CopyToClipboard(std::wstring_view content) noexcept = 0;

protected:
ITerminalApi() = default;
};
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,11 @@ void Terminal::SetTitleChangedCallback(std::function<void(const std::wstring_vie
_pfnTitleChanged.swap(pfn);
}

void Terminal::SetCopyToClipboardCallback(std::function<void(const std::wstring_view&)> pfn) noexcept
{
_pfnCopyToClipboard.swap(pfn);
}

void Terminal::SetScrollPositionChangedCallback(std::function<void(const int, const int, const int)> pfn) noexcept
{
_pfnScrollPositionChanged.swap(pfn);
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ class Microsoft::Terminal::Core::Terminal final :
bool EnableAlternateScrollMode(const bool enabled) noexcept override;

bool IsVtInputEnabled() const noexcept override;

bool CopyToClipboard(std::wstring_view content) noexcept override;
#pragma endregion

#pragma region ITerminalInput
Expand Down Expand Up @@ -172,6 +174,7 @@ class Microsoft::Terminal::Core::Terminal final :

void SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept;
void SetTitleChangedCallback(std::function<void(const std::wstring_view&)> pfn) noexcept;
void SetCopyToClipboardCallback(std::function<void(const std::wstring_view&)> pfn) noexcept;
void SetScrollPositionChangedCallback(std::function<void(const int, const int, const int)> pfn) noexcept;
void SetCursorPositionChangedCallback(std::function<void()> pfn) noexcept;
void SetBackgroundCallback(std::function<void(const COLORREF)> pfn) noexcept;
Expand All @@ -198,6 +201,7 @@ class Microsoft::Terminal::Core::Terminal final :
private:
std::function<void(std::wstring&)> _pfnWriteInput;
std::function<void(const std::wstring_view&)> _pfnTitleChanged;
std::function<void(const std::wstring_view&)> _pfnCopyToClipboard;
std::function<void(const int, const int, const int)> _pfnScrollPositionChanged;
std::function<void(const COLORREF)> _pfnBackgroundColorChanged;
std::function<void()> _pfnCursorPositionChanged;
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalCore/TerminalApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,12 @@ bool Terminal::EnableCursorBlinking(const bool enable) noexcept
_buffer->GetCursor().SetIsOn(true);
return true;
}

bool Terminal::CopyToClipboard(std::wstring_view content) noexcept
try
{
_pfnCopyToClipboard(content);

return true;
}
CATCH_LOG_RETURN_FALSE()
7 changes: 7 additions & 0 deletions src/cascadia/TerminalCore/TerminalDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ try
}
CATCH_LOG_RETURN_FALSE()

bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept
try
{
return _terminalApi.CopyToClipboard(content);
}
CATCH_LOG_RETURN_FALSE()

// Method Description:
// - Sets the default foreground color to a new value
// Arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalCore/TerminalDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc
bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override;
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;

bool SetClipboard(std::wstring_view content) noexcept override;

bool SetDefaultForeground(const DWORD color) noexcept override;
bool SetDefaultBackground(const DWORD color) noexcept override;
bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; // ED
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/adapter/ITermDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor

virtual bool SetClipboard(std::wstring_view content) = 0; // OSCSetClipboard

// DTTERM_WindowManipulation
virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function,
const std::basic_string_view<size_t> parameters) = 0;
Expand Down
11 changes: 11 additions & 0 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,17 @@ bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor)
return _pConApi->SetCursorColor(cursorColor);
}

// Routine Description:
// - OSC Copy to Clipboard
// Arguments:
// - content - The content to copy to clipboard. Must be null terminated.
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::SetClipboard(const std::wstring_view /*content*/) noexcept
{
return false;
}

// Method Description:
// - Sets a single entry of the colortable to a new value
// Arguments:
Expand Down
6 changes: 4 additions & 2 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace Microsoft::Console::VirtualTerminal
bool CarriageReturn() override; // CR
bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) override; // IND, NEL, LF, FF, VT
bool ReverseLineFeed() override; // RI
bool SetWindowTitle(const std::wstring_view title) override; // OscWindowTitle
bool SetWindowTitle(const std::wstring_view title) override; // OSCWindowTitle
bool UseAlternateScreenBuffer() override; // ASBSET
bool UseMainScreenBuffer() override; // ASBRST
bool HorizontalTabSet() override; // HTS
Expand All @@ -106,8 +106,10 @@ namespace Microsoft::Console::VirtualTerminal
bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
bool SetCursorColor(const COLORREF cursorColor) override;

bool SetClipboard(const std::wstring_view content) noexcept override; // OSCSetClipboard

bool SetColorTableEntry(const size_t tableIndex,
const DWORD color) override; // OscColorTable
const DWORD color) override; // OSCColorTable
bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground
bool SetDefaultBackground(const DWORD color) override; // OSCDefaultBackground

Expand Down
2 changes: 2 additions & 0 deletions src/terminal/adapter/termDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) noexcept override { return false; } // DECSCUSR
bool SetCursorColor(const COLORREF /*color*/) noexcept override { return false; } // OSCSetCursorColor, OSCResetCursorColor

bool SetClipboard(std::wstring_view /*content*/) noexcept override { return false; } // OscSetClipboard

// DTTERM_WindowManipulation
bool WindowManipulation(const DispatchTypes::WindowManipulationType /*function*/,
const std::basic_string_view<size_t> /*params*/) noexcept override { return false; }
Expand Down
44 changes: 44 additions & 0 deletions src/terminal/parser/OutputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "stateMachine.hpp"
#include "OutputStateMachineEngine.hpp"
#include "base64.hpp"

#include "ascii.hpp"

Expand Down Expand Up @@ -881,6 +882,8 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
{
bool success = false;
std::wstring title;
std::wstring setClipboardContent;
bool queryClipboard = false;
size_t tableIndex = 0;
DWORD color = 0;

Expand All @@ -899,6 +902,9 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
case OscActionCodes::SetCursorColor:
success = _GetOscSetColor(string, color);
break;
case OscActionCodes::SetClipboard:
success = _GetOscSetClipboard(string, setClipboardContent, queryClipboard);
break;
case OscActionCodes::ResetCursorColor:
// the console uses 0xffffffff as an "invalid color" value
color = 0xffffffff;
Expand Down Expand Up @@ -935,6 +941,13 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
success = _dispatch->SetCursorColor(color);
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC);
break;
case OscActionCodes::SetClipboard:
if (!queryClipboard)
{
success = _dispatch->SetClipboard(setClipboardContent);
}
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCB);
break;
case OscActionCodes::ResetCursorColor:
success = _dispatch->SetCursorColor(color);
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCRCC);
Expand Down Expand Up @@ -1859,6 +1872,37 @@ bool OutputStateMachineEngine::_GetRepeatCount(std::basic_string_view<size_t> pa
return success;
}

// Routine Description:
// - Parse OscSetClipboard parameters with the format `Pc;Pd`. Currently the first parameter `Pc` is
// ignored. The second parameter `Pd` should be a valid base64 string or character `?`.
// Arguments:
// - string - Osc String input.
// - content - Content to set to clipboard.
// - queryClipboard - Whether to get clipboard content and return it to terminal with base64 encoded.
// Return Value:
// - True if there was a valid base64 string or the passed parameter was `?`.
bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view string,
std::wstring& content,
bool& queryClipboard) const noexcept
{
const size_t pos = string.find(';');
Copy link
Member

Choose a reason for hiding this comment

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

I supposed @DHowett is right, and we could save ourselves a bit of work with a

if (_pfnFlushToTerminal != nullptr)
{
    return false;
}

here. That should cause conpty to pass this through without decoding it once first. That being said, I'm pretty sure setting the clipboard isn't something that's going to be done in a tight loop or end up as any sort of hot path, so I wouldn't block this PR on account of that.

if (pos != std::wstring_view::npos)
{
const std::wstring_view substr = string.substr(pos + 1);
if (substr == L"?")
{
queryClipboard = true;
return true;
}
else
{
return Base64::s_Decode(substr, content);
}
}

return false;
}

// Method Description:
// - Clears our last stored character. The last stored character is the last
// graphical character we printed, which is reset if any other action is
Expand Down
5 changes: 5 additions & 0 deletions src/terminal/parser/OutputStateMachineEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ namespace Microsoft::Console::VirtualTerminal
SetForegroundColor = 10,
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
SetBackgroundColor = 11,
SetCursorColor = 12,
SetClipboard = 52,
ResetForegroundColor = 110, // Not implemented
ResetBackgroundColor = 111, // Not implemented
ResetCursorColor = 112
Expand Down Expand Up @@ -252,6 +253,10 @@ namespace Microsoft::Console::VirtualTerminal
bool _GetRepeatCount(const std::basic_string_view<size_t> parameters,
size_t& repeatCount) const noexcept;

bool _GetOscSetClipboard(const std::wstring_view string,
std::wstring& content,
bool& queryClipboard) const noexcept;

void _ClearLastChar() noexcept;
};
}
Loading