Skip to content

Commit

Permalink
fix mouse events in the wpf control
Browse files Browse the repository at this point in the history
  • Loading branch information
ZoeyR committed Feb 25, 2020
1 parent 4def49c commit a6c14fc
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 57 deletions.
275 changes: 240 additions & 35 deletions src/cascadia/PublicTerminalCore/HwndTerminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "pch.h"
#include "HwndTerminal.hpp"
#include <windowsx.h>
#include "../../types/TermControlUiaProvider.hpp"
#include <DefaultSettings.h>
#include "../../renderer/base/Renderer.hpp"
Expand Down Expand Up @@ -33,6 +34,36 @@ LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
{
return UiaReturnRawElementProvider(hwnd, wParam, lParam, terminal->_GetUiaProvider());
}
break;
case WM_LBUTTONDOWN:
LOG_IF_FAILED(terminal->_StartSelection(lParam));
return 0;
case WM_MOUSEMOVE:
if ((wParam & 0x0001) == 1)
{
LOG_IF_FAILED(terminal->_MoveSelection(lParam));
return 0;
}
break;
case WM_RBUTTONDOWN:
if (terminal->_terminal->IsSelectionActive())
{
try
{
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
terminal->_terminal->ClearSelection();
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
else
{
terminal->_PasteTextFromClipboard();
}
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
Expand Down Expand Up @@ -66,6 +97,7 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
_uiaProvider{ nullptr },
_uiaProviderInitialized{ false },
_currentDpi{ USER_DEFAULT_SCREEN_DPI }
//_pfnWriteCallback{ nullptr }
{
HINSTANCE hInstance = wil::GetModuleInstanceHandle();

Expand Down Expand Up @@ -128,7 +160,7 @@ HRESULT HwndTerminal::Initialize()
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
_terminal->SetDefaultBackground(RGB(5, 27, 80));
_terminal->SetDefaultForeground(RGB(255, 255, 255));

_terminal->SetWriteInputCallback([=](std::wstring& input) { _WriteTextToConnection(input); });
localPointerToThread->EnablePainting();

return S_OK;
Expand All @@ -139,27 +171,35 @@ void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> cal
_terminal->SetScrollPositionChangedCallback(callback);
}

void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
void HwndTerminal::_WriteTextToConnection(std::wstring& input) noexcept
{
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept {
const wchar_t* text = input.c_str();
const size_t textChars = wcslen(text) + 1;
const size_t textBytes = textChars * sizeof(wchar_t);
wchar_t* callingText = nullptr;
if (!_pfnWriteCallback)
{
return;
}

callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));
const wchar_t* text = input.c_str();
const size_t textChars = wcslen(text) + 1;
const size_t textBytes = textChars * sizeof(wchar_t);
wchar_t* callingText = nullptr;

if (callingText == nullptr)
{
callback(nullptr);
}
else
{
wcscpy_s(callingText, textChars, text);
callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));

callback(callingText);
}
});
if (callingText == nullptr)
{
_pfnWriteCallback(nullptr);
}
else
{
wcscpy_s(callingText, textChars, text);

_pfnWriteCallback(callingText);
}
}

void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
{
_pfnWriteCallback = callback;
}

::Microsoft::Console::Types::IUiaData* HwndTerminal::GetUiaData() const noexcept
Expand Down Expand Up @@ -320,42 +360,61 @@ void _stdcall TerminalUserScroll(void* terminal, int viewTop)
publicTerminal->_terminal->UserScrollViewport(viewTop);
}

HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed)
HRESULT HwndTerminal::_StartSelection(LPARAM lParam) noexcept
{
COORD terminalPosition = { cursorPosition };
bool altPressed = GetKeyState(VK_MENU) < 0;
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};

const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
const auto fontSize = this->_actualFont.GetSize();

RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);

terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;

publicTerminal->_terminal->SetSelectionAnchor(terminalPosition);
publicTerminal->_terminal->SetBoxSelection(altPressed);
try
{
this->_terminal->SetSelectionAnchor(cursorPosition);
this->_terminal->SetBoxSelection(altPressed);

publicTerminal->_renderer->TriggerSelection();
this->_renderer->TriggerSelection();
}
catch (...)
{
RETURN_HR(wil::ResultFromCaughtException());
}

return S_OK;
}

HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition)
HRESULT HwndTerminal::_MoveSelection(LPARAM lParam) noexcept
{
COORD terminalPosition = { cursorPosition };
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};

const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
const auto fontSize = this->_actualFont.GetSize();

RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);

terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;

publicTerminal->_terminal->SetEndSelectionPosition(terminalPosition);
publicTerminal->_renderer->TriggerSelection();
try
{
this->_terminal->SetEndSelectionPosition(cursorPosition);
this->_renderer->TriggerSelection();
}
catch (...)
{
RETURN_HR(wil::ResultFromCaughtException());
}

return S_OK;
}
Expand Down Expand Up @@ -501,6 +560,152 @@ void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
publicTerminal->_terminal->SetCursorVisible(visible);
}

// Routine Description:
// - Copies the text given onto the global system clipboard.
// Arguments:
// - rows - Rows of text data to copy
// - fAlsoCopyFormatting - true if the color and formatting should also be copied, false otherwise
HRESULT HwndTerminal::_CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting) noexcept
{
std::wstring finalString;

// Concatenate strings into one giant string to put onto the clipboard.
for (const auto& str : rows.text)
{
finalString += str;
}

// allocate the final clipboard data
const size_t cchNeeded = finalString.size() + 1;
const size_t cbNeeded = sizeof(wchar_t) * cchNeeded;
wil::unique_hglobal globalHandle(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbNeeded));
RETURN_LAST_ERROR_IF_NULL(globalHandle.get());

PWSTR pwszClipboard = (PWSTR)GlobalLock(globalHandle.get());
RETURN_LAST_ERROR_IF_NULL(pwszClipboard);

// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr = StringCchCopyW(pwszClipboard, cchNeeded, finalString.data());
GlobalUnlock(globalHandle.get());
RETURN_IF_FAILED(hr);

// Set global data to clipboard
RETURN_LAST_ERROR_IF(!OpenClipboard(_hwnd.get()));

{ // Clipboard Scope
auto clipboardCloser = wil::scope_exit([]() {
LOG_LAST_ERROR_IF(!CloseClipboard());
});

RETURN_LAST_ERROR_IF(!EmptyClipboard());
RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_UNICODETEXT, globalHandle.get()));

if (fAlsoCopyFormatting)
{
const auto& fontData = _actualFont;
int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / this->_currentDpi;
const COLORREF bgColor = _terminal->GetBackgroundColor(_terminal->GetDefaultBrushColors());

std::string HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor, "Hwnd Console Host");
_CopyToSystemClipboard(HTMLToPlaceOnClip, L"HTML Format");

std::string RTFToPlaceOnClip = TextBuffer::GenRTF(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor);
_CopyToSystemClipboard(RTFToPlaceOnClip, L"Rich Text Format");
}
}

// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandle.release();

return S_OK;
}

// Routine Description:
// - Copies the given string onto the global system clipboard in the specified format
// Arguments:
// - stringToCopy - The string to copy
// - lpszFormat - the name of the format
HRESULT HwndTerminal::_CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat) noexcept
{
const size_t cbData = stringToCopy.size() + 1; // +1 for '\0'
if (cbData)
{
wil::unique_hglobal globalHandleData(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbData));
RETURN_LAST_ERROR_IF_NULL(globalHandleData.get());

PSTR pszClipboardHTML = (PSTR)GlobalLock(globalHandleData.get());
RETURN_LAST_ERROR_IF_NULL(pszClipboardHTML);

// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr2 = StringCchCopyA(pszClipboardHTML, cbData, stringToCopy.data());
GlobalUnlock(globalHandleData.get());
RETURN_IF_FAILED(hr2);

UINT const CF_FORMAT = RegisterClipboardFormatW(lpszFormat);
RETURN_LAST_ERROR_IF(0 == CF_FORMAT);

RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_FORMAT, globalHandleData.get()));

// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandleData.release();
}

return S_OK;
}

void HwndTerminal::_PasteTextFromClipboard() noexcept
{
HANDLE ClipboardDataHandle;

// Clear any selection or scrolling that may be active.
_terminal->ClearSelection();

// Get paste data from clipboard
if (!OpenClipboard(_hwnd.get()))
{
return;
}

ClipboardDataHandle = GetClipboardData(CF_UNICODETEXT);
if (ClipboardDataHandle == nullptr)
{
CloseClipboard();
return;
}

PWCHAR pwstr = (PWCHAR)GlobalLock(ClipboardDataHandle);

_StringPaste(pwstr);

GlobalUnlock(ClipboardDataHandle);

CloseClipboard();
}

void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
{
if (pData == nullptr)
{
return;
}

try
{
std::wstring text(pData);
_WriteTextToConnection(text);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}

COORD HwndTerminal::GetFontSize() const
{
return _actualFont.GetSize();
Expand Down
15 changes: 10 additions & 5 deletions src/cascadia/PublicTerminalCore/HwndTerminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ __declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(void* terminal, dou
__declspec(dllexport) HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
__declspec(dllexport) HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
__declspec(dllexport) HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
__declspec(dllexport) const wchar_t* _stdcall TerminalGetSelection(void* terminal);
__declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
Expand Down Expand Up @@ -69,7 +67,7 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
FontInfo _actualFont;
int _currentDpi;
bool _uiaProviderInitialized;

std::function<void(wchar_t*)> _pfnWriteCallback;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;

std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
Expand All @@ -81,8 +79,6 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
friend HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
friend void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
friend void _stdcall TerminalUserScroll(void* terminal, int viewTop);
friend HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
friend HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
friend void _stdcall TerminalClearSelection(void* terminal);
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
Expand All @@ -91,7 +87,16 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);

void _UpdateFont(int newDpi);
void _WriteTextToConnection(std::wstring& text) noexcept;
HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting) noexcept;
HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat) noexcept;
void _PasteTextFromClipboard() noexcept;
void _StringPaste(const wchar_t* const pData) noexcept;

HRESULT _StartSelection(LPARAM lParam) noexcept;
HRESULT _MoveSelection(LPARAM lParam) noexcept;
IRawElementProviderSimple* _GetUiaProvider() noexcept;

// Inherited via IControlAccessibilityInfo
Expand Down
Loading

0 comments on commit a6c14fc

Please sign in to comment.