mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 06:09:50 +00:00
Compare commits
17 Commits
dev/duhowe
...
v1.2.2234.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dac5aa280 | ||
|
|
c235ab38fb | ||
|
|
61234f6585 | ||
|
|
9d75011f9c | ||
|
|
9ecd8f574f | ||
|
|
5d7455795c | ||
|
|
4fe9165788 | ||
|
|
f4b97fe37d | ||
|
|
fc83a4cbeb | ||
|
|
39a1ce0539 | ||
|
|
139414723b | ||
|
|
288436a2e9 | ||
|
|
6084670f7b | ||
|
|
ac99c3065c | ||
|
|
4d00019a9b | ||
|
|
d48ecd24f4 | ||
|
|
ccf201fe44 |
10
.github/actions/spell-check/expect/af1ff90dc512c83c902762b02f284c1c61603b4a.txt
vendored
Normal file
10
.github/actions/spell-check/expect/af1ff90dc512c83c902762b02f284c1c61603b4a.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
abcd
|
||||
dst
|
||||
EFG
|
||||
EFGh
|
||||
EMPTYBOX
|
||||
GFEh
|
||||
nrcs
|
||||
Remoting
|
||||
Scs
|
||||
Shobjidl
|
||||
33
.github/actions/spell-check/expect/expect.txt
vendored
33
.github/actions/spell-check/expect/expect.txt
vendored
@@ -99,7 +99,6 @@ AStomps
|
||||
ASYNCWINDOWPOS
|
||||
atch
|
||||
ATest
|
||||
atg
|
||||
attr
|
||||
ATTRCOLOR
|
||||
aumid
|
||||
@@ -160,7 +159,6 @@ bitsavers
|
||||
bitset
|
||||
BKCOLOR
|
||||
BKGND
|
||||
BKMK
|
||||
Bksp
|
||||
blog
|
||||
Blt
|
||||
@@ -295,7 +293,6 @@ codepage
|
||||
codepoint
|
||||
codeproject
|
||||
COINIT
|
||||
colo
|
||||
colorizing
|
||||
colororacle
|
||||
colorref
|
||||
@@ -352,7 +349,6 @@ conpty
|
||||
conptylib
|
||||
consecteturadipiscingelit
|
||||
conserv
|
||||
consoleaccessibility
|
||||
consoleapi
|
||||
CONSOLECONTROL
|
||||
CONSOLEENDTASK
|
||||
@@ -640,7 +636,6 @@ DROPDOWNLIST
|
||||
DROPFILES
|
||||
drv
|
||||
dsm
|
||||
Dst
|
||||
DSwap
|
||||
DTest
|
||||
dtor
|
||||
@@ -683,7 +678,6 @@ Elems
|
||||
elif
|
||||
elseif
|
||||
emacs
|
||||
emptybox
|
||||
enabledelayedexpansion
|
||||
endian
|
||||
endif
|
||||
@@ -752,7 +746,6 @@ fdw
|
||||
fea
|
||||
fesb
|
||||
FFDE
|
||||
FFF
|
||||
FFrom
|
||||
FGCOLOR
|
||||
fgetc
|
||||
@@ -769,7 +762,6 @@ FILETYPE
|
||||
FILEW
|
||||
FILLATTR
|
||||
FILLCONSOLEOUTPUT
|
||||
Filledbox
|
||||
FILTERONPASTE
|
||||
finalizer
|
||||
FINDCASE
|
||||
@@ -821,7 +813,6 @@ fsproj
|
||||
fstream
|
||||
fte
|
||||
Ftm
|
||||
fullcolor
|
||||
fullscreen
|
||||
fullwidth
|
||||
func
|
||||
@@ -1011,7 +1002,6 @@ hwheel
|
||||
hwnd
|
||||
HWNDPARENT
|
||||
hxx
|
||||
hyperlink
|
||||
IAccessibility
|
||||
IAction
|
||||
IApi
|
||||
@@ -1034,7 +1024,6 @@ ICore
|
||||
IData
|
||||
IDCANCEL
|
||||
IDD
|
||||
IDefault
|
||||
IDesktop
|
||||
IDictionary
|
||||
IDISHWND
|
||||
@@ -1384,7 +1373,6 @@ mincore
|
||||
mindbogglingly
|
||||
mingw
|
||||
minkernel
|
||||
minmax
|
||||
minwin
|
||||
minwindef
|
||||
Mip
|
||||
@@ -1545,7 +1533,6 @@ NOYIELD
|
||||
NOZORDER
|
||||
NPM
|
||||
npos
|
||||
NRCS
|
||||
NSTATUS
|
||||
ntapi
|
||||
ntcon
|
||||
@@ -1722,7 +1709,6 @@ pguid
|
||||
pgup
|
||||
PHANDLE
|
||||
phhook
|
||||
phsl
|
||||
phwnd
|
||||
pid
|
||||
pidl
|
||||
@@ -1752,7 +1738,6 @@ POBJECT
|
||||
Podcast
|
||||
POINTSLIST
|
||||
POLYTEXTW
|
||||
popclip
|
||||
popd
|
||||
POPF
|
||||
poppack
|
||||
@@ -1935,7 +1920,6 @@ REGSTR
|
||||
reingest
|
||||
Relayout
|
||||
RELBINPATH
|
||||
remoting
|
||||
renderengine
|
||||
rendersize
|
||||
reparent
|
||||
@@ -1943,7 +1927,6 @@ reparenting
|
||||
replatformed
|
||||
Replymessage
|
||||
repositorypath
|
||||
rerendered
|
||||
rescap
|
||||
Resequence
|
||||
Reserialize
|
||||
@@ -1989,7 +1972,6 @@ RMENU
|
||||
roadmap
|
||||
robomac
|
||||
roundtrip
|
||||
ROWSTOSCROLL
|
||||
rparen
|
||||
RRF
|
||||
RRRGGGBB
|
||||
@@ -2051,7 +2033,6 @@ SCROLLSCALE
|
||||
SCROLLSCREENBUFFER
|
||||
Scrollup
|
||||
Scrolluppage
|
||||
SCS
|
||||
scursor
|
||||
sddl
|
||||
sdeleted
|
||||
@@ -2121,7 +2102,6 @@ Shl
|
||||
shlguid
|
||||
shlobj
|
||||
shlwapi
|
||||
shobjidl
|
||||
SHORTPATH
|
||||
SHOWCURSOR
|
||||
SHOWMAXIMIZED
|
||||
@@ -2194,14 +2174,11 @@ STARTWPARMSW
|
||||
Statusline
|
||||
stdafx
|
||||
STDAPI
|
||||
stdarg
|
||||
stdcall
|
||||
stddef
|
||||
stderr
|
||||
stdexcept
|
||||
stdin
|
||||
stdio
|
||||
stdlib
|
||||
STDMETHODCALLTYPE
|
||||
STDMETHODIMP
|
||||
stdout
|
||||
@@ -2295,7 +2272,6 @@ telnetd
|
||||
telnetpp
|
||||
templated
|
||||
terminalcore
|
||||
terminalnuget
|
||||
TERMINALSCROLLING
|
||||
terminfo
|
||||
TEs
|
||||
@@ -2351,7 +2327,6 @@ TMult
|
||||
tmultiple
|
||||
tmux
|
||||
todo
|
||||
Tofill
|
||||
tofrom
|
||||
tokenhelpers
|
||||
tokenized
|
||||
@@ -2447,7 +2422,6 @@ undef
|
||||
Unescape
|
||||
unexpand
|
||||
Unfocus
|
||||
unfocuses
|
||||
unhighlighting
|
||||
unhosted
|
||||
unicode
|
||||
@@ -2753,33 +2727,26 @@ WWith
|
||||
wx
|
||||
wxh
|
||||
xa
|
||||
xab
|
||||
xact
|
||||
xamarin
|
||||
xaml
|
||||
Xamlmeta
|
||||
xargs
|
||||
xaz
|
||||
xb
|
||||
xbc
|
||||
xbf
|
||||
xbutton
|
||||
XBUTTONDBLCLK
|
||||
XBUTTONDOWN
|
||||
XBUTTONUP
|
||||
xca
|
||||
XCast
|
||||
xce
|
||||
XCENTER
|
||||
XColors
|
||||
xcopy
|
||||
XCount
|
||||
xdd
|
||||
xdy
|
||||
xe
|
||||
XEncoding
|
||||
xff
|
||||
xffff
|
||||
XFile
|
||||
xlang
|
||||
XManifest
|
||||
|
||||
@@ -16,12 +16,35 @@ using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
static constexpr bool _IsMouseMessage(UINT uMsg)
|
||||
{
|
||||
return uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK ||
|
||||
uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP || uMsg == WM_MBUTTONDBLCLK ||
|
||||
uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_RBUTTONDBLCLK ||
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL;
|
||||
}
|
||||
|
||||
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
||||
// See microsoft/terminal#2066 for more info.
|
||||
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */) noexcept
|
||||
{
|
||||
return false; // glyph is not wide.
|
||||
}
|
||||
|
||||
static bool _EnsureStaticInitialization()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static bool initialized = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback);
|
||||
|
||||
return true;
|
||||
}();
|
||||
return initialized;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
|
||||
@@ -36,10 +59,29 @@ try
|
||||
|
||||
if (terminal)
|
||||
{
|
||||
if (_IsMouseMessage(uMsg) && terminal->_CanSendVTMouseInput())
|
||||
if (_IsMouseMessage(uMsg))
|
||||
{
|
||||
if (terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
if (terminal->_CanSendVTMouseInput() && terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// _SendMouseEvent returns false if the terminal's not in VT mode, so we'll
|
||||
// fall through to release the capture.
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(hwnd);
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
}
|
||||
|
||||
// Suppress all mouse events that made it into the terminal.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -57,6 +99,10 @@ try
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
terminal->_singleClickTouchdownPos = std::nullopt;
|
||||
[[fallthrough]];
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
if (WI_IsFlagSet(wParam, MK_LBUTTON))
|
||||
@@ -72,7 +118,7 @@ try
|
||||
{
|
||||
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
|
||||
terminal->_terminal->ClearSelection();
|
||||
TerminalClearSelection(terminal);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -81,6 +127,11 @@ try
|
||||
terminal->_PasteTextFromClipboard();
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
|
||||
terminal->_hwnd.release();
|
||||
terminal->Teardown();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
@@ -114,14 +165,16 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
|
||||
}
|
||||
|
||||
HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8, false },
|
||||
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr },
|
||||
_multiClickTime{ 500 } // this will be overwritten by the windows system double-click time
|
||||
{
|
||||
_EnsureStaticInitialization();
|
||||
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
if (RegisterTermClass(hInstance))
|
||||
@@ -148,6 +201,11 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
}
|
||||
}
|
||||
|
||||
HwndTerminal::~HwndTerminal()
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Initialize()
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
@@ -162,9 +220,6 @@ HRESULT HwndTerminal::Initialize()
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
const auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
|
||||
SetGlyphWidthFallback(pfn);
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
GetWindowRect(_hwnd.get(), &windowRect);
|
||||
@@ -181,8 +236,8 @@ HRESULT HwndTerminal::Initialize()
|
||||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(5, 27, 80));
|
||||
_terminal->SetDefaultForeground(RGB(255, 255, 255));
|
||||
_terminal->SetDefaultBackground(RGB(12, 12, 12));
|
||||
_terminal->SetDefaultForeground(RGB(204, 204, 204));
|
||||
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
@@ -191,6 +246,33 @@ HRESULT HwndTerminal::Initialize()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::Teardown() noexcept
|
||||
try
|
||||
{
|
||||
// As a rule, detach resources from the Terminal before shutting them down.
|
||||
// This ensures that teardown is reentrant.
|
||||
|
||||
// Shut down the renderer (and therefore the thread) before we implode
|
||||
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
||||
{
|
||||
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
|
||||
{
|
||||
localRenderer->TriggerTeardown();
|
||||
// renderer is destroyed
|
||||
}
|
||||
// renderEngine is destroyed
|
||||
}
|
||||
|
||||
if (auto localHwnd{ _hwnd.release() })
|
||||
{
|
||||
// If we're being called through WM_DESTROY, we won't get here (hwnd is already released)
|
||||
// If we're not, we may end up in Teardown _again_... but by the time we do, all other
|
||||
// resources have been released and will not be released again.
|
||||
DestroyWindow(localHwnd);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> callback)
|
||||
{
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
@@ -467,11 +549,21 @@ try
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void HwndTerminal::_ClearSelection() noexcept
|
||||
try
|
||||
{
|
||||
auto lock{ _terminal->LockForWriting() };
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->ClearSelection();
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_ClearSelection();
|
||||
}
|
||||
|
||||
bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
@@ -482,9 +574,10 @@ bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
// Returns the selected text in the terminal.
|
||||
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
publicTerminal->_ClearSelection();
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring selectedText;
|
||||
@@ -494,8 +587,6 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
}
|
||||
|
||||
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
|
||||
TerminalClearSelection(terminal);
|
||||
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
@@ -541,19 +632,30 @@ bool HwndTerminal::_CanSendVTMouseInput() const noexcept
|
||||
bool HwndTerminal::_SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
|
||||
try
|
||||
{
|
||||
const til::point cursorPosition{
|
||||
til::point cursorPosition{
|
||||
GET_X_LPARAM(lParam),
|
||||
GET_Y_LPARAM(lParam),
|
||||
};
|
||||
|
||||
const til::size fontSize{ this->_actualFont.GetSize() };
|
||||
short wheelDelta{ 0 };
|
||||
if (uMsg == WM_MOUSEWHEEL)
|
||||
if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL)
|
||||
{
|
||||
wheelDelta = HIWORD(wParam);
|
||||
|
||||
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
|
||||
POINT coordsToTransform = cursorPosition;
|
||||
ScreenToClient(_hwnd.get(), &coordsToTransform);
|
||||
cursorPosition = coordsToTransform;
|
||||
}
|
||||
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta);
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -561,20 +663,24 @@ catch (...)
|
||||
return false;
|
||||
}
|
||||
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept
|
||||
try
|
||||
{
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode) noexcept
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept
|
||||
try
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_ClearSelection();
|
||||
if (ch == UNICODE_ESC)
|
||||
{
|
||||
// ESC should clear any selection before it triggers input.
|
||||
@@ -589,21 +695,25 @@ try
|
||||
return;
|
||||
}
|
||||
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendCharEvent(ch, scanCode, flags);
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown)
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, keyDown);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode)
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode, flags);
|
||||
}
|
||||
|
||||
void _stdcall DestroyTerminal(void* terminal)
|
||||
@@ -632,7 +742,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(theme.CursorStyle);
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, 10, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
// When the font changes the terminal dimensions need to be recalculated since the available row and column
|
||||
|
||||
@@ -34,8 +34,8 @@ __declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD flags, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
__declspec(dllexport) void _stdcall TerminalSetFocus(void* terminal);
|
||||
@@ -51,9 +51,10 @@ public:
|
||||
HwndTerminal(HwndTerminal&&) = default;
|
||||
HwndTerminal& operator=(const HwndTerminal&) = default;
|
||||
HwndTerminal& operator=(HwndTerminal&&) = default;
|
||||
~HwndTerminal() = default;
|
||||
~HwndTerminal();
|
||||
|
||||
HRESULT Initialize();
|
||||
void Teardown() noexcept;
|
||||
void SendOutput(std::wstring_view data);
|
||||
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
|
||||
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
|
||||
@@ -92,8 +93,8 @@ private:
|
||||
friend void _stdcall TerminalClearSelection(void* terminal);
|
||||
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags);
|
||||
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);
|
||||
@@ -112,11 +113,13 @@ private:
|
||||
HRESULT _MoveSelection(LPARAM lParam) noexcept;
|
||||
IRawElementProviderSimple* _GetUiaProvider() noexcept;
|
||||
|
||||
void _ClearSelection() noexcept;
|
||||
|
||||
bool _CanSendVTMouseInput() const noexcept;
|
||||
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode) noexcept;
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
|
||||
|
||||
// Inherited via IControlAccessibilityInfo
|
||||
COORD GetFontSize() const override;
|
||||
|
||||
@@ -226,9 +226,12 @@ void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
void CascadiaSettings::_ResolveDefaultProfile()
|
||||
{
|
||||
const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() };
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
if (unparsedDefaultProfile)
|
||||
{
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(*unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -61,7 +61,7 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// GH 3588, we need this below to know if the user chose something that wasn't our default.
|
||||
// Collect it up here in case it gets modified by any of the other layers between now and when
|
||||
// the user's preferences are loaded and layered.
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().UnparsedDefaultProfile();
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
@@ -141,12 +141,11 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// is a lot of computation we can skip if no one cares.
|
||||
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
|
||||
{
|
||||
const auto hardcodedDefaultGuidAsGuid = Utils::GuidFromString(hardcodedDefaultGuid);
|
||||
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
// Compare to the defaults.json one that we set on install.
|
||||
// If it's different, log what the user chose.
|
||||
if (hardcodedDefaultGuidAsGuid != guid)
|
||||
if (hardcodedDefaultGuid != guid)
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
@@ -229,6 +228,7 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadDefaults()
|
||||
// them from a file (and the potential that could fail)
|
||||
resultPtr->_ParseJsonString(DefaultJson, true);
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
resultPtr->_ResolveDefaultProfile();
|
||||
|
||||
return resultPtr;
|
||||
}
|
||||
|
||||
@@ -187,14 +187,16 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
const auto actionAndArgs = command.Action();
|
||||
_dispatch.DoAction(actionAndArgs);
|
||||
_close();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"CommandPaletteDispatchedAction",
|
||||
TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"),
|
||||
TraceLoggingUInt32(_searchBox().Text().size(), "SearchTextLength", "Number of characters in the search string"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
_close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,9 @@ GUID GlobalAppSettings::DefaultProfile() const
|
||||
return _defaultProfile;
|
||||
}
|
||||
|
||||
std::wstring GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
std::optional<std::wstring> GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
{
|
||||
return _unparsedDefaultProfile.value();
|
||||
return _unparsedDefaultProfile;
|
||||
}
|
||||
|
||||
AppKeyBindings GlobalAppSettings::GetKeybindings() const noexcept
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
// by higher layers in the app.
|
||||
void DefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID DefaultProfile() const;
|
||||
std::wstring UnparsedDefaultProfile() const;
|
||||
std::optional<std::wstring> UnparsedDefaultProfile() const;
|
||||
|
||||
GETSET_PROPERTY(int32_t, InitialRows); // default value set in constructor
|
||||
GETSET_PROPERTY(int32_t, InitialCols); // default value set in constructor
|
||||
|
||||
@@ -166,7 +166,28 @@ namespace winrt::TerminalApp::implementation
|
||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings->GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
page->_SplitPane(TerminalApp::SplitState::Automatic,
|
||||
TerminalApp::SplitType::Manual,
|
||||
nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
|
||||
@@ -1778,7 +1799,7 @@ namespace winrt::TerminalApp::implementation
|
||||
tab->SetFocused(true);
|
||||
|
||||
// Raise an event that our title changed
|
||||
_titleChangeHandlers(*this, Title());
|
||||
_titleChangeHandlers(*this, tab->GetActiveTitle());
|
||||
|
||||
// Raise an event that our titlebar color changed
|
||||
std::optional<Windows::UI::Color> color = tab->GetTabColor();
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
"foreground": "#839496",
|
||||
"background": "#002B36",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#073642",
|
||||
"black": "#002B36",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -195,7 +195,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#002B36",
|
||||
"brightBlack": "#073642",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -209,7 +209,7 @@
|
||||
"foreground": "#657B83",
|
||||
"background": "#FDF6E3",
|
||||
"cursorColor": "#002B36",
|
||||
"black": "#073642",
|
||||
"black": "#002B36",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -217,7 +217,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#002B36",
|
||||
"brightBlack": "#073642",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
|
||||
interface IMouseWheelListener
|
||||
{
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta);
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "TermControlAutomationPeer.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Console::VirtualTerminal;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
@@ -737,7 +738,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
const auto ch = e.Character();
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
e.Handled(handled);
|
||||
}
|
||||
@@ -826,9 +831,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases
|
||||
// Alt, so we should be ignoring the individual keydowns. The character
|
||||
@@ -1001,7 +1010,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta);
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta, state);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1325,10 +1335,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
const auto props = point.Properties();
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
auto result = _DoMouseWheel(point.Position(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
point.Properties().MouseWheelDelta(),
|
||||
point.Properties().IsLeftButtonPressed());
|
||||
state);
|
||||
if (result)
|
||||
{
|
||||
args.Handled(true);
|
||||
@@ -1351,7 +1363,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
bool TermControl::_DoMouseWheel(const Windows::Foundation::Point point,
|
||||
const ControlKeyStates modifiers,
|
||||
const int32_t delta,
|
||||
const bool isLeftButtonPressed)
|
||||
const TerminalInput::MouseButtonState state)
|
||||
{
|
||||
if (_CanSendVTMouseInput())
|
||||
{
|
||||
@@ -1363,7 +1375,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return _terminal->SendMouseEvent(_GetTerminalPosition(point),
|
||||
WM_MOUSEWHEEL,
|
||||
_GetPressedModifierKeys(),
|
||||
::base::saturated_cast<short>(delta));
|
||||
::base::saturated_cast<short>(delta),
|
||||
state);
|
||||
}
|
||||
|
||||
const auto ctrlPressed = modifiers.IsCtrlPressed();
|
||||
@@ -1379,7 +1392,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MouseScrollHandler(delta, point, isLeftButtonPressed);
|
||||
_MouseScrollHandler(delta, point, state.isLeftButtonDown);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1393,11 +1406,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - location: the location of the mouse during this event. This location is
|
||||
// relative to the origin of the control
|
||||
// - delta: the mouse wheel delta that triggered this event.
|
||||
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
|
||||
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
|
||||
const int32_t delta)
|
||||
const int32_t delta,
|
||||
const bool leftButtonDown,
|
||||
const bool midButtonDown,
|
||||
const bool rightButtonDown)
|
||||
{
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
return _DoMouseWheel(location, modifiers, delta, false);
|
||||
TerminalInput::MouseButtonState state{ leftButtonDown, midButtonDown, rightButtonDown };
|
||||
return _DoMouseWheel(location, modifiers, delta, state);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
#include "SearchBoxControl.h"
|
||||
#include "ThrottledFunc.h"
|
||||
|
||||
namespace Microsoft::Console::VirtualTerminal
|
||||
{
|
||||
struct MouseButtonState;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
struct CopyToClipboardEventArgs :
|
||||
@@ -89,7 +94,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta);
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
|
||||
|
||||
~TermControl();
|
||||
|
||||
@@ -221,7 +226,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _MouseScrollHandler(const double mouseDelta, const Windows::Foundation::Point point, const bool isLeftButtonPressed);
|
||||
void _MouseZoomHandler(const double delta);
|
||||
void _MouseTransparencyHandler(const double delta);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const bool isLeftButtonPressed);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Microsoft::Terminal::Core
|
||||
virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept = 0;
|
||||
virtual bool SetCursorColor(const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetDefaultForeground(const DWORD color) noexcept = 0;
|
||||
virtual bool SetDefaultBackground(const DWORD color) noexcept = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Microsoft::Terminal::Core
|
||||
ITerminalInput& operator=(ITerminalInput&&) = default;
|
||||
|
||||
virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0;
|
||||
virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0;
|
||||
|
||||
// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
|
||||
|
||||
@@ -430,6 +430,19 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
|
||||
_StoreKeyEvent(vkey, scanCode);
|
||||
|
||||
// As a Terminal we're mostly interested in getting key events from physical hardware (mouse & keyboard).
|
||||
// We're thus ignoring events whose values are outside the valid range and unlikely to be generated by the current keyboard.
|
||||
// It's very likely that a proper followup character event will be sent to us.
|
||||
// This prominently happens using AutoHotKey's keyboard remapping feature,
|
||||
// which sends input events whose vkey is 0xff and scanCode is 0.
|
||||
// We need to check for this early, as _CharacterFromKeyEvent() always returns 0 for such invalid values,
|
||||
// making us believe that this is an actual non-character input, while it usually isn't.
|
||||
// GH#7064
|
||||
if (vkey == 0 || vkey >= 0xff || scanCode == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed();
|
||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed();
|
||||
|
||||
@@ -484,16 +497,16 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
// Return Value:
|
||||
// - true if we translated the key event, and it should not be processed any further.
|
||||
// - false if we did not translate the key, and it should be processed into a character.
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta)
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state)
|
||||
{
|
||||
// viewportPos must be within the dimensions of the viewport
|
||||
const auto viewportDimensions = _mutableViewport.Dimensions();
|
||||
if (viewportPos.X < 0 || viewportPos.X >= viewportDimensions.X || viewportPos.Y < 0 || viewportPos.Y >= viewportDimensions.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _terminalInput->HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta);
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
#pragma warning(suppress : 26496) // analysis can't tell we're assigning through a reference below
|
||||
auto clampedPos{ viewportPos };
|
||||
_mutableViewport.ToOrigin().Clamp(clampedPos);
|
||||
return _terminalInput->HandleMouse(clampedPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
bool SetWindowTitle(std::wstring_view title) noexcept override;
|
||||
bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const COLORREF color) noexcept override;
|
||||
bool SetDefaultForeground(const COLORREF color) noexcept override;
|
||||
bool SetDefaultBackground(const COLORREF color) noexcept override;
|
||||
|
||||
@@ -115,7 +116,7 @@ public:
|
||||
#pragma region ITerminalInput
|
||||
// These methods are defined in Terminal.cpp
|
||||
bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
|
||||
bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
|
||||
|
||||
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
|
||||
|
||||
@@ -64,6 +64,14 @@ COORD Terminal::GetCursorPosition() noexcept
|
||||
return newPos;
|
||||
}
|
||||
|
||||
bool Terminal::SetCursorColor(const COLORREF color) noexcept
|
||||
try
|
||||
{
|
||||
_buffer->GetCursor().SetColor(color);
|
||||
return true;
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
// Method Description:
|
||||
// - Moves the cursor down one line, and possibly also to the leftmost column.
|
||||
// Arguments:
|
||||
|
||||
@@ -146,6 +146,13 @@ try
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetCursorColor(const DWORD color) noexcept
|
||||
try
|
||||
{
|
||||
return _terminalApi.SetCursorColor(color);
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept
|
||||
try
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
|
||||
bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const DWORD color) noexcept override;
|
||||
|
||||
bool SetClipboard(std::wstring_view content) noexcept override;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace TerminalCoreUnitTests
|
||||
|
||||
TEST_METHOD(AltShiftKey);
|
||||
TEST_METHOD(AltSpace);
|
||||
TEST_METHOD(InvalidKeyEvent);
|
||||
|
||||
void _VerifyExpectedInput(std::wstring& actualInput)
|
||||
{
|
||||
@@ -65,4 +66,14 @@ namespace TerminalCoreUnitTests
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', 0, ControlKeyStates::LeftAltPressed, false));
|
||||
VERIFY_IS_FALSE(term.SendCharEvent(L' ', 0, ControlKeyStates::LeftAltPressed));
|
||||
}
|
||||
|
||||
void InputTest::InvalidKeyEvent()
|
||||
{
|
||||
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
||||
// send us key events using SendInput() whose values are outside of the valid range.
|
||||
// We don't want to handle those events as we're probably going to get a proper followup character event.
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(123, 0, {}, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
AppHost::AppHost() noexcept :
|
||||
_app{},
|
||||
_logic{ nullptr }, // don't make one, we're going to take a ref on app's
|
||||
@@ -405,7 +409,11 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
|
||||
|
||||
const til::point offsetPoint = coord - controlOrigin;
|
||||
|
||||
if (control.OnMouseWheel(offsetPoint, delta))
|
||||
const auto lButtonDown = WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed);
|
||||
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
|
||||
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
|
||||
|
||||
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
|
||||
{
|
||||
// If the element handled the mouse wheel event, don't
|
||||
// continue to iterate over the remaining controls.
|
||||
|
||||
@@ -62,6 +62,16 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
WM_CHAR = 0x0102,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is pressed. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 0x0104,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is released. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 0x0105,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
/// </summary>
|
||||
@@ -215,10 +225,10 @@ namespace Microsoft.Terminal.Wpf
|
||||
public static extern void DestroyTerminal(IntPtr terminal);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, bool keyDown);
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, ushort flags, bool keyDown);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode);
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode, ushort flags);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSetTheme(IntPtr terminal, [MarshalAs(UnmanagedType.Struct)] TerminalTheme theme, string fontFamily, short fontSize, int newDpi);
|
||||
|
||||
@@ -20,6 +20,20 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </remarks>
|
||||
public class TerminalContainer : HwndHost
|
||||
{
|
||||
private static void UnpackKeyMessage(IntPtr wParam, IntPtr lParam, out ushort vkey, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
ulong scanCodeAndFlags = (((ulong)lParam) & 0xFFFF0000) >> 16;
|
||||
scanCode = (ushort)(scanCodeAndFlags & 0x00FFu);
|
||||
flags = (ushort)(scanCodeAndFlags & 0xFF00u);
|
||||
vkey = (ushort)wParam;
|
||||
}
|
||||
|
||||
private static void UnpackCharMessage(IntPtr wParam, IntPtr lParam, out char character, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vKey, out scanCode, out flags);
|
||||
character = (char)vKey;
|
||||
}
|
||||
|
||||
private ITerminalConnection connection;
|
||||
private IntPtr hwnd;
|
||||
private IntPtr terminal;
|
||||
@@ -124,6 +138,20 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.TriggerResize(this.RenderSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text from the terminal renderer and clears the selection.
|
||||
/// </summary>
|
||||
/// <returns>The selected text, empty if no text is selected.</returns>
|
||||
internal string GetSelectedText()
|
||||
{
|
||||
if (NativeMethods.TerminalIsSelectionActive(this.terminal))
|
||||
{
|
||||
return NativeMethods.TerminalGetSelection(this.terminal);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a refresh of the terminal with the given size.
|
||||
/// </summary>
|
||||
@@ -235,29 +263,35 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.Focus();
|
||||
NativeMethods.SetFocus(this.hwnd);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYDOWN: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYDOWN:
|
||||
{
|
||||
// WM_KEYDOWN lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
|
||||
NativeMethods.TerminalSetCursorVisible(this.terminal, true);
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, true);
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, vkey, scanCode, flags, true);
|
||||
this.blinkTimer?.Start();
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYUP: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYUP:
|
||||
{
|
||||
// WM_KEYUP lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, false);
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, scanCode, flags, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_CHAR:
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, (char)wParam, (ushort)((uint)lParam >> 16));
|
||||
break;
|
||||
{
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
UnpackCharMessage(wParam, lParam, out char character, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, character, scanCode, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_WINDOWPOSCHANGED:
|
||||
var windowpos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS));
|
||||
if (((NativeMethods.SetWindowPosFlags)windowpos.flags).HasFlag(NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
|
||||
|
||||
@@ -70,6 +70,15 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.termContainer.SetTheme(theme, fontFamily, fontSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text in the terminal, clearing the selection. Otherwise returns an empty string.
|
||||
/// </summary>
|
||||
/// <returns>Selected text, empty string if no content is selected.</returns>
|
||||
public string GetSelectedText()
|
||||
{
|
||||
return this.termContainer.GetSelectedText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the terminal to the specified rows and columns.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,12 +22,20 @@
|
||||
at the winmd we build to generate type info.
|
||||
In general, cppwinrt projects all want this.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='false'">
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'=='true'">
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='true'">
|
||||
<!-- Some of our projects include the cppwinrt build options to
|
||||
just build cppwinrt things, but don't want the store bits
|
||||
in full swing. MinimalCoreWin != false means "don't let me
|
||||
use win32 APIs"
|
||||
-->
|
||||
<MinimalCoreWin>false</MinimalCoreWin>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This is magic that tells msbuild to link against the Desktop platform
|
||||
instead of the App platform. This you definitely want, because we're not
|
||||
|
||||
@@ -23,12 +23,17 @@
|
||||
#pragma hdrstop
|
||||
|
||||
using namespace Microsoft::Console::Interactivity::Win32;
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
// For usage with WM_SYSKEYDOWN message processing.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx
|
||||
// Bit 29 is whether ALT was held when the message was posted.
|
||||
#define WM_SYSKEYDOWN_ALT_PRESSED (0x20000000)
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
// ----------------------------
|
||||
// Helpers
|
||||
// ----------------------------
|
||||
@@ -123,7 +128,20 @@ bool HandleTerminalMouseEvent(const COORD cMousePosition,
|
||||
// Virtual terminal input mode
|
||||
if (IsInVirtualTerminalInputMode())
|
||||
{
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(cMousePosition, uiButton, sModifierKeystate, sWheelDelta);
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
auto clampedPosition{ cMousePosition };
|
||||
const auto clampViewport{ gci.GetActiveOutputBuffer().GetViewport().ToOrigin() };
|
||||
clampViewport.Clamp(clampedPosition);
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(clampedPosition, uiButton, sModifierKeystate, sWheelDelta, state);
|
||||
}
|
||||
|
||||
return fWasHandled;
|
||||
@@ -635,6 +653,25 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo,
|
||||
|
||||
if (HandleTerminalMouseEvent(MousePosition, Message, GET_KEYSTATE_WPARAM(wParam), sDelta))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// HandleTerminalMouseEvent returns false if the terminal's not in VT mode,
|
||||
// so capturing/releasing here should not impact other console mouse event
|
||||
// consumers.
|
||||
switch (Message)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(ServiceLocator::LocateConsoleWindow()->GetWindowHandle());
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,57 +847,113 @@ CATCH_RETURN();
|
||||
auto mutableOrigin = origin;
|
||||
|
||||
// Draw each run separately.
|
||||
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
|
||||
for (INT32 runIndex = 0; runIndex < gsl::narrow<INT32>(_runs.size()); ++runIndex)
|
||||
{
|
||||
// Get the run
|
||||
const Run& run = _runs.at(runIndex);
|
||||
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
if (!WI_IsFlagSet(run.bidiLevel, 1))
|
||||
{
|
||||
mutableOrigin.x = postOriginX;
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, run));
|
||||
}
|
||||
// This is the RTL behavior. We will advance to the last contiguous RTL run, draw that,
|
||||
// and then keep on going backwards from there, and then move runIndex beyond.
|
||||
// Let's say we have runs abcdEFGh, where runs EFG are RTL.
|
||||
// Then we will draw them in the order abcdGFEh
|
||||
else
|
||||
{
|
||||
const INT32 originalRunIndex = runIndex;
|
||||
INT32 lastIndexRTL = runIndex;
|
||||
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
// Step 1: Get to the last contiguous RTL run from here
|
||||
while (lastIndexRTL < gsl::narrow<INT32>(_runs.size()) - 1) // only could ever advance if there's something left
|
||||
{
|
||||
const Run& nextRun = _runs.at(gsl::narrow_cast<size_t>(lastIndexRTL + 1));
|
||||
if (WI_IsFlagSet(nextRun.bidiLevel, 1))
|
||||
{
|
||||
lastIndexRTL++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
// Go from the last to the first and draw
|
||||
for (runIndex = lastIndexRTL; runIndex >= originalRunIndex; runIndex--)
|
||||
{
|
||||
const Run& currentRun = _runs.at(runIndex);
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, currentRun));
|
||||
}
|
||||
runIndex = lastIndexRTL; // and the for loop will take the increment to the last one
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Draw the given run
|
||||
// - The origin is updated to be after the run.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Optional pointer to information that the renderer might need
|
||||
// while attempting to graphically place the text onto the screen
|
||||
// - renderer - The interface to be used for actually putting text onto the screen
|
||||
// - origin - pixel point of top left corner on final surface for drawing
|
||||
// - run - the run to be drawn
|
||||
[[nodiscard]] HRESULT CustomTextLayout::_DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
{
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
|
||||
@@ -147,6 +147,10 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT _DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept;
|
||||
[[nodiscard]] HRESULT _DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept;
|
||||
|
||||
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
|
||||
mouseInput->EnableDefaultTracking(true);
|
||||
|
||||
@@ -309,7 +309,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -327,7 +328,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -345,7 +347,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -371,7 +374,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
|
||||
mouseInput->SetUtf8ExtendedMode(true);
|
||||
|
||||
@@ -391,7 +394,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -409,7 +413,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -427,7 +432,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -453,7 +459,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
|
||||
mouseInput->SetSGRExtendedMode(true);
|
||||
|
||||
@@ -471,7 +477,7 @@ public:
|
||||
|
||||
// validate translation
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled,
|
||||
mouseInput->HandleMouse(Coord, uiButton, sModifierKeystate, sScrollDelta),
|
||||
mouseInput->HandleMouse(Coord, uiButton, sModifierKeystate, sScrollDelta, {}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -488,7 +494,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -506,7 +513,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -532,7 +540,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
|
||||
// Default Tracking, Default Encoding
|
||||
mouseInput->EnableDefaultTracking(true);
|
||||
@@ -550,7 +558,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -570,7 +579,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -589,7 +599,8 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta),
|
||||
sScrollDelta,
|
||||
{}),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -606,31 +617,31 @@ public:
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling up");
|
||||
s_pwszInputExpected = L"\x1B[A";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling down");
|
||||
s_pwszInputExpected = L"\x1B[B";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
|
||||
|
||||
Log::Comment(L"Enable cursor keys mode");
|
||||
mouseInput->ChangeCursorKeysMode(true);
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling up");
|
||||
s_pwszInputExpected = L"\x1BOA";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling down");
|
||||
s_pwszInputExpected = L"\x1BOB";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
|
||||
|
||||
Log::Comment(L"Confirm no effect when scroll mode is disabled");
|
||||
mouseInput->UseAlternateScreenBuffer();
|
||||
mouseInput->EnableAlternateScroll(false);
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
|
||||
Log::Comment(L"Confirm no effect when using the main buffer");
|
||||
mouseInput->UseMainScreenBuffer();
|
||||
mouseInput->EnableAlternateScroll(true);
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,10 +13,6 @@ using namespace Microsoft::Console::VirtualTerminal;
|
||||
#endif
|
||||
static const int s_MaxDefaultCoordinate = 94;
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
// Alternate scroll sequences
|
||||
static constexpr std::wstring_view CursorUpSequence{ L"\x1b[A" };
|
||||
static constexpr std::wstring_view CursorDownSequence{ L"\x1b[B" };
|
||||
@@ -105,22 +101,22 @@ static constexpr bool _isButtonDown(const unsigned int button) noexcept
|
||||
// - Retrieves which mouse button is currently pressed. This is needed because
|
||||
// MOUSEMOVE events do not also tell us if any mouse buttons are pressed during the move.
|
||||
// Parameters:
|
||||
// <none>
|
||||
// - state - the current state of which mouse buttons are pressed
|
||||
// Return value:
|
||||
// - a button corresponding to any pressed mouse buttons, else WM_LBUTTONUP if none are pressed.
|
||||
unsigned int TerminalInput::s_GetPressedButton() noexcept
|
||||
constexpr unsigned int TerminalInput::s_GetPressedButton(const MouseButtonState state) noexcept
|
||||
{
|
||||
// TODO GH#4869: Have the caller pass the mouse button state into HandleMouse
|
||||
unsigned int button = WM_LBUTTONUP; // Will be treated as a release, or no button pressed.
|
||||
if (WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed))
|
||||
// Will be treated as a release, or no button pressed.
|
||||
unsigned int button = WM_LBUTTONUP;
|
||||
if (state.isLeftButtonDown)
|
||||
{
|
||||
button = WM_LBUTTONDOWN;
|
||||
}
|
||||
else if (WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed))
|
||||
else if (state.isMiddleButtonDown)
|
||||
{
|
||||
button = WM_MBUTTONDOWN;
|
||||
}
|
||||
else if (WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed))
|
||||
else if (state.isRightButtonDown)
|
||||
{
|
||||
button = WM_RBUTTONDOWN;
|
||||
}
|
||||
@@ -298,12 +294,14 @@ bool TerminalInput::IsTrackingMouseInput() const noexcept
|
||||
// - button - the message to decode.
|
||||
// - modifierKeyState - the modifier keys pressed with this button
|
||||
// - delta - the amount that the scroll wheel changed (should be 0 unless button is a WM_MOUSE*WHEEL)
|
||||
// - state - the state of the mouse buttons at this moment
|
||||
// Return value:
|
||||
// - true if the event was handled and we should stop event propagation to the default window handler.
|
||||
bool TerminalInput::HandleMouse(const COORD position,
|
||||
const unsigned int button,
|
||||
const short modifierKeyState,
|
||||
const short delta)
|
||||
const short delta,
|
||||
const MouseButtonState state)
|
||||
{
|
||||
if (Utils::Sign(delta) != Utils::Sign(_mouseInputState.accumulatedDelta))
|
||||
{
|
||||
@@ -354,7 +352,7 @@ bool TerminalInput::HandleMouse(const COORD position,
|
||||
// _GetPressedButton will return the first pressed mouse button.
|
||||
// If it returns WM_LBUTTONUP, then we can assume that the mouse
|
||||
// moved without a button being pressed.
|
||||
const unsigned int realButton = isHover ? s_GetPressedButton() : button;
|
||||
const unsigned int realButton = isHover ? s_GetPressedButton(state) : button;
|
||||
|
||||
// In default mode, only button presses/releases are sent
|
||||
// In ButtonEvent mode, changing coord hovers WITH A BUTTON PRESSED
|
||||
|
||||
@@ -43,10 +43,19 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
#pragma region MouseInput
|
||||
// These methods are defined in mouseInput.cpp
|
||||
|
||||
struct MouseButtonState
|
||||
{
|
||||
bool isLeftButtonDown;
|
||||
bool isMiddleButtonDown;
|
||||
bool isRightButtonDown;
|
||||
};
|
||||
|
||||
bool HandleMouse(const COORD position,
|
||||
const unsigned int button,
|
||||
const short modifierKeyState,
|
||||
const short delta);
|
||||
const short delta,
|
||||
const MouseButtonState state);
|
||||
|
||||
bool IsTrackingMouseInput() const noexcept;
|
||||
#pragma endregion
|
||||
@@ -136,7 +145,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool _ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept;
|
||||
bool _SendAlternateScroll(const short delta) const noexcept;
|
||||
|
||||
static unsigned int s_GetPressedButton() noexcept;
|
||||
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
|
||||
#pragma endregion
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,7 +145,9 @@ const COORD UiaTextRangeBase::GetEndpoint(TextPatternRangeEndpoint endpoint) con
|
||||
// - true if range is degenerate, false otherwise.
|
||||
bool UiaTextRangeBase::SetEndpoint(TextPatternRangeEndpoint endpoint, const COORD val) noexcept
|
||||
{
|
||||
const auto bufferSize = _getBufferSize();
|
||||
// GH#6402: Get the actual buffer size here, instead of the one
|
||||
// constrained by the virtual bottom.
|
||||
const auto bufferSize = _pData->GetTextBuffer().GetSize();
|
||||
switch (endpoint)
|
||||
{
|
||||
case TextPatternRangeEndpoint_End:
|
||||
@@ -284,6 +286,8 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO GH#6986: properly handle "end of buffer" as last character
|
||||
// instead of last cell
|
||||
// expand to document
|
||||
_start = bufferSize.Origin();
|
||||
_end = bufferSize.EndExclusive();
|
||||
@@ -393,7 +397,9 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_
|
||||
// set of coords.
|
||||
std::vector<double> coords;
|
||||
|
||||
const auto bufferSize = _getBufferSize();
|
||||
// GH#6402: Get the actual buffer size here, instead of the one
|
||||
// constrained by the virtual bottom.
|
||||
const auto bufferSize = _pData->GetTextBuffer().GetSize();
|
||||
|
||||
// these viewport vars are converted to the buffer coordinate space
|
||||
const auto viewport = bufferSize.ConvertToOrigin(_pData->GetViewport());
|
||||
|
||||
Reference in New Issue
Block a user