mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 06:09:50 +00:00
Compare commits
4 Commits
release-1.
...
wpf-render
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06001e4acc | ||
|
|
1d022aa816 | ||
|
|
f68bd09d7a | ||
|
|
8fb90dcea5 |
@@ -294,8 +294,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{D3EF
|
||||
build\scripts\Test-WindowsTerminalPackage.ps1 = build\scripts\Test-WindowsTerminalPackage.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dx.Unit.Tests", "src\renderer\dx\ut_dx\Dx.Unit.Tests.vcxproj", "{95B136F9-B238-490C-A7C5-5843C1FECAC4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
|
||||
@@ -1425,32 +1423,6 @@ Global
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.Build.0 = Release|x64
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.Build.0 = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.Build.0 = Debug|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.ActiveCfg = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.Build.0 = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.Build.0 = Release|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
@@ -1465,12 +1437,6 @@ Global
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.Build.0 = Release|x64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.Build.0 = Release|Win32
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
@@ -1559,7 +1525,6 @@ Global
|
||||
{53DD5520-E64C-4C06-B472-7CE62CA539C9} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{6B5A44ED-918D-4747-BFB1-2472A1FCA173} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
EndGlobalSection
|
||||
|
||||
@@ -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"
|
||||
@@ -33,6 +34,33 @@ 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 (WI_IsFlagSet(wParam, MK_LBUTTON))
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
terminal->_PasteTextFromClipboard();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
@@ -65,7 +93,8 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI }
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr }
|
||||
{
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
@@ -128,7 +157,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) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
return S_OK;
|
||||
@@ -139,27 +168,24 @@ void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> cal
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
}
|
||||
|
||||
void HwndTerminal::_WriteTextToConnection(const std::wstring& input) noexcept
|
||||
{
|
||||
if (!_pfnWriteCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto callingText{ wil::make_cotaskmem_string(input.data(), input.size()) };
|
||||
_pfnWriteCallback(callingText.release());
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
|
||||
{
|
||||
_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;
|
||||
|
||||
callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));
|
||||
|
||||
if (callingText == nullptr)
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcscpy_s(callingText, textChars, text);
|
||||
|
||||
callback(callingText);
|
||||
}
|
||||
});
|
||||
_pfnWriteCallback = callback;
|
||||
}
|
||||
|
||||
::Microsoft::Console::Types::IUiaData* HwndTerminal::GetUiaData() const noexcept
|
||||
@@ -320,45 +346,54 @@ 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
|
||||
try
|
||||
{
|
||||
COORD terminalPosition = { cursorPosition };
|
||||
const 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->SetBlockSelection(altPressed);
|
||||
this->_terminal->SetSelectionAnchor(cursorPosition);
|
||||
this->_terminal->SetBlockSelection(altPressed);
|
||||
|
||||
publicTerminal->_renderer->TriggerSelection();
|
||||
this->_renderer->TriggerSelection();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition)
|
||||
HRESULT HwndTerminal::_MoveSelection(LPARAM lParam) noexcept
|
||||
try
|
||||
{
|
||||
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->SetSelectionEnd(terminalPosition);
|
||||
publicTerminal->_renderer->TriggerSelection();
|
||||
this->_terminal->SetSelectionEnd(cursorPosition);
|
||||
this->_renderer->TriggerSelection();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
@@ -501,6 +536,144 @@ 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)
|
||||
{
|
||||
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 = static_cast<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([]() noexcept {
|
||||
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)
|
||||
{
|
||||
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 = static_cast<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
|
||||
{
|
||||
// Get paste data from clipboard
|
||||
if (!OpenClipboard(_hwnd.get()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE ClipboardDataHandle = GetClipboardData(CF_UNICODETEXT);
|
||||
if (ClipboardDataHandle == nullptr)
|
||||
{
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
PCWCH pwstr = static_cast<PCWCH>(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();
|
||||
}
|
||||
|
||||
COORD HwndTerminal::GetFontSize() const
|
||||
{
|
||||
return _actualFont.GetSize();
|
||||
|
||||
@@ -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);
|
||||
@@ -69,7 +67,7 @@ private:
|
||||
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;
|
||||
@@ -81,8 +79,6 @@ private:
|
||||
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);
|
||||
@@ -91,7 +87,16 @@ private:
|
||||
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(const std::wstring& text) noexcept;
|
||||
HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting);
|
||||
HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat);
|
||||
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
|
||||
|
||||
@@ -230,23 +230,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.Focus();
|
||||
NativeMethods.SetFocus(this.hwnd);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_LBUTTONDOWN:
|
||||
this.LeftClickHandler((int)lParam);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_RBUTTONDOWN:
|
||||
if (NativeMethods.TerminalIsSelectionActive(this.terminal))
|
||||
{
|
||||
Clipboard.SetText(NativeMethods.TerminalGetSelection(this.terminal));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.connection.WriteInput(Clipboard.GetText());
|
||||
}
|
||||
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_MOUSEMOVE:
|
||||
this.MouseMoveHandler((int)wParam, (int)lParam);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_KEYDOWN:
|
||||
NativeMethods.TerminalSetCursorVisible(this.terminal, true);
|
||||
NativeMethods.TerminalClearSelection(this.terminal);
|
||||
|
||||
@@ -650,11 +650,6 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||
// Ensure that our cluster vector is clear.
|
||||
clusters.clear();
|
||||
|
||||
// Reset our flag to know when we're in the special circumstance
|
||||
// of attempting to draw only the right-half of a two-column character
|
||||
// as the first item in our run.
|
||||
bool trimLeft = false;
|
||||
|
||||
// This inner loop will accumulate clusters until the color changes.
|
||||
// When the color changes, it will save the new color off and break.
|
||||
do
|
||||
@@ -666,33 +661,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||
}
|
||||
|
||||
// Walk through the text data and turn it into rendering clusters.
|
||||
|
||||
// If we're on the first cluster to be added and it's marked as "trailing"
|
||||
// (a.k.a. the right half of a two column character), then we need some special handling.
|
||||
if (clusters.empty() && it->DbcsAttr().IsTrailing())
|
||||
{
|
||||
// If we have room to move to the left to start drawing...
|
||||
if (screenPoint.X > 0)
|
||||
{
|
||||
// Move left to the one so the whole character can be struck correctly.
|
||||
--screenPoint.X;
|
||||
// And tell the next function to trim off the left half of it.
|
||||
trimLeft = true;
|
||||
// And add one to the number of columns we expect it to take as we insert it.
|
||||
clusters.emplace_back(it->Chars(), it->Columns() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we didn't have room, move to the right one and just skip this one.
|
||||
screenPoint.X++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Otherwise if it's not a special case, just insert it as is.
|
||||
else
|
||||
{
|
||||
clusters.emplace_back(it->Chars(), it->Columns());
|
||||
}
|
||||
clusters.emplace_back(it->Chars(), it->Columns());
|
||||
|
||||
// Advance the cluster and column counts.
|
||||
const auto columnCount = clusters.back().GetColumns();
|
||||
@@ -702,7 +671,8 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||
} while (it);
|
||||
|
||||
// Do the painting.
|
||||
THROW_IF_FAILED(pEngine->PaintBufferLine({ clusters.data(), clusters.size() }, screenPoint, trimLeft, lineWrapped));
|
||||
// TODO: Calculate when trim left should be TRUE
|
||||
THROW_IF_FAILED(pEngine->PaintBufferLine({ clusters.data(), clusters.size() }, screenPoint, false, lineWrapped));
|
||||
|
||||
// If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data)
|
||||
if (_pData->IsGridLineDrawingAllowed())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
@@ -154,7 +154,18 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
}
|
||||
|
||||
// Resequence the resulting runs in order before returning to caller.
|
||||
_OrderRuns();
|
||||
const size_t totalRuns = _runs.size();
|
||||
std::vector<LinkedRun> runs;
|
||||
runs.resize(totalRuns);
|
||||
|
||||
UINT32 nextRunIndex = 0;
|
||||
for (size_t i = 0; i < totalRuns; ++i)
|
||||
{
|
||||
runs.at(i) = _runs.at(nextRunIndex);
|
||||
nextRunIndex = _runs.at(nextRunIndex).nextRunIndex;
|
||||
}
|
||||
|
||||
_runs.swap(runs);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
@@ -357,31 +368,6 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
{
|
||||
LOG_IF_FAILED(_CorrectGlyphRun(runIndex));
|
||||
}
|
||||
|
||||
// If scale corrections were needed, we need to split the run.
|
||||
for (auto [index, scale] : _glyphScaleCorrections)
|
||||
{
|
||||
// Don't split in the middle of a surrogate pair.
|
||||
const auto after = IS_HIGH_SURROGATE(_text.at(index)) ? 2 : 1;
|
||||
|
||||
// Split after the adjustment first so it
|
||||
// takes a copy of all the run properties before we modify them.
|
||||
// GH 4665: This is the other half of the potential future perf item.
|
||||
// If glyphs needing the same scale are coalesced, we could
|
||||
// break fewer times and have fewer runs.
|
||||
_SetCurrentRun(index + after);
|
||||
_SplitCurrentRun(index + after);
|
||||
|
||||
// Now split just this glyph off.
|
||||
_SetCurrentRun(index);
|
||||
_SplitCurrentRun(index);
|
||||
|
||||
// Get the run with the one glyph and adjust the scale.
|
||||
auto& run = _GetCurrentRun();
|
||||
run.fontScale = scale;
|
||||
}
|
||||
|
||||
_OrderRuns();
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
@@ -390,94 +376,90 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
// Routine Description:
|
||||
// - Adjusts the advances for each glyph in the run so it fits within a fixed-column count of cells.
|
||||
// Arguments:
|
||||
// - runIndex - The ID number of the internal runs array to use while shaping.
|
||||
// - runIndex - The ID number of the internal runs array to use while shaping
|
||||
// Return Value:
|
||||
// - S_OK or suitable DirectWrite or STL error code
|
||||
[[nodiscard]] HRESULT CustomTextLayout::_CorrectGlyphRun(const UINT32 runIndex) noexcept
|
||||
try
|
||||
{
|
||||
const Run& run = _runs.at(runIndex);
|
||||
|
||||
if (run.textLength == 0)
|
||||
try
|
||||
{
|
||||
return S_FALSE; // Nothing to do..
|
||||
}
|
||||
Run& run = _runs.at(runIndex);
|
||||
|
||||
// We're going to walk through and check for advances that don't match the space that we expect to give out.
|
||||
|
||||
DWRITE_FONT_METRICS1 metrics;
|
||||
run.fontFace->GetMetrics(&metrics);
|
||||
|
||||
// Walk through advances and space out characters that are too small to consume their box.
|
||||
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
|
||||
{
|
||||
// Advance is how wide in pixels the glyph is
|
||||
auto& advance = _glyphAdvances.at(i);
|
||||
|
||||
// Offsets is how far to move the origin (in pixels) from where it is
|
||||
auto& offset = _glyphOffsets.at(i);
|
||||
|
||||
// Get how many columns we expected the glyph to have and mutiply into pixels.
|
||||
const auto columns = _textClusterColumns.at(i);
|
||||
const auto advanceExpected = static_cast<float>(columns * _width);
|
||||
|
||||
// If what we expect is bigger than what we have... pad it out.
|
||||
if (advanceExpected > advance)
|
||||
if (run.textLength == 0)
|
||||
{
|
||||
// Get the amount of space we have leftover.
|
||||
const auto diff = advanceExpected - advance;
|
||||
|
||||
// Move the X offset (pixels to the right from the left edge) by half the excess space
|
||||
// so half of it will be left of the glyph and the other half on the right.
|
||||
offset.advanceOffset += diff / 2;
|
||||
|
||||
// Set the advance to the perfect width we want.
|
||||
advance = advanceExpected;
|
||||
return S_FALSE; // Nothing to do..
|
||||
}
|
||||
// If what we expect is smaller than what we have... rescale the font size to get a smaller glyph to fit.
|
||||
else if (advanceExpected < advance)
|
||||
|
||||
// We're going to walk through and check for advances that don't match the space that we expect to give out.
|
||||
|
||||
DWRITE_FONT_METRICS1 metrics;
|
||||
run.fontFace->GetMetrics(&metrics);
|
||||
|
||||
// Walk through advances and space out characters that are too small to consume their box.
|
||||
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
|
||||
{
|
||||
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
|
||||
// height proportional to the width that we desire.
|
||||
INT32 advanceInDesignUnits;
|
||||
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices.at(i), &advanceInDesignUnits));
|
||||
// Advance is how wide in pixels the glyph is
|
||||
auto& advance = _glyphAdvances.at(i);
|
||||
|
||||
// When things are drawn, we want the font size (as specified in the base font in the original format)
|
||||
// to be scaled by some factor.
|
||||
// i.e. if the original font size was 16, we might want to draw this glyph with a 15.2 size font so
|
||||
// the width (and height) of the glyph will shrink to fit the monospace cell box.
|
||||
// Offsets is how far to move the origin (in pixels) from where it is
|
||||
auto& offset = _glyphOffsets.at(i);
|
||||
|
||||
// This pattern is copied from the DxRenderer's algorithm for figuring out the font height for a specific width
|
||||
// and was advised by the DirectWrite team.
|
||||
const float widthAdvance = static_cast<float>(advanceInDesignUnits) / metrics.designUnitsPerEm;
|
||||
const auto fontSizeWant = advanceExpected / widthAdvance;
|
||||
const auto scaleProposed = fontSizeWant / _format->GetFontSize();
|
||||
// Get how many columns we expected the glyph to have and mutiply into pixels.
|
||||
const auto columns = _textClusterColumns.at(i);
|
||||
const auto advanceExpected = static_cast<float>(columns * _width);
|
||||
|
||||
// Store the glyph scale correction for future run breaking
|
||||
// GH 4665: In theory, we could also store the length of the new run and coalesce
|
||||
// in case two adjacent glyphs need the same scale factor.
|
||||
_glyphScaleCorrections.push_back(std::tuple{ i, scaleProposed });
|
||||
// If what we expect is bigger than what we have... pad it out.
|
||||
if (advanceExpected > advance)
|
||||
{
|
||||
// Get the amount of space we have leftover.
|
||||
const auto diff = advanceExpected - advance;
|
||||
|
||||
// Set the advance to the perfect width that we want before figuring out if the scale factor doesn't match this run
|
||||
advance = advanceExpected;
|
||||
// Move the X offset (pixels to the right from the left edge) by half the excess space
|
||||
// so half of it will be left of the glyph and the other half on the right.
|
||||
offset.advanceOffset += diff / 2;
|
||||
|
||||
// Set the advance to the perfect width we want.
|
||||
advance = advanceExpected;
|
||||
}
|
||||
// If what we expect is smaller than what we have... rescale the font size to get a smaller glyph to fit.
|
||||
else if (advanceExpected < advance)
|
||||
{
|
||||
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
|
||||
// height proportional to the width that we desire.
|
||||
INT32 advanceInDesignUnits;
|
||||
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices.at(i), &advanceInDesignUnits));
|
||||
|
||||
// When things are drawn, we want the font size (as specified in the base font in the original format)
|
||||
// to be scaled by some factor.
|
||||
// i.e. if the original font size was 16, we might want to draw this glyph with a 15.2 size font so
|
||||
// the width (and height) of the glyph will shrink to fit the monospace cell box.
|
||||
|
||||
// This pattern is copied from the DxRenderer's algorithm for figuring out the font height for a specific width
|
||||
// and was advised by the DirectWrite team.
|
||||
const float widthAdvance = static_cast<float>(advanceInDesignUnits) / metrics.designUnitsPerEm;
|
||||
const auto fontSizeWant = advanceExpected / widthAdvance;
|
||||
run.fontScale = fontSizeWant / _format->GetFontSize();
|
||||
|
||||
// Set the advance to the perfect width that we want.
|
||||
advance = advanceExpected;
|
||||
}
|
||||
}
|
||||
|
||||
// Certain fonts, like Batang, contain glyphs for hidden control
|
||||
// and formatting characters. So we'll want to explicitly force their
|
||||
// advance to zero.
|
||||
// I'm leaving this here for future reference, but I don't think we want invisible glyphs for this renderer.
|
||||
//if (run.script.shapes & DWRITE_SCRIPT_SHAPES_NO_VISUAL)
|
||||
//{
|
||||
// std::fill(_glyphAdvances.begin() + glyphStart,
|
||||
// _glyphAdvances.begin() + glyphStart + actualGlyphCount,
|
||||
// 0.0f
|
||||
// );
|
||||
//}
|
||||
}
|
||||
|
||||
// Certain fonts, like Batang, contain glyphs for hidden control
|
||||
// and formatting characters. So we'll want to explicitly force their
|
||||
// advance to zero.
|
||||
// I'm leaving this here for future reference, but I don't think we want invisible glyphs for this renderer.
|
||||
//if (run.script.shapes & DWRITE_SCRIPT_SHAPES_NO_VISUAL)
|
||||
//{
|
||||
// std::fill(_glyphAdvances.begin() + glyphStart,
|
||||
// _glyphAdvances.begin() + glyphStart + actualGlyphCount,
|
||||
// 0.0f
|
||||
// );
|
||||
//}
|
||||
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
// Routine Description:
|
||||
// - Takes the analyzed and shaped textual information from the layout process and
|
||||
@@ -969,18 +951,6 @@ CATCH_RETURN();
|
||||
return _runs.at(originalRunIndex);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the current run according to the internal
|
||||
// positioning set by Set/Split Current Run methods.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Mutable reference ot the current run.
|
||||
[[nodiscard]] CustomTextLayout::LinkedRun& CustomTextLayout::_GetCurrentRun()
|
||||
{
|
||||
return _runs.at(_runIndex);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Move the current run to the given position.
|
||||
// Since the analyzers generally return results in a forward manner,
|
||||
@@ -1037,126 +1007,5 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
|
||||
frontHalf.textLength = splitPoint;
|
||||
frontHalf.nextRunIndex = gsl::narrow<UINT32>(totalRuns);
|
||||
_runIndex = gsl::narrow<UINT32>(totalRuns);
|
||||
|
||||
// If there is already a glyph mapping in these runs,
|
||||
// we need to correct it for the split as well.
|
||||
// See also (for NxM):
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/993365bc-8689-45ff-a675-c5ed0c011788/dwriteglyphrundescriptionclustermap-explained
|
||||
|
||||
if (frontHalf.glyphCount > 0)
|
||||
{
|
||||
// Starting from this:
|
||||
// TEXT (_text)
|
||||
// f i ñ e
|
||||
// CLUSTERMAP (_glyphClusters)
|
||||
// 0 0 1 3
|
||||
// GLYPH INDICIES (_glyphIndicies)
|
||||
// 19 81 23 72
|
||||
// With _runs length = 1
|
||||
// _runs(0):
|
||||
// - Text Index: 0
|
||||
// - Text Length: 4
|
||||
// - Glyph Index: 0
|
||||
// - Glyph Length: 4
|
||||
//
|
||||
// If we split at text index = 2 (between i and ñ)...
|
||||
// ... then this will be the state after the text splitting above:
|
||||
//
|
||||
// TEXT (_text)
|
||||
// f i ñ e
|
||||
// CLUSTERMAP (_glyphClusters)
|
||||
// 0 0 1 3
|
||||
// GLYPH INDICIES (_glyphIndicies)
|
||||
// 19 81 23 72
|
||||
// With _runs length = 2
|
||||
// _runs(0):
|
||||
// - Text Index: 0
|
||||
// - Text Length: 2
|
||||
// - Glyph Index: 0
|
||||
// - Glyph Length: 4
|
||||
// _runs(1):
|
||||
// - Text Index: 2
|
||||
// - Text Length: 2
|
||||
// - Glyph Index: 0
|
||||
// - Glyph Length: 4
|
||||
//
|
||||
// Notice that the text index/length values are correct,
|
||||
// but we haven't fixed up the glyph index/lengths to match.
|
||||
// We need it to say:
|
||||
// With _runs length = 2
|
||||
// _runs(0):
|
||||
// - Text Index: 0
|
||||
// - Text Length: 2
|
||||
// - Glyph Index: 0
|
||||
// - Glyph Length: 1
|
||||
// _runs(1):
|
||||
// - Text Index: 2
|
||||
// - Text Length: 2
|
||||
// - Glyph Index: 1
|
||||
// - Glyph Length: 3
|
||||
//
|
||||
// Which means that the cluster map value under the beginning
|
||||
// of the right-hand text range is our offset to fix all the values.
|
||||
// In this case, that's 1 corresponding with the ñ.
|
||||
const auto mapOffset = _glyphClusters.at(backHalf.textStart);
|
||||
|
||||
// The front half's glyph start index (position in _glyphIndicies)
|
||||
// stays the same.
|
||||
|
||||
// The front half's glyph count (items in _glyphIndicies to consume)
|
||||
// is the offset value as that's now one past the end of the front half.
|
||||
// (and count is end index + 1)
|
||||
frontHalf.glyphCount = mapOffset;
|
||||
|
||||
// The back half starts at the index that's one past the end of the front
|
||||
backHalf.glyphStart = mapOffset;
|
||||
|
||||
// And the back half count (since it was copied from the front half above)
|
||||
// now just needs to be subtracted by how many we gave the front half.
|
||||
backHalf.glyphCount -= mapOffset;
|
||||
|
||||
// The CLUSTERMAP is also wrong given that it is relative
|
||||
// to each run. And now there are two runs so the map
|
||||
// value under the ñ and e need to updated to be relative
|
||||
// to the text index "2" now instead of the original.
|
||||
//
|
||||
// For the entire range of the back half, we need to walk through and
|
||||
// slide all the glyph mapping values to be relative to the new
|
||||
// backHalf.glyphStart, or adjust it by the offset we just set it to.
|
||||
const auto updateBegin = _glyphClusters.begin() + backHalf.textStart;
|
||||
std::for_each_n(updateBegin, backHalf.textLength, [mapOffset](UINT16& n) {
|
||||
n -= mapOffset;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes the linked runs stored in the state variable _runs
|
||||
// and ensures that their vector/array indexes are in order in which they're drawn.
|
||||
// - This is to be used after splitting and reordering them with the split/select functions
|
||||
// as those manipulate the runs like a linked list (instead of an ordered array)
|
||||
// while splitting to reduce copy overhead and just reorder them when complete with this func.
|
||||
// Arguments:
|
||||
// - <none> - Manipulates _runs variable.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CustomTextLayout::_OrderRuns()
|
||||
{
|
||||
const size_t totalRuns = _runs.size();
|
||||
std::vector<LinkedRun> runs;
|
||||
runs.resize(totalRuns);
|
||||
|
||||
UINT32 nextRunIndex = 0;
|
||||
for (UINT32 i = 0; i < totalRuns; ++i)
|
||||
{
|
||||
runs.at(i) = _runs.at(nextRunIndex);
|
||||
runs.at(i).nextRunIndex = i + 1;
|
||||
nextRunIndex = _runs.at(nextRunIndex).nextRunIndex;
|
||||
}
|
||||
|
||||
runs.back().nextRunIndex = 0;
|
||||
|
||||
_runs.swap(runs);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
@@ -117,10 +117,8 @@ namespace Microsoft::Console::Render
|
||||
};
|
||||
|
||||
[[nodiscard]] LinkedRun& _FetchNextRun(UINT32& textLength);
|
||||
[[nodiscard]] LinkedRun& _GetCurrentRun();
|
||||
void _SetCurrentRun(const UINT32 textPosition);
|
||||
void _SplitCurrentRun(const UINT32 splitPosition);
|
||||
void _OrderRuns();
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeFontFallback(IDWriteTextAnalysisSource* const source, UINT32 textPosition, UINT32 textLength);
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetMappedFont(UINT32 textPosition, UINT32 textLength, IDWriteFont* const font, FLOAT const scale);
|
||||
@@ -166,28 +164,9 @@ namespace Microsoft::Console::Render
|
||||
UINT32 _runIndex;
|
||||
|
||||
// Glyph shaping results
|
||||
|
||||
std::vector<DWRITE_GLYPH_OFFSET> _glyphOffsets;
|
||||
|
||||
// Clusters are complicated. They're in respect to each individual run.
|
||||
// The offsets listed here are in respect to the _text string, but from the beginning index of
|
||||
// each run.
|
||||
// That means if we have two runs, we will see 0 1 2 3 4 0 1 2 3 4 5 6 7... in this clusters count.
|
||||
std::vector<UINT16> _glyphClusters;
|
||||
|
||||
// This appears to be the index of the glyph inside each font.
|
||||
std::vector<UINT16> _glyphIndices;
|
||||
|
||||
std::vector<float> _glyphAdvances;
|
||||
|
||||
// These are used to further break the runs apart and adjust the font size so glyphs fit inside the cells.
|
||||
std::vector<std::tuple<UINT32, float>> _glyphScaleCorrections;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
public:
|
||||
CustomTextLayout() = default;
|
||||
|
||||
friend class CustomTextLayoutTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -272,13 +272,6 @@ using namespace Microsoft::Console::Render;
|
||||
|
||||
rect.right = std::accumulate(advancesSpan.cbegin(), advancesSpan.cend(), rect.right);
|
||||
|
||||
// Clip all drawing in this glyph run to where we expect.
|
||||
d2dContext->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
// Ensure we pop it on the way out
|
||||
auto popclip = wil::scope_exit([&d2dContext]() noexcept {
|
||||
d2dContext->PopAxisAlignedClip();
|
||||
});
|
||||
|
||||
d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);
|
||||
|
||||
// Now go onto drawing the text.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
using namespace DirectX;
|
||||
|
||||
std::atomic<size_t> Microsoft::Console::Render::DxEngine::_tracelogCount{ 0 };
|
||||
#pragma warning(suppress : 26477) // We don't control tracelogging macros
|
||||
#pragma warning(suppress : 26477)
|
||||
TRACELOGGING_DEFINE_PROVIDER(g_hDxRenderProvider,
|
||||
"Microsoft.Windows.Terminal.Renderer.DirectX",
|
||||
// {c93e739e-ae50-5a14-78e7-f171e947535d}
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
DIRS= \
|
||||
lib \
|
||||
ut_dx \
|
||||
lib
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
#include "..\..\inc\consoletaeftemplates.hpp"
|
||||
|
||||
#include "..\CustomTextLayout.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
class Microsoft::Console::Render::CustomTextLayoutTests
|
||||
{
|
||||
TEST_CLASS(CustomTextLayoutTests);
|
||||
|
||||
TEST_METHOD(OrderRuns)
|
||||
{
|
||||
CustomTextLayout layout;
|
||||
|
||||
// Create linked list runs where a --> c --> b
|
||||
CustomTextLayout::LinkedRun a;
|
||||
a.nextRunIndex = 2;
|
||||
a.textStart = 0;
|
||||
CustomTextLayout::LinkedRun b;
|
||||
b.nextRunIndex = 0;
|
||||
b.textStart = 20;
|
||||
CustomTextLayout::LinkedRun c;
|
||||
c.nextRunIndex = 1;
|
||||
c.textStart = 10;
|
||||
|
||||
// but insert them into the runs as a, b, c
|
||||
layout._runs.push_back(a);
|
||||
layout._runs.push_back(b);
|
||||
layout._runs.push_back(c);
|
||||
|
||||
// Now order them.
|
||||
layout._OrderRuns();
|
||||
|
||||
// Validate that they've been reordered to a, c, b by index so they can be iterated to go in order.
|
||||
|
||||
// The text starts should be in order 0, 10, 20.
|
||||
// The next run indexes should point at each other.
|
||||
VERIFY_ARE_EQUAL(a.textStart, layout._runs.at(0).textStart);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(0).nextRunIndex);
|
||||
VERIFY_ARE_EQUAL(c.textStart, layout._runs.at(1).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).nextRunIndex);
|
||||
VERIFY_ARE_EQUAL(b.textStart, layout._runs.at(2).textStart);
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(2).nextRunIndex);
|
||||
}
|
||||
|
||||
TEST_METHOD(SplitCurrentRunIncludingGlyphs)
|
||||
{
|
||||
CustomTextLayout layout;
|
||||
|
||||
// Put glyph data into the layout as if we've already gone through analysis.
|
||||
// This data matches the verbose comment from the CustomTextLayout.cpp file
|
||||
// and is derived from
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/993365bc-8689-45ff-a675-c5ed0c011788/dwriteglyphrundescriptionclustermap-explained
|
||||
|
||||
layout._text = L"fiñe";
|
||||
|
||||
layout._glyphIndices.push_back(19);
|
||||
layout._glyphIndices.push_back(81);
|
||||
layout._glyphIndices.push_back(23);
|
||||
layout._glyphIndices.push_back(72);
|
||||
|
||||
layout._glyphClusters.push_back(0);
|
||||
layout._glyphClusters.push_back(0);
|
||||
layout._glyphClusters.push_back(1);
|
||||
layout._glyphClusters.push_back(3);
|
||||
|
||||
// Set up the layout to have a run that already has glyph data inside of it.
|
||||
CustomTextLayout::LinkedRun run;
|
||||
run.textStart = 0;
|
||||
run.textLength = 4;
|
||||
run.glyphStart = 0;
|
||||
run.glyphCount = 4;
|
||||
|
||||
layout._runs.push_back(run);
|
||||
|
||||
// Now split it in the middle per the comment example
|
||||
layout._SetCurrentRun(2);
|
||||
layout._SplitCurrentRun(2);
|
||||
|
||||
// And validate that the split state matches what we expected.
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(0).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(0).textLength);
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(0).glyphStart);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(0).glyphCount);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).textLength);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(1).glyphStart);
|
||||
VERIFY_ARE_EQUAL(3u, layout._runs.at(1).glyphCount);
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
//Autogenerated file name + version resource file for Device Guard whitelisting effort
|
||||
|
||||
#include <windows.h>
|
||||
#include <ntverp.h>
|
||||
|
||||
#define VER_FILETYPE VFT_UNKNOWN
|
||||
#define VER_FILESUBTYPE VFT2_UNKNOWN
|
||||
#define VER_FILEDESCRIPTION_STR ___TARGETNAME
|
||||
#define VER_INTERNALNAME_STR ___TARGETNAME
|
||||
#define VER_ORIGINALFILENAME_STR ___TARGETNAME
|
||||
|
||||
#include "common.ver"
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{95B136F9-B238-490C-A7C5-5843C1FECAC4}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>DxUnitTests</RootNamespace>
|
||||
<ProjectName>Dx.Unit.Tests</ProjectName>
|
||||
<TargetName>Dx.Unit.Tests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CustomTextLayoutTests.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\types\lib\types.vcxproj">
|
||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\dx.vcxproj">
|
||||
<Project>{48D21369-3D7B-4431-9967-24E81292CF62}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;$(SolutionDir)src\inc;$(SolutionDir)src\inc\test;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="ProductBuild" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(NTMAKEENV)\UniversalTest\Microsoft.TestInfrastructure.UniversalTest.props" />
|
||||
</Project>
|
||||
@@ -1,34 +0,0 @@
|
||||
!include ..\..\project.unittest.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Program Information
|
||||
# -------------------------------------
|
||||
|
||||
TARGETNAME = Microsoft.Console.Renderer.Dx.UnitTests
|
||||
TARGETTYPE = DYNLINK
|
||||
DLLDEF =
|
||||
|
||||
# -------------------------------------
|
||||
# Sources, Headers, and Libraries
|
||||
# -------------------------------------
|
||||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
CustomTextLayoutTests.cpp \
|
||||
DefaultResource.rc \
|
||||
|
||||
INCLUDES = \
|
||||
.. \
|
||||
$(INCLUDES) \
|
||||
|
||||
TARGETLIBS = \
|
||||
$(WINCORE_OBJ_PATH)\console\open\src\renderer\dx\lib\$(O)\ConRenderDx.lib \
|
||||
$(TARGETLIBS) \
|
||||
|
||||
# -------------------------------------
|
||||
# Localization
|
||||
# -------------------------------------
|
||||
|
||||
# Autogenerated. Sets file name for Device Guard whitelisting effort, used in RC.exe.
|
||||
C_DEFINES = $(C_DEFINES) -D___TARGETNAME="""$(TARGETNAME).$(TARGETTYPE)"""
|
||||
MUI_VERIFY_NO_LOC_RESOURCE = 1
|
||||
@@ -73,7 +73,7 @@ void RenderTracing::TraceString(const std::string_view& instr) const
|
||||
const char* const seq = _seq.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceString",
|
||||
TraceLoggingUtf8String(seq),
|
||||
TraceLoggingString(seq),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user