terminal/src/interactivity/win32/window.cpp
Leonard Hecker 70eeea68e4 wip
2021-10-11 02:15:21 +02:00

1409 lines
52 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "ConsoleControl.hpp"
#include "icon.hpp"
#include "menu.hpp"
#include "window.hpp"
#include "windowio.hpp"
#include "windowdpiapi.hpp"
#include "WindowMetrics.hpp"
#include "../../inc/conint.h"
#include "../../host/globals.h"
#include "../../host/dbcs.h"
#include "../../host/misc.h"
#include "../../host/output.h"
#include "../../host/scrolling.hpp"
#include "../../host/srvinit.h"
#include "../../host/stream.h"
#include "../../host/telemetry.hpp"
#include "../../host/tracing.hpp"
#include "../../renderer/base/renderer.hpp"
#include "../../renderer/gdi/gdirenderer.hpp"
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
#include "../../renderer/atlas/AtlasEngine.h"
#include "../../renderer/dx/DxRenderer.hpp"
#endif
#include "../inc/ServiceLocator.hpp"
#include "../../types/inc/Viewport.hpp"
#include "../interactivity/win32/windowUiaProvider.hpp"
// The following default masks are used in creating windows
// Make sure that these flags match when switching to fullscreen and back
#define CONSOLE_WINDOW_FLAGS (WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL)
#define CONSOLE_WINDOW_EX_FLAGS (WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES | WS_EX_APPWINDOW | WS_EX_LAYERED)
// Window class name
#define CONSOLE_WINDOW_CLASS (L"ConsoleWindowClass")
using namespace Microsoft::Console::Interactivity::Win32;
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity;
using namespace Microsoft::Console::Render;
ATOM Window::s_atomWindowClass = 0;
Window* Window::s_Instance = nullptr;
Window::Window() :
_fIsInFullscreen(false),
_pSettings(nullptr),
_hWnd(nullptr),
_pUiaProvider(nullptr),
_fWasMaximizedBeforeFullscreen(false),
_dpiBeforeFullscreen(0)
{
ZeroMemory((void*)&_rcClientLast, sizeof(_rcClientLast));
ZeroMemory((void*)&_rcWindowBeforeFullscreen, sizeof(_rcWindowBeforeFullscreen));
ZeroMemory((void*)&_rcWorkBeforeFullscreen, sizeof(_rcWorkBeforeFullscreen));
}
Window::~Window()
{
if (ServiceLocator::LocateGlobals().pRender != nullptr)
{
delete ServiceLocator::LocateGlobals().pRender;
}
}
// Routine Description:
// - This routine allocates and initializes a window for the console
// Arguments:
// - pSettings - All user-configurable settings related to the console host
// - pScreen - The initial screen rendering data to attach to (renders in the client area of this window)
// Return Value:
// - STATUS_SUCCESS or suitable NT error code
[[nodiscard]] NTSTATUS Window::CreateInstance(_In_ Settings* const pSettings,
_In_ SCREEN_INFORMATION* const pScreen)
{
NTSTATUS status = s_RegisterWindowClass();
if (NT_SUCCESS(status))
{
Window* pNewWindow = new (std::nothrow) Window();
status = NT_TESTNULL(pNewWindow);
if (NT_SUCCESS(status))
{
status = pNewWindow->_MakeWindow(pSettings, pScreen);
if (NT_SUCCESS(status))
{
Window::s_Instance = pNewWindow;
LOG_IF_FAILED(ServiceLocator::SetConsoleWindowInstance(pNewWindow));
}
}
}
return status;
}
// Routine Description:
// - Registers the window class information with the system
// - Only should happen once for the entire lifetime of this class.
// Arguments:
// - <none>
// Return Value:
// - STATUS_SUCCESS or failure from loading icons/registering class with the system
[[nodiscard]] NTSTATUS Window::s_RegisterWindowClass()
{
NTSTATUS status = STATUS_SUCCESS;
// Today we never call this more than once.
// In the future, if we need multiple windows (for tabs, etc.) we will need to make this thread-safe.
// As such, the window class should always be 0 when we are entering this the first and only time.
FAIL_FAST_IF(!(s_atomWindowClass == 0));
// Only register if we haven't already registered
if (s_atomWindowClass == 0)
{
// Prepare window class structure
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
wc.lpfnWndProc = s_ConsoleWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = GWL_CONSOLE_WNDALLOC;
wc.hInstance = nullptr;
wc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wc.hbrBackground = nullptr; // We don't want the background painted. It will cause flickering.
wc.lpszMenuName = nullptr;
wc.lpszClassName = CONSOLE_WINDOW_CLASS;
// Load icons
status = Icon::Instance().GetIcons(&wc.hIcon, &wc.hIconSm);
if (NT_SUCCESS(status))
{
s_atomWindowClass = RegisterClassExW(&wc);
if (s_atomWindowClass == 0)
{
status = NTSTATUS_FROM_WIN32(GetLastError());
}
}
}
return status;
}
// Routine Description:
// - Updates some global system metrics when triggered.
// - Calls subroutines to update metrics for other relevant items
// - Example metrics include window borders, scroll size, timer values, etc.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Window::_UpdateSystemMetrics() const
{
WindowDpiApi* const dpiApi = ServiceLocator::LocateHighDpiApi<WindowDpiApi>();
Globals& g = ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();
Scrolling::s_UpdateSystemMetrics();
g.sVerticalScrollSize = (SHORT)dpiApi->GetSystemMetricsForDpi(SM_CXVSCROLL, g.dpi);
g.sHorizontalScrollSize = (SHORT)dpiApi->GetSystemMetricsForDpi(SM_CYHSCROLL, g.dpi);
gci.GetCursorBlinker().UpdateSystemMetrics();
const auto sysConfig = ServiceLocator::LocateSystemConfigurationProvider();
g.cursorPixelWidth = sysConfig->GetCursorWidth();
}
// Routine Description:
// - This will call the system to create a window for the console, set
// up settings, and prepare for rendering.
// Arguments:
// - pSettings - Load user-configurable settings from this structure
// - pScreen - Attach to this screen for rendering the client area of the window
// Return Value:
// - STATUS_SUCCESS, invalid parameters, or various potential errors from calling CreateWindow
[[nodiscard]] NTSTATUS Window::_MakeWindow(_In_ Settings* const pSettings,
_In_ SCREEN_INFORMATION* const pScreen)
{
Globals& g = ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();
NTSTATUS status = STATUS_SUCCESS;
if (pSettings == nullptr)
{
status = STATUS_INVALID_PARAMETER_1;
}
else if (pScreen == nullptr)
{
status = STATUS_INVALID_PARAMETER_2;
}
// Ensure we have appropriate system metrics before we start constructing the window.
_UpdateSystemMetrics();
const auto useDx = pSettings->GetUseDx();
GdiEngine* pGdiEngine = nullptr;
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
DxEngine* pDxEngine = nullptr;
AtlasEngine* pAtlasEngine = nullptr;
#endif
try
{
switch (useDx)
{
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
case 1:
pDxEngine = new DxEngine();
// TODO: MSFT:21255595 make this less gross
// Manually set the Dx Engine to Hwnd mode. When we're trying to
// determine the initial window size, which happens BEFORE the
// window is created, we'll want to make sure the DX engine does
// math in the hwnd mode, not the Composition mode.
THROW_IF_FAILED(pDxEngine->SetHwnd(nullptr));
g.pRender->AddRenderEngine(pDxEngine);
break;
case 2:
pAtlasEngine = new AtlasEngine();
g.pRender->AddRenderEngine(pAtlasEngine);
break;
#endif
default:
pGdiEngine = new GdiEngine();
g.pRender->AddRenderEngine(pGdiEngine);
break;
}
}
catch (...)
{
status = NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException());
}
if (NT_SUCCESS(status))
{
// Save reference to settings
_pSettings = pSettings;
// Figure out coordinates and how big to make the window from the desired client viewport size
// Put left, top, right and bottom into rectProposed for checking against monitor screens below
RECT rectProposed = { pSettings->GetWindowOrigin().X, pSettings->GetWindowOrigin().Y, 0, 0 };
_CalculateWindowRect(pSettings->GetWindowSize(), &rectProposed); //returns with rectangle filled out
if (!WI_IsFlagSet(gci.Flags, CONSOLE_AUTO_POSITION))
{
//if launched from a shortcut, ensure window is visible on screen
if (pSettings->IsStartupTitleIsLinkNameSet())
{
// if window would be fully OFFscreen, change position so it is ON screen.
// This doesn't change the actual coordinates
// stored in the link, just the starting position
// of the window.
// When the user reconnects the other monitor, the
// window will be where he left it. Great for take
// home laptop scenario.
if (!MonitorFromRect(&rectProposed, MONITOR_DEFAULTTONULL))
{
//Monitor we'll move to
HMONITOR hMon = MonitorFromRect(&rectProposed, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { 0 };
//get origin of monitor's workarea
mi.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMon, &mi);
//Adjust right and bottom to new positions, relative to monitor workarea's origin
//Need to do this before adjusting left/top so RECT_* calculations are correct
rectProposed.right = mi.rcWork.left + RECT_WIDTH(&rectProposed);
rectProposed.bottom = mi.rcWork.top + RECT_HEIGHT(&rectProposed);
// Move origin to top left of nearest
// monitor's WORKAREA (accounting for taskbar
// and any app toolbars)
rectProposed.left = mi.rcWork.left;
rectProposed.top = mi.rcWork.top;
}
}
}
// CreateWindowExW needs a null terminated string, so ensure
// title is null terminated in a std::wstring here.
// We don't mind the string copy here because making the window
// should be infrequent.
const std::wstring title{ gci.GetTitle() };
// Attempt to create window
HWND hWnd = CreateWindowExW(
CONSOLE_WINDOW_EX_FLAGS,
CONSOLE_WINDOW_CLASS,
title.c_str(),
CONSOLE_WINDOW_FLAGS,
WI_IsFlagSet(gci.Flags, CONSOLE_AUTO_POSITION) ? CW_USEDEFAULT : rectProposed.left,
rectProposed.top, // field is ignored if CW_USEDEFAULT was chosen above
RECT_WIDTH(&rectProposed),
RECT_HEIGHT(&rectProposed),
HWND_DESKTOP,
nullptr,
nullptr,
this // handle to this window class, passed to WM_CREATE to help dispatching to this instance
);
if (hWnd == nullptr)
{
DWORD const gle = GetLastError();
RIPMSG1(RIP_WARNING, "CreateWindow failed with gle = 0x%x", gle);
status = NTSTATUS_FROM_WIN32(gle);
}
if (NT_SUCCESS(status))
{
_hWnd = hWnd;
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
if (pDxEngine)
{
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->SetHwnd(hWnd))));
if (NT_SUCCESS(status))
{
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->Enable())));
}
}
else if (pAtlasEngine)
{
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pAtlasEngine->SetHwnd(hWnd))));
}
else
#endif
{
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pGdiEngine->SetHwnd(hWnd))));
}
if (NT_SUCCESS(status))
{
// Set alpha on window if requested
ApplyWindowOpacity();
status = Menu::CreateInstance(hWnd);
if (NT_SUCCESS(status))
{
gci.ConsoleIme.RefreshAreaAttributes();
// Do WM_GETICON workaround. Must call WM_SETICON once or apps calling WM_GETICON will get null.
LOG_IF_FAILED(Icon::Instance().ApplyWindowMessageWorkaround(hWnd));
// Set up the hot key for this window.
if (gci.GetHotKey() != 0)
{
SendMessageW(hWnd, WM_SETHOTKEY, gci.GetHotKey(), 0);
}
// Post a window size update so that the new console window will size itself correctly once it's up and
// running. This works around chicken & egg cases involving window size calculations having to do with font
// sizes, DPI, and non-primary monitors (see MSFT #2367234).
SCREEN_INFORMATION& siAttached = GetScreenInfo();
siAttached.PostUpdateWindowSize();
// Locate window theming modules and try to set the dark mode.
LOG_IF_FAILED(Microsoft::Console::Internal::Theming::TrySetDarkMode(_hWnd));
}
}
}
}
return status;
}
// Routine Description:
// - Called when the window is about to close
// - Right now, it just triggers the process list management to notify that we're closing
// Arguments:
// - <none>
// Return Value:
// - <none>
void Window::_CloseWindow() const
{
// Pass on the notification to attached processes.
// Since we only have one window for now, this will be the end of the host process as well.
CloseConsoleProcessState();
}
// Routine Description:
// - Activates and shows this window based on the flags given.
// Arguments:
// - wShowWindow - See STARTUPINFO wShowWindow member: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
// Return Value:
// - STATUS_SUCCESS or system errors from activating the window and setting its show states
[[nodiscard]] NTSTATUS Window::ActivateAndShow(const WORD wShowWindow)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
NTSTATUS status = STATUS_SUCCESS;
HWND const hWnd = GetWindowHandle();
// Only activate if the wShowWindow we were passed at process create doesn't explicitly tell us to remain inactive/hidden
if (wShowWindow != SW_SHOWNOACTIVATE &&
wShowWindow != SW_SHOWMINNOACTIVE &&
wShowWindow != SW_HIDE)
{
// Do not check result. On some SKUs, such as WinPE, it's perfectly OK for NULL to be returned.
SetActiveWindow(hWnd);
}
else if (wShowWindow == SW_SHOWMINNOACTIVE)
{
// If we're minimized and not the active window, set iconic to stop rendering
gci.Flags |= CONSOLE_IS_ICONIC;
}
if (NT_SUCCESS(status))
{
ShowWindow(hWnd, wShowWindow);
SCREEN_INFORMATION& siAttached = GetScreenInfo();
siAttached.InternalUpdateScrollBars();
}
return status;
}
// Routine Description:
// - This routine sets the window origin.
// Arguments:
// - NewWindow: the inclusive rect to use as the new viewport in the buffer
// Return Value:
// <none>
void Window::ChangeViewport(const SMALL_RECT NewWindow)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& ScreenInfo = GetScreenInfo();
COORD const FontSize = ScreenInfo.GetScreenFontSize();
if (WI_IsFlagClear(gci.Flags, CONSOLE_IS_ICONIC))
{
Selection* pSelection = &Selection::Instance();
pSelection->HideSelection();
// Fire off an event to let accessibility apps know we've scrolled.
IAccessibilityNotifier* pNotifier = ServiceLocator::LocateAccessibilityNotifier();
if (pNotifier != nullptr)
{
pNotifier->NotifyConsoleUpdateScrollEvent(ScreenInfo.GetViewport().Left() - NewWindow.Left,
ScreenInfo.GetViewport().Top() - NewWindow.Top);
}
// The new window is OK. Store it in screeninfo and refresh screen.
ScreenInfo.SetViewport(Viewport::FromInclusive(NewWindow), false);
Tracing::s_TraceWindowViewport(ScreenInfo.GetViewport());
if (ServiceLocator::LocateGlobals().pRender != nullptr)
{
ServiceLocator::LocateGlobals().pRender->TriggerScroll();
}
pSelection->ShowSelection();
}
else
{
// we're iconic
ScreenInfo.SetViewport(Viewport::FromInclusive(NewWindow), false);
Tracing::s_TraceWindowViewport(ScreenInfo.GetViewport());
}
LOG_IF_FAILED(ConsoleImeResizeCompStrView());
ScreenInfo.UpdateScrollBars();
}
// Routine Description:
// - Sends an update to the window size based on the character size requested.
// Arguments:
// - Size of the window in characters (relative to the current font)
// Return Value:
// - <none>
void Window::UpdateWindowSize(const COORD coordSizeInChars)
{
GetScreenInfo().SetViewportSize(&coordSizeInChars);
PostUpdateWindowSize();
}
// Routine Description:
// Arguments:
// Return Value:
void Window::UpdateWindowPosition(_In_ POINT const ptNewPos) const
{
SetWindowPos(GetWindowHandle(),
nullptr,
ptNewPos.x,
ptNewPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER);
}
// This routine adds or removes the name to or from the beginning of the window title. The possible names are "Scroll", "Mark", and "Select"
void Window::UpdateWindowText()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const bool fInScrollMode = Scrolling::s_IsInScrollMode();
Selection* pSelection = &Selection::Instance();
const bool fInKeyboardMarkMode = pSelection->IsInSelectingState() && pSelection->IsKeyboardMarkSelection();
const bool fInMouseSelectMode = pSelection->IsInSelectingState() && pSelection->IsMouseInitiatedSelection();
// should have at most one active mode
FAIL_FAST_IF(!((fInKeyboardMarkMode && !fInMouseSelectMode && !fInScrollMode) ||
(!fInKeyboardMarkMode && fInMouseSelectMode && !fInScrollMode) ||
(!fInKeyboardMarkMode && !fInMouseSelectMode && fInScrollMode) ||
(!fInKeyboardMarkMode && !fInMouseSelectMode && !fInScrollMode)));
// determine which message, if any, we want to use
DWORD dwMsgId = 0;
if (fInKeyboardMarkMode)
{
dwMsgId = ID_CONSOLE_MSGMARKMODE;
}
else if (fInMouseSelectMode)
{
dwMsgId = ID_CONSOLE_MSGSELECTMODE;
}
else if (fInScrollMode)
{
dwMsgId = ID_CONSOLE_MSGSCROLLMODE;
}
// if we have a message, use it
if (dwMsgId != 0)
{
// load mode string
WCHAR szMsg[64];
if (LoadStringW(ServiceLocator::LocateGlobals().hInstance, dwMsgId, szMsg, ARRAYSIZE(szMsg)) > 0)
{
gci.SetTitlePrefix(szMsg);
}
}
else
{
// no mode-based message. set title back to original state.
gci.SetTitlePrefix(L"");
}
}
void Window::CaptureMouse()
{
SetCapture(_hWnd);
}
BOOL Window::ReleaseMouse()
{
return ReleaseCapture();
}
// Routine Description:
// - Adjusts the outer window frame size. Does not move the position.
// Arguments:
// - sizeNew - The X and Y dimensions
// Return Value:
// - <none>
void Window::_UpdateWindowSize(const SIZE sizeNew)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& ScreenInfo = GetScreenInfo();
if (WI_IsFlagClear(gci.Flags, CONSOLE_IS_ICONIC))
{
ScreenInfo.InternalUpdateScrollBars();
SetWindowPos(GetWindowHandle(),
nullptr,
0,
0,
sizeNew.cx,
sizeNew.cy,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
}
}
// Routine Description:
// - Triggered when the buffer dimensions/viewport is changed.
// - This function recalculates what size the window should be in order to host the given buffer and viewport
// - Then it will trigger an actual adjustment of the outer window frame
// Arguments:
// - <none> - All state is read from the attached screen buffer
// Return Value:
// - STATUS_SUCCESS or suitable error code
[[nodiscard]] NTSTATUS Window::_InternalSetWindowSize()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& siAttached = GetScreenInfo();
WI_ClearFlag(gci.Flags, CONSOLE_SETTING_WINDOW_SIZE);
if (!IsInFullscreen() && !IsInMaximized())
{
// Figure out how big to make the window, given the desired client area size.
siAttached.ResizingWindow++;
// First get the buffer viewport size
const auto WindowDimensions = siAttached.GetViewport().Dimensions();
// We'll use the font to convert characters to pixels
const auto ScreenFontSize = siAttached.GetScreenFontSize();
// Now do the multiplication of characters times pixels per char. This is the client area pixel size.
SIZE WindowSize;
WindowSize.cx = WindowDimensions.X * ScreenFontSize.X;
WindowSize.cy = WindowDimensions.Y * ScreenFontSize.Y;
// Fill a rectangle to call the system to adjust the client rect into a window rect
RECT rectSizeTemp = { 0 };
rectSizeTemp.right = WindowSize.cx;
rectSizeTemp.bottom = WindowSize.cy;
FAIL_FAST_IF(!(rectSizeTemp.top == 0 && rectSizeTemp.left == 0));
ServiceLocator::LocateWindowMetrics<WindowMetrics>()->ConvertClientRectToWindowRect(&rectSizeTemp);
// Measure the adjusted rectangle dimensions and fill up the size variable
WindowSize.cx = RECT_WIDTH(&rectSizeTemp);
WindowSize.cy = RECT_HEIGHT(&rectSizeTemp);
if (WindowDimensions.Y != 0)
{
// We want the alt to have scroll bars if the main has scroll bars.
// The bars are disabled, but they're still there.
// This keeps the window, viewport, and SB size from changing when swapping.
if (!siAttached.GetMainBuffer().IsMaximizedX())
{
WindowSize.cy += ServiceLocator::LocateGlobals().sHorizontalScrollSize;
}
if (!siAttached.GetMainBuffer().IsMaximizedY())
{
WindowSize.cx += ServiceLocator::LocateGlobals().sVerticalScrollSize;
}
}
// Only attempt to actually change the window size if the difference between the size we just calculated
// and the size of the current window is substantial enough to make a rendering difference.
// This is an issue now in the V2 console because we allow sub-character window sizes
// such that there isn't leftover space around the window when snapping.
// To figure out if it's substantial, calculate what the window size would be if it were one character larger than what we just proposed
SIZE WindowSizeMax;
WindowSizeMax.cx = WindowSize.cx + ScreenFontSize.X;
WindowSizeMax.cy = WindowSize.cy + ScreenFontSize.Y;
// And figure out the current window size as well.
RECT const rcWindowCurrent = GetWindowRect();
SIZE WindowSizeCurrent;
WindowSizeCurrent.cx = RECT_WIDTH(&rcWindowCurrent);
WindowSizeCurrent.cy = RECT_HEIGHT(&rcWindowCurrent);
// If the current window has a few extra sub-character pixels between the proposed size (WindowSize) and the next size up (WindowSizeMax), then don't change anything.
bool const fDeltaXSubstantial = !(WindowSizeCurrent.cx >= WindowSize.cx && WindowSizeCurrent.cx < WindowSizeMax.cx);
bool const fDeltaYSubstantial = !(WindowSizeCurrent.cy >= WindowSize.cy && WindowSizeCurrent.cy < WindowSizeMax.cy);
// If either change was substantial, update the window accordingly to the newly proposed value.
if (fDeltaXSubstantial || fDeltaYSubstantial)
{
_UpdateWindowSize(WindowSize);
}
else
{
// If the change wasn't substantial, we may still need to update scrollbar positions. Note that PSReadLine
// scrolls the window via Console.SetWindowPosition, which ultimately calls down to SetConsoleWindowInfo,
// which ends up in this function.
siAttached.InternalUpdateScrollBars();
}
// MSFT: 12092729
// To fix an issue with 3rd party applications that wrap our console, notify that the screen buffer size changed
// when the window viewport is updated.
// ---
// - The specific scenario that this impacts is ConEmu (wrapping our console) to use Bash in WSL.
// - The reason this is a problem is because ConEmu has to programmatically manipulate our buffer and window size
// one after another to get our dimensions to change.
// - The WSL layer watches our Buffer change message to know when to get the new Window size and send it into the
// WSL environment. This isn't technically correct to use a Buffer message to know when Window changes, but
// it's not totally their fault because we do not provide a Window changed message at all.
// - If our window is adjusted directly, the Buffer and Window dimensions are both updated simultaneously under lock
// and WSL gets one message and updates appropriately.
// - If ConEmu updates it via the API, one is updated, then the other with an unlocked time interval.
// The WSL layer will potentially get the Window size that hasn't been updated yet or is out of sync before the
// other API call is completed which results in the application in the WSL environment thinking the window is
// a different size and outputting VT sequences with an invalid assumption.
// - If we make it so a Window change also emits a Buffer change message, then WSL will be notified appropriately
// and can pass that information into the WSL environment.
// - To Windows apps that weren't expecting this information, it should cause no harm because they should just receive
// an additional Buffer message with the same size again and do nothing special.
ScreenBufferSizeChange(siAttached.GetActiveBuffer().GetBufferSize().Dimensions());
siAttached.ResizingWindow--;
}
LOG_IF_FAILED(ConsoleImeResizeCompStrView());
return STATUS_SUCCESS;
}
// Routine Description:
// - Adjusts the window contents in response to vertical scrolling
// Arguments:
// - ScrollCommand - The relevant command (one line, one page, etc.)
// - AbsoluteChange - The magnitude of the change (for tracking commands)
// Return Value:
// - <none>
void Window::VerticalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
COORD NewOrigin;
SCREEN_INFORMATION& ScreenInfo = GetScreenInfo();
// Log a telemetry event saying the user interacted with the Console
Telemetry::Instance().SetUserInteractive();
const auto& viewport = ScreenInfo.GetViewport();
NewOrigin = viewport.Origin();
const SHORT sScreenBufferSizeY = ScreenInfo.GetBufferSize().Height();
switch (wScrollCommand)
{
case SB_LINEUP:
{
NewOrigin.Y--;
break;
}
case SB_LINEDOWN:
{
NewOrigin.Y++;
break;
}
case SB_PAGEUP:
{
NewOrigin.Y -= viewport.Height() - 1;
break;
}
case SB_PAGEDOWN:
{
NewOrigin.Y += viewport.Height() - 1;
break;
}
case SB_THUMBTRACK:
{
gci.Flags |= CONSOLE_SCROLLBAR_TRACKING;
NewOrigin.Y = wAbsoluteChange;
break;
}
case SB_THUMBPOSITION:
{
UnblockWriteConsole(CONSOLE_SCROLLBAR_TRACKING);
NewOrigin.Y = wAbsoluteChange;
break;
}
case SB_TOP:
{
NewOrigin.Y = 0;
break;
}
case SB_BOTTOM:
{
NewOrigin.Y = sScreenBufferSizeY - viewport.Height();
break;
}
default:
{
return;
}
}
NewOrigin.Y = std::clamp(NewOrigin.Y, 0i16, gsl::narrow<SHORT>(sScreenBufferSizeY - viewport.Height()));
LOG_IF_FAILED(ScreenInfo.SetViewportOrigin(true, NewOrigin, false));
}
// Routine Description:
// - Adjusts the window contents in response to horizontal scrolling
// Arguments:
// - ScrollCommand - The relevant command (one line, one page, etc.)
// - AbsoluteChange - The magnitude of the change (for tracking commands)
// Return Value:
// - <none>
void Window::HorizontalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange)
{
// Log a telemetry event saying the user interacted with the Console
Telemetry::Instance().SetUserInteractive();
SCREEN_INFORMATION& ScreenInfo = GetScreenInfo();
const SHORT sScreenBufferSizeX = ScreenInfo.GetBufferSize().Width();
const auto& viewport = ScreenInfo.GetViewport();
COORD NewOrigin = viewport.Origin();
switch (wScrollCommand)
{
case SB_LINEUP:
{
NewOrigin.X--;
break;
}
case SB_LINEDOWN:
{
NewOrigin.X++;
break;
}
case SB_PAGEUP:
{
NewOrigin.X -= viewport.Width() - 1;
break;
}
case SB_PAGEDOWN:
{
NewOrigin.X += viewport.Width() - 1;
break;
}
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
{
NewOrigin.X = wAbsoluteChange;
break;
}
case SB_TOP:
{
NewOrigin.X = 0;
break;
}
case SB_BOTTOM:
{
NewOrigin.X = (WORD)(sScreenBufferSizeX - viewport.Width());
break;
}
default:
{
return;
}
}
NewOrigin.X = std::clamp(NewOrigin.X, 0i16, gsl::narrow<SHORT>(sScreenBufferSizeX - viewport.Width()));
LOG_IF_FAILED(ScreenInfo.SetViewportOrigin(true, NewOrigin, false));
}
BOOL Window::EnableBothScrollBars()
{
return EnableScrollBar(_hWnd, SB_BOTH, ESB_ENABLE_BOTH);
}
int Window::UpdateScrollBar(bool isVertical,
bool isAltBuffer,
UINT pageSize,
int maxSize,
int viewportPosition)
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = isAltBuffer ? SIF_ALL | SIF_DISABLENOSCROLL : SIF_ALL;
si.nPage = pageSize;
si.nMin = 0;
si.nMax = maxSize;
si.nPos = viewportPosition;
return SetScrollInfo(_hWnd, isVertical ? SB_VERT : SB_HORZ, &si, TRUE);
}
// Routine Description:
// - Converts a window position structure (sent to us when the window moves) into a window rectangle (the outside edge dimensions)
// Arguments:
// - lpWindowPos - position structure received via Window message
// - prc - rectangle to fill
// Return Value:
// - <none>
void Window::s_ConvertWindowPosToWindowRect(const LPWINDOWPOS lpWindowPos, _Out_ RECT* const prc)
{
prc->left = lpWindowPos->x;
prc->top = lpWindowPos->y;
prc->right = lpWindowPos->x + lpWindowPos->cx;
prc->bottom = lpWindowPos->y + lpWindowPos->cy;
}
// Routine Description:
// - Converts character counts of the viewport (client area, screen buffer) into the outer pixel dimensions of the window using the current window for context
// Arguments:
// - coordWindowInChars - The size of the viewport
// - prectWindow - rectangle to fill with pixel positions of the outer edge rectangle for this window
// Return Value:
// - <none>
void Window::_CalculateWindowRect(const COORD coordWindowInChars, _Inout_ RECT* const prectWindow) const
{
auto& g = ServiceLocator::LocateGlobals();
const SCREEN_INFORMATION& siAttached = GetScreenInfo();
const COORD coordFontSize = siAttached.GetScreenFontSize();
const HWND hWnd = GetWindowHandle();
const COORD coordBufferSize = siAttached.GetBufferSize().Dimensions();
const int iDpi = g.dpi;
s_CalculateWindowRect(coordWindowInChars, iDpi, coordFontSize, coordBufferSize, hWnd, prectWindow);
}
// Routine Description:
// - Converts character counts of the viewport (client area, screen buffer) into
// the outer pixel dimensions of the window
// Arguments:
// - coordWindowInChars - The size of the viewport
// - iDpi - The DPI of the monitor on which this window will reside
// (used to get DPI-scaled system metrics)
// - coordFontSize - the size in pixels of the font on the monitor
// (this should be already scaled for DPI)
// - coordBufferSize - the character count of the buffer rectangle in X by Y
// - hWnd - If available, a handle to the window we would change so we can
// retrieve its class information for border/titlebar/etc metrics.
// - prectWindow - rectangle to fill with pixel positions of the outer edge
// rectangle for this window
// Return Value:
// - <none>
void Window::s_CalculateWindowRect(const COORD coordWindowInChars,
const int iDpi,
const COORD coordFontSize,
const COORD coordBufferSize,
_In_opt_ HWND const hWnd,
_Inout_ RECT* const prectWindow)
{
SIZE sizeWindow;
// Initially use the given size in characters * font size to get client area pixel size
sizeWindow.cx = coordWindowInChars.X * coordFontSize.X;
sizeWindow.cy = coordWindowInChars.Y * coordFontSize.Y;
// Create a proposed rectangle
RECT rectProposed = { prectWindow->left, prectWindow->top, prectWindow->left + sizeWindow.cx, prectWindow->top + sizeWindow.cy };
// Now adjust the client area into a window size
// 1. Start with default window style
DWORD dwStyle = CONSOLE_WINDOW_FLAGS;
DWORD dwExStyle = CONSOLE_WINDOW_EX_FLAGS;
BOOL fMenu = FALSE;
// 2. if we already have a window handle, check if the style has been updated
if (hWnd != nullptr)
{
dwStyle = GetWindowStyle(hWnd);
dwExStyle = GetWindowExStyle(hWnd);
}
// 3. Perform adjustment
// NOTE: This may adjust the position of the window as well as the size. This is why we use rectProposed in the interim.
ServiceLocator::LocateWindowMetrics<WindowMetrics>()->AdjustWindowRectEx(&rectProposed, dwStyle, fMenu, dwExStyle, iDpi);
// Finally compensate for scroll bars
// If the window is smaller than the buffer in width, add space at the bottom for a horizontal scroll bar
if (coordWindowInChars.X < coordBufferSize.X)
{
rectProposed.bottom += (SHORT)ServiceLocator::LocateHighDpiApi<WindowDpiApi>()->GetSystemMetricsForDpi(SM_CYHSCROLL, iDpi);
}
// If the window is smaller than the buffer in height, add space at the right for a vertical scroll bar
if (coordWindowInChars.Y < coordBufferSize.Y)
{
rectProposed.right += (SHORT)ServiceLocator::LocateHighDpiApi<WindowDpiApi>()->GetSystemMetricsForDpi(SM_CXVSCROLL, iDpi);
}
// Apply the calculated sizes to the existing window pointer
// We do this at the end so we can preserve the positioning of the window and just change the size.
prectWindow->right = prectWindow->left + RECT_WIDTH(&rectProposed);
prectWindow->bottom = prectWindow->top + RECT_HEIGHT(&rectProposed);
}
RECT Window::GetWindowRect() const noexcept
{
RECT rc = { 0 };
::GetWindowRect(GetWindowHandle(), &rc);
return rc;
}
HWND Window::GetWindowHandle() const
{
return _hWnd;
}
SCREEN_INFORMATION& Window::GetScreenInfo()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return gci.GetActiveOutputBuffer();
}
const SCREEN_INFORMATION& Window::GetScreenInfo() const
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return gci.GetActiveOutputBuffer();
}
// Routine Description:
// - Gets the window opacity (alpha channel)
// Arguments:
// - <none>
// Return Value:
// - The level of opacity. 0xff should represent 100% opaque and 0x00 should be 100% transparent. (Used for Alpha channel in drawing.)
BYTE Window::GetWindowOpacity() const
{
return _pSettings->GetWindowAlpha();
}
// Routine Description:
// - Sets the window opacity (alpha channel) with the given value
// - Will restrict to within the valid range. Invalid values will use 0% transparency/100% opaque.
// Arguments:
// - bOpacity - 0xff/100% opacity = opaque window. 0xb2/70% opacity = 30% transparent window.
// Return Value:
// - <none>
void Window::SetWindowOpacity(const BYTE bOpacity)
{
_pSettings->SetWindowAlpha(bOpacity);
}
// Routine Description:
// - Calls the operating system to apply the current window opacity settings to the active console window.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Window::ApplyWindowOpacity() const
{
const BYTE bAlpha = GetWindowOpacity();
HWND const hWnd = GetWindowHandle();
// See: http://msdn.microsoft.com/en-us/library/ms997507.aspx
SetLayeredWindowAttributes(hWnd, 0, bAlpha, LWA_ALPHA);
}
// Routine Description:
// - Changes the window opacity by a specified delta.
// - This will update the internally stored value by the given delta (within boundaries)
// and then will have the new value applied to the actual window.
// - Values that would make the opacity greater than 100% will be fixed to 100%.
// - Values that would bring the opacity below the minimum threshold will be fixed to the minimum threshold.
// Arguments:
// - sOpacityDelta - How much to modify the current window opacity. Positive = more opaque. Negative = more transparent.
// Return Value:
// - <none>
void Window::ChangeWindowOpacity(const short sOpacityDelta)
{
// Window Opacity is always a BYTE (unsigned char, 1 byte)
// Delta is a short (signed short, 2 bytes)
int iAlpha = GetWindowOpacity(); // promote unsigned char to fit into a signed int (4 bytes)
iAlpha += sOpacityDelta; // performing signed math of 2 byte delta into 4 bytes will not under/overflow.
// comparisons are against 1 byte values and are ok.
if (iAlpha > BYTE_MAX)
{
iAlpha = BYTE_MAX;
}
else if (iAlpha < MIN_WINDOW_OPACITY)
{
iAlpha = MIN_WINDOW_OPACITY;
}
//Opacity bool is set to true when keyboard or mouse short cut used.
SetWindowOpacity((BYTE)iAlpha); // cast to fit is guaranteed to be within byte bounds by the checks above.
ApplyWindowOpacity();
}
// Routine Description:
// - Shorthand for checking if the current window has the maximized property set
// - Uses internally stored window handle
// Return Value:
// - True if maximized. False otherwise.
bool Window::IsInMaximized() const
{
return IsMaximized(_hWnd);
}
bool Window::IsInFullscreen() const
{
return _fIsInFullscreen;
}
// Routine Description:
// - Called when entering fullscreen, with the window's current monitor rect and work area.
// - The current window position, dpi, work area, and maximized state are stored, and the
// window is positioned to the monitor rect.
void Window::_SetFullscreenPosition(const RECT rcMonitor, const RECT rcWork)
{
::GetWindowRect(GetWindowHandle(), &_rcWindowBeforeFullscreen);
_dpiBeforeFullscreen = GetDpiForWindow(GetWindowHandle());
_fWasMaximizedBeforeFullscreen = IsZoomed(GetWindowHandle());
_rcWorkBeforeFullscreen = rcWork;
SetWindowPos(GetWindowHandle(),
HWND_TOP,
rcMonitor.left,
rcMonitor.top,
rcMonitor.right - rcMonitor.left,
rcMonitor.bottom - rcMonitor.top,
SWP_FRAMECHANGED);
}
// Routine Description:
// - Called when exiting fullscreen, with the window's current monitor work area.
// - The window is restored to its previous position, migrating that previous position to the
// window's current monitor (if the current work area or window DPI have changed).
// - A fullscreen window's monitor can be changed by win+shift+left/right hotkeys or monitor
// topology changes (for example unplugging a monitor or disconnecting a remote session).
void Window::_RestoreFullscreenPosition(const RECT rcWork)
{
// If the window was previously maximized, re-maximize the window.
if (_fWasMaximizedBeforeFullscreen)
{
ShowWindow(GetWindowHandle(), SW_SHOWMAXIMIZED);
SetWindowPos(GetWindowHandle(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
return;
}
// Start with the stored window position.
RECT rcRestore = _rcWindowBeforeFullscreen;
// If the window DPI has changed, re-size the stored position by the change in DPI. This
// ensures the window restores to the same logical size (even if to a monitor with a different
// DPI/ scale factor).
UINT dpiWindow = GetDpiForWindow(GetWindowHandle());
rcRestore.right = rcRestore.left + MulDiv(rcRestore.right - rcRestore.left, dpiWindow, _dpiBeforeFullscreen);
rcRestore.bottom = rcRestore.top + MulDiv(rcRestore.bottom - rcRestore.top, dpiWindow, _dpiBeforeFullscreen);
// Offset the stored position by the difference in work area.
OffsetRect(&rcRestore,
rcWork.left - _rcWorkBeforeFullscreen.left,
rcWork.top - _rcWorkBeforeFullscreen.top);
// Enforce that our position is entirely within the bounds of our work area.
// Prefer the top-left be on-screen rather than bottom-right (right before left, bottom before top).
if (rcRestore.right > rcWork.right)
{
OffsetRect(&rcRestore, rcWork.right - rcRestore.right, 0);
}
if (rcRestore.left < rcWork.left)
{
OffsetRect(&rcRestore, rcWork.left - rcRestore.left, 0);
}
if (rcRestore.bottom > rcWork.bottom)
{
OffsetRect(&rcRestore, 0, rcWork.bottom - rcRestore.bottom);
}
if (rcRestore.top < rcWork.top)
{
OffsetRect(&rcRestore, 0, rcWork.top - rcRestore.top);
}
// Show the window at the computed position.
SetWindowPos(GetWindowHandle(),
HWND_TOP,
rcRestore.left,
rcRestore.top,
rcRestore.right - rcRestore.left,
rcRestore.bottom - rcRestore.top,
SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
void Window::SetIsFullscreen(const bool fFullscreenEnabled)
{
const bool fChangingFullscreen = (fFullscreenEnabled != _fIsInFullscreen);
_fIsInFullscreen = fFullscreenEnabled;
HWND const hWnd = GetWindowHandle();
// First, modify regular window styles as appropriate
LONG dwWindowStyle = GetWindowLongW(hWnd, GWL_STYLE);
if (_fIsInFullscreen)
{
// moving to fullscreen. remove WS_OVERLAPPEDWINDOW, which specifies styles for non-fullscreen windows (e.g.
// caption bar). add the WS_POPUP style to allow us to size ourselves to the monitor size.
WI_ClearAllFlags(dwWindowStyle, WS_OVERLAPPEDWINDOW);
WI_SetFlag(dwWindowStyle, WS_POPUP);
}
else
{
// coming back from fullscreen. undo what we did to get in to fullscreen in the first place.
WI_ClearFlag(dwWindowStyle, WS_POPUP);
WI_SetAllFlags(dwWindowStyle, WS_OVERLAPPEDWINDOW);
}
SetWindowLongW(hWnd, GWL_STYLE, dwWindowStyle);
// Now modify extended window styles as appropriate
LONG dwExWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE);
if (_fIsInFullscreen)
{
// moving to fullscreen. remove the window edge style to avoid an ugly border when not focused.
WI_ClearFlag(dwExWindowStyle, WS_EX_WINDOWEDGE);
}
else
{
// coming back from fullscreen.
WI_SetFlag(dwExWindowStyle, WS_EX_WINDOWEDGE);
}
SetWindowLongW(hWnd, GWL_EXSTYLE, dwExWindowStyle);
// Only change the window position if changing fullscreen state.
if (fChangingFullscreen)
{
// Get the monitor info for the window's current monitor.
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromWindow(GetWindowHandle(), MONITOR_DEFAULTTONEAREST), &mi);
if (_fIsInFullscreen)
{
// Store the window's current position and size the window to the monitor.
_SetFullscreenPosition(mi.rcMonitor, mi.rcWork);
}
else
{
// Restore the stored window position.
_RestoreFullscreenPosition(mi.rcWork);
SCREEN_INFORMATION& siAttached = GetScreenInfo();
siAttached.MakeCurrentCursorVisible();
}
}
}
void Window::ToggleFullscreen()
{
SetIsFullscreen(!IsInFullscreen());
}
void Window::s_ReinitializeFontsForDPIChange()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveOutputBuffer().RefreshFontWithRenderer();
}
[[nodiscard]] LRESULT Window::s_RegPersistWindowPos(_In_ PCWSTR const pwszTitle,
const BOOL fAutoPos,
const Window* const pWindow)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
HKEY hCurrentUserKey, hConsoleKey, hTitleKey;
// Open the current user registry key.
NTSTATUS Status = RegistrySerialization::s_OpenCurrentUserConsoleTitleKey(pwszTitle, &hCurrentUserKey, &hConsoleKey, &hTitleKey);
if (NT_SUCCESS(Status))
{
// Save window size
auto windowRect = pWindow->GetWindowRect();
const auto windowDimensions = gci.GetActiveOutputBuffer().GetViewport().Dimensions();
DWORD dwValue = MAKELONG(windowDimensions.X, windowDimensions.Y);
Status = RegistrySerialization::s_UpdateValue(hConsoleKey,
hTitleKey,
CONSOLE_REGISTRY_WINDOWSIZE,
REG_DWORD,
reinterpret_cast<BYTE*>(&dwValue),
static_cast<DWORD>(sizeof(dwValue)));
if (NT_SUCCESS(Status))
{
const COORD coordScreenBufferSize = gci.GetActiveOutputBuffer().GetBufferSize().Dimensions();
auto screenBufferWidth = coordScreenBufferSize.X;
auto screenBufferHeight = coordScreenBufferSize.Y;
dwValue = MAKELONG(screenBufferWidth, screenBufferHeight);
Status = RegistrySerialization::s_UpdateValue(hConsoleKey,
hTitleKey,
CONSOLE_REGISTRY_BUFFERSIZE,
REG_DWORD,
reinterpret_cast<BYTE*>(&dwValue),
static_cast<DWORD>(sizeof(dwValue)));
if (NT_SUCCESS(Status))
{
// Save window position
if (fAutoPos)
{
Status = RegistrySerialization::s_DeleteValue(hTitleKey, CONSOLE_REGISTRY_WINDOWPOS);
}
else
{
dwValue = MAKELONG(windowRect.left, windowRect.top);
Status = RegistrySerialization::s_UpdateValue(hConsoleKey,
hTitleKey,
CONSOLE_REGISTRY_WINDOWPOS,
REG_DWORD,
reinterpret_cast<BYTE*>(&dwValue),
static_cast<DWORD>(sizeof(dwValue)));
}
}
}
if (hTitleKey != hConsoleKey)
{
RegCloseKey(hTitleKey);
}
RegCloseKey(hConsoleKey);
RegCloseKey(hCurrentUserKey);
}
return Status;
}
[[nodiscard]] LRESULT Window::s_RegPersistWindowOpacity(_In_ PCWSTR const pwszTitle, const Window* const pWindow)
{
HKEY hCurrentUserKey, hConsoleKey, hTitleKey;
// Open the current user registry key.
NTSTATUS Status = RegistrySerialization::s_OpenCurrentUserConsoleTitleKey(pwszTitle, &hCurrentUserKey, &hConsoleKey, &hTitleKey);
if (NT_SUCCESS(Status))
{
// Save window opacity
DWORD dwValue;
dwValue = pWindow->GetWindowOpacity();
Status = RegistrySerialization::s_UpdateValue(hConsoleKey,
hTitleKey,
CONSOLE_REGISTRY_WINDOWALPHA,
REG_DWORD,
reinterpret_cast<BYTE*>(&dwValue),
static_cast<DWORD>(sizeof(dwValue)));
if (hTitleKey != hConsoleKey)
{
RegCloseKey(hTitleKey);
}
RegCloseKey(hConsoleKey);
RegCloseKey(hCurrentUserKey);
}
return Status;
}
// Routine Description:
// - Creates/retrieves a handle to the UI Automation provider COM interfaces
// Arguments:
// - <none>
// Return Value:
// - Pointer to UI Automation provider class/interfaces.
IRawElementProviderSimple* Window::_GetUiaProvider()
{
if (nullptr == _pUiaProvider)
{
LOG_IF_FAILED(WRL::MakeAndInitialize<WindowUiaProvider>(&_pUiaProvider, this));
}
return _pUiaProvider.Get();
}
[[nodiscard]] HRESULT Window::SignalUia(_In_ EVENTID id)
{
if (_pUiaProvider != nullptr)
{
return _pUiaProvider->Signal(id);
}
return S_FALSE;
}
[[nodiscard]] HRESULT Window::UiaSetTextAreaFocus()
{
if (_pUiaProvider != nullptr)
{
LOG_IF_FAILED(_pUiaProvider->SetTextAreaFocus());
return S_OK;
}
return S_FALSE;
}
void Window::SetOwner()
{
SetConsoleWindowOwner(_hWnd, nullptr);
}
BOOL Window::GetCursorPosition(_Out_ LPPOINT lpPoint)
{
return GetCursorPos(lpPoint);
}
BOOL Window::GetClientRectangle(_Out_ LPRECT lpRect)
{
return GetClientRect(_hWnd, lpRect);
}
int Window::MapPoints(_Inout_updates_(cPoints) LPPOINT lpPoints, _In_ UINT cPoints)
{
return MapWindowPoints(_hWnd, nullptr, lpPoints, cPoints);
}
BOOL Window::ConvertScreenToClient(_Inout_ LPPOINT lpPoint)
{
return ScreenToClient(_hWnd, lpPoint);
}