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
11 changes: 11 additions & 0 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,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 @@ -1906,6 +1909,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 @@ -200,6 +200,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);
winrt::fire_and_forget _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 @@ -58,6 +58,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 @@ -861,6 +861,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 @@ -111,6 +111,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 @@ -169,6 +171,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 uint32_t)> pfn) noexcept;
Expand All @@ -195,6 +198,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 uint32_t)> _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 @@ -578,3 +578,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::CopyToClipboard(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 @@ -34,6 +34,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 CopyToClipboard(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
13 changes: 13 additions & 0 deletions src/host/VtIo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,16 @@ bool VtIo::IsResizeQuirkEnabled() const
}
return S_OK;
}

// Method Description:
// - Process operating system control sequence.
// Arguments:
// - parameter: identifier of the OSC action to perform.
// - string: OSC string.
// Return Value:
// - S_OK if we successfully processed OSC or did nothing, else an
// appropriate HRESULT
[[nodiscard]] HRESULT VtIo::ProcessOsc(const size_t parameter, const std::wstring_view string)
{
return _pVtRenderEngine->ProcessOsc(parameter, string);
}
2 changes: 2 additions & 0 deletions src/host/VtIo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ namespace Microsoft::Console::VirtualTerminal
[[nodiscard]] HRESULT SuppressResizeRepaint();
[[nodiscard]] HRESULT SetCursorPosition(const COORD coordCursor);

[[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string);

void CloseInput() override;
void CloseOutput() override;

Expand Down
18 changes: 18 additions & 0 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using namespace Microsoft::Console;
using Microsoft::Console::Interactivity::ServiceLocator;
using Microsoft::Console::Render::VtEngine;

WriteBuffer::WriteBuffer(_In_ Microsoft::Console::IIoProvider& io) :
_io{ io },
Expand Down Expand Up @@ -728,3 +729,20 @@ bool ConhostInternalGetSet::PrivateIsVtInputEnabled() const
{
return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode();
}

// Routine Description:
// - Process copy-to-clipboard OSC.
// Arguments:
// - content - The content to copy to clipboard.
// Return Value:
// - true if not in VT mode or OSC was processed successfully, false otherwise.
bool ConhostInternalGetSet::CopyToClipboard(std::wstring_view content)
{
HRESULT hr = S_OK;
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.IsInVtIoMode())
{
hr = gci.GetVtIo()->ProcessOsc(VtEngine::OscActionCodes::CopyToClipboard, content);
}
return SUCCEEDED(hr);
}
2 changes: 2 additions & 0 deletions src/host/outputStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::

bool PrivateIsVtInputEnabled() const override;

bool CopyToClipboard(const std::wstring_view content) override;

private:
Microsoft::Console::IIoProvider& _io;
};
30 changes: 30 additions & 0 deletions src/renderer/vt/VtSequences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "precomp.h"
#include "vtrenderer.hpp"
#include "../../inc/conattrs.hpp"
#include "../../types/inc/convert.hpp"

#pragma hdrstop
using namespace Microsoft::Console::Render;
Expand Down Expand Up @@ -436,3 +437,32 @@ using namespace Microsoft::Console::Render;
{
return _Write("\x1b[29m");
}

// Method Description:
// - Writes operating system control sequence
// Arguments:
// - parameter: identifier of the OSC action to perform
// - string: OSC string
// Return Value:
// - S_OK if we succeeded or OSC code was ignored, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT VtEngine::ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept
{
HRESULT hr = S_OK;
std::string contentFormat;

switch (parameter)
{
case OscActionCodes::CopyToClipboard:
contentFormat = "\x1b]" + std::to_string(parameter) + ";" + ConvertToA(CP_UTF8, string) + "\x07";
break;
default:
break;
}

if (!contentFormat.empty())
{
hr = _Write(contentFormat);
}

return hr;
}
7 changes: 7 additions & 0 deletions src/renderer/vt/vtrenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ namespace Microsoft::Console::Render

[[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0;

[[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept;

void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner);
void BeginResizeRequest();
void EndResizeRequest();
Expand All @@ -110,6 +112,11 @@ namespace Microsoft::Console::Render

[[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept;

enum OscActionCodes : unsigned int
{
CopyToClipboard = 52,
};

protected:
wil::unique_hfile _hFile;
std::string _buffer;
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 @@ -102,6 +102,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 CopyToClipboard(std::wstring_view content) = 0; // OscCopyToClipboard

// 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 @@ -2070,6 +2070,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::CopyToClipboard(const std::wstring_view content)
{
return _pConApi->CopyToClipboard(content);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}

// Method Description:
// - Sets a single entry of the colortable to a new value
// Arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ namespace Microsoft::Console::VirtualTerminal
bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
bool SetCursorColor(const COLORREF cursorColor) override;

bool CopyToClipboard(const std::wstring_view content) override; // OscCopyToClipboard

bool SetColorTableEntry(const size_t tableIndex,
const DWORD color) override; // OscColorTable
bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/adapter/conGetSet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,7 @@ namespace Microsoft::Console::VirtualTerminal
const std::optional<SMALL_RECT> clipRect,
const COORD destinationOrigin,
const bool standardFillAttrs) = 0;

virtual bool CopyToClipboard(const std::wstring_view content) = 0;
};
}
2 changes: 2 additions & 0 deletions src/terminal/adapter/termDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,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 CopyToClipboard(std::wstring_view /*content*/) noexcept override { return false; } // OscCopyToClipboard

// DTTERM_WindowManipulation
bool WindowManipulation(const DispatchTypes::WindowManipulationType /*function*/,
const std::basic_string_view<size_t> /*params*/) noexcept override { return false; }
Expand Down
5 changes: 5 additions & 0 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@ class TestGetSet final : public ConGetSet
_expectedScrollRegion.Bottom = (bottom > 0) ? rect->Bottom - 1 : rect->Bottom;
}

bool CopyToClipboard(const std::wstring_view /*content*/)
{
return true;
}

~TestGetSet()
{
}
Expand Down
24 changes: 16 additions & 8 deletions src/terminal/parser/OutputStateMachineEngine.cpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
{
bool success = false;
std::wstring title;
std::wstring copyContent;
size_t tableIndex = 0;
DWORD color = 0;

Expand All @@ -734,7 +735,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
case OscActionCodes::SetIconAndWindowTitle:
case OscActionCodes::SetWindowIcon:
case OscActionCodes::SetWindowTitle:
success = _GetOscTitle(string, title);
success = _GetOscString(string, title);
break;
case OscActionCodes::SetColor:
success = _GetOscSetColorTable(string, tableIndex, color);
Expand All @@ -744,6 +745,9 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
case OscActionCodes::SetCursorColor:
success = _GetOscSetColor(string, color);
break;
case OscActionCodes::CopyToClipboard:
success = _GetOscString(string, copyContent);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
break;
case OscActionCodes::ResetCursorColor:
// the console uses 0xffffffff as an "invalid color" value
color = 0xffffffff;
Expand Down Expand Up @@ -780,6 +784,10 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
success = _dispatch->SetCursorColor(color);
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC);
break;
case OscActionCodes::CopyToClipboard:
success = _dispatch->CopyToClipboard(copyContent);
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC);
Copy link
Member

Choose a reason for hiding this comment

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

This may be the wrong TermTelemetry code!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I checked http://inwap.com/pdp10/ansicode.txt to find a proper name for that code, but failed. So I named it as OSCSCB where SCB means set clipboard. I didn't use SC because it's too generic and may cause conflict.

break;
case OscActionCodes::ResetCursorColor:
success = _dispatch->SetCursorColor(color);
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCRCC);
Expand Down Expand Up @@ -1189,16 +1197,16 @@ bool OutputStateMachineEngine::_VerifyDeviceAttributesParams(const std::basic_st
// Routine Description:
// - Null terminates, then returns, the string that we've collected as part of the OSC string.
// Arguments:
// - string - Osc String input
// - title - Where to place the Osc String to use as a title.
// - inString - Osc String input.
// - outString - Where to place the Osc String.
// Return Value:
// - True if there was a title to output. (a title with length=0 is still valid)
bool OutputStateMachineEngine::_GetOscTitle(const std::wstring_view string,
std::wstring& title) const
// - True if there was a string to output.
bool OutputStateMachineEngine::_GetOscString(const std::wstring_view inString,
std::wstring& outString) const
{
title = string;
outString = inString;

return !string.empty();
return !inString.empty();
}

// Routine Description:
Expand Down
5 changes: 3 additions & 2 deletions src/terminal/parser/OutputStateMachineEngine.hpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ namespace Microsoft::Console::VirtualTerminal
SetForegroundColor = 10,
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
SetBackgroundColor = 11,
SetCursorColor = 12,
CopyToClipboard = 52,
ResetForegroundColor = 110, // Not implemented
ResetBackgroundColor = 111, // Not implemented
ResetCursorColor = 112,
Expand Down Expand Up @@ -191,8 +192,8 @@ namespace Microsoft::Console::VirtualTerminal
size_t& topMargin,
size_t& bottomMargin) const noexcept;

bool _GetOscTitle(const std::wstring_view string,
std::wstring& title) const;
bool _GetOscString(const std::wstring_view inString,
std::wstring& outString) const;

static constexpr size_t DefaultTabDistance = 1;
bool _GetTabDistance(const std::basic_string_view<size_t> parameters,
Expand Down
2 changes: 1 addition & 1 deletion src/terminal/parser/stateMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ static constexpr bool _isOscTerminationInitiator(const wchar_t wch) noexcept
// - True if it is. False if it isn't.
static constexpr bool _isOscInvalid(const wchar_t wch) noexcept
{
return wch <= L'\x17' ||
return (wch <= L'\x17' && wch != L'\x0a' && wch != L'\x0d') ||
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, this wasn't. Before I added base64 support, to support passing "\r\n" into the clipboard, the state machine should view \r 0x0d and \n 0x0a as valid OSC characters. But wch <= L'\x17' will make them invalid, so I changed it to (wch <= L'\x17' && wch != L'\x0a' && wch != L'\x0d').

Anyway, that change is no longer needed, because base64-encoded string won't include \r and \n.

wch == L'\x19' ||
(wch >= L'\x1c' && wch <= L'\x1f');
}
Expand Down
Loading