diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 43c0a70293..24ba784327 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -13,7 +13,6 @@ // - ulSize - The height of the cursor within this buffer Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept : _parentBuffer{ parentBuffer }, - _fHasMoved(false), _fIsVisible(true), _fIsOn(true), _fIsDouble(false), @@ -35,11 +34,6 @@ til::point Cursor::GetPosition() const noexcept return _cPosition; } -bool Cursor::HasMoved() const noexcept -{ - return _fHasMoved; -} - bool Cursor::IsVisible() const noexcept { return _fIsVisible; @@ -75,11 +69,6 @@ ULONG Cursor::GetSize() const noexcept return _ulSize; } -void Cursor::SetHasMoved(const bool fHasMoved) noexcept -{ - _fHasMoved = fHasMoved; -} - void Cursor::SetIsVisible(const bool fIsVisible) noexcept { _fIsVisible = fIsVisible; @@ -249,7 +238,6 @@ void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept // We shouldn't copy the position as it will be already rearranged by the resize operation. //_cPosition = pOtherCursor->_cPosition; - _fHasMoved = OtherCursor._fHasMoved; _fIsVisible = OtherCursor._fIsVisible; _fIsOn = OtherCursor._fIsOn; _fIsDouble = OtherCursor._fIsDouble; diff --git a/src/buffer/out/cursor.h b/src/buffer/out/cursor.h index d66762c1b8..d2697eeb69 100644 --- a/src/buffer/out/cursor.h +++ b/src/buffer/out/cursor.h @@ -38,7 +38,6 @@ public: Cursor(Cursor&&) = default; Cursor& operator=(Cursor&&) & = delete; - bool HasMoved() const noexcept; bool IsVisible() const noexcept; bool IsOn() const noexcept; bool IsBlinkingAllowed() const noexcept; @@ -54,7 +53,6 @@ public: bool IsDeferDrawing() noexcept; void EndDeferDrawing() noexcept; - void SetHasMoved(const bool fHasMoved) noexcept; void SetIsVisible(const bool fIsVisible) noexcept; void SetIsOn(const bool fIsOn) noexcept; void SetBlinkingAllowed(const bool fIsOn) noexcept; @@ -90,7 +88,6 @@ private: til::point _cPosition; // current position on screen (in screen buffer coords). - bool _fHasMoved; bool _fIsVisible; // whether cursor is visible (set only through the API) bool _fIsOn; // whether blinking cursor is on or not bool _fIsDouble; // whether the cursor size should be doubled diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 73974e85da..17eb814ad1 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -154,7 +154,6 @@ public: void UseMainScreenBuffer() override; bool IsVtInputEnabled() const noexcept override; - void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; void NotifyBufferRotation(const int delta) override; void NotifyShellIntegrationMark() override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 825712028e..9a4fa85efc 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -347,11 +347,6 @@ bool Terminal::IsVtInputEnabled() const noexcept return false; } -void Terminal::NotifyAccessibilityChange(const til::rect& /*changedRect*/) noexcept -{ - // This is only needed in conhost. Terminal handles accessibility in another way. -} - void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) { if (_pfnCompletionsChanged) diff --git a/src/host/AccessibilityNotifier.cpp b/src/host/AccessibilityNotifier.cpp new file mode 100644 index 0000000000..5d5fd0da59 --- /dev/null +++ b/src/host/AccessibilityNotifier.cpp @@ -0,0 +1,511 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "AccessibilityNotifier.h" + +#include "../interactivity/inc/ServiceLocator.hpp" + +using namespace Microsoft::Console; +using namespace Microsoft::Console::Interactivity; + +template +constexpr U satcast(T v) noexcept +{ + static_assert(sizeof(U) <= sizeof(T), "use this for narrowing"); + constexpr T min = std::numeric_limits::min(); + constexpr T max = std::numeric_limits::max(); + return gsl::narrow_cast(v < min ? min : (v > max ? max : v)); +} + +AccessibilityNotifier::AccessibilityNotifier() +{ + // Mirrors _timerEmitMSAA / _timerEmitUIA + memset(&_state, 0, sizeof(_state)); +} + +AccessibilityNotifier::~AccessibilityNotifier() +{ + SetUIAProvider(nullptr); +} + +void AccessibilityNotifier::Initialize(HWND hwnd, DWORD msaaDelay, DWORD uiaDelay) noexcept +{ + _hwnd = hwnd; + + // delay=INFINITE is intended to disable events completely, but realistically, + // even a delay of 1s makes little sense. So, the cut-off was set to 10s. + if (msaaDelay < 10000 && hwnd) + { + _msaaEnabled = true; + + // msaaDelay=0 makes all events synchronous. That's how + // it used to work and has a huge performance impact. + if (msaaDelay != 0) + { + // Convert from milliseconds to 100-nanosecond intervals. + // Negative values indicate relative time. + _msaaDelay = static_cast(msaaDelay) * -10000; + } + } + + if (uiaDelay < 10000) + { + _uiaEnabled = true; + + if (uiaDelay != 0) + { + _uiaDelay = static_cast(uiaDelay) * -10000; + } + } + + if (_msaaDelay || _uiaDelay) + { + _timer.reset(_createTimer(&_timerEmitMSAA)); + } + + // Triggers the computation of _delay and _delayWindow. + SetUIAProvider(nullptr); +} + +void AccessibilityNotifier::SetUIAProvider(IRawElementProviderSimple* provider) noexcept +{ + // NOTE: The assumption is that you're holding the console lock when calling any of the member functions. + // This is why we can safely update these members (no worker thread is running nor can be scheduled). + assert(ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked()); + + // If UIA events are disabled, don't set _uiaProvider either. + // It would trigger unnecessary work. + if (!_uiaEnabled) + { + return; + } + + // Of course we must ensure our precious provider object doesn't go away. + if (provider) + { + provider->AddRef(); + } + + const auto old = _uiaProvider.exchange(provider, std::memory_order_relaxed); + + // Before we can release the old object, we must ensure it's not in use by a worker thread. + WaitForThreadpoolTimerCallbacks(_timer.get(), TRUE); + + if (old) + { + old->Release(); + } + + // Update the delay. If UIA is enabled now, use the UIA delay. + // Note that a delay of 0 means "no delay" and we signal that as _delay=nullptr. + // + // NOTE: We don't set a second timer just for UIA, because some applications like NVDA + // listen to both MSAA and UIA events. If they don't arrive approximately together, + // they'll be announced as seperate events, which breaks announcements. + if (const auto delay = provider ? &_uiaDelay : &_msaaDelay; *delay == 0) + { + _delay = nullptr; + _delayWindow = 0; + } + else + { + static_assert(sizeof(FILETIME) == sizeof(_uiaDelay)); + _delay = reinterpret_cast(delay); + // Set the delay window to 1/5th of the delay, but in milliseconds. + _delayWindow = gsl::narrow_cast(std::max(0, *delay / (5 * -10000))); + } + + // If we canceled the timer, reschedule it. + if (_state.timerScheduled) + { + _state.timerScheduled = false; + + // Of course there's no point to schedule it if there isn't a provider. + if (provider) + { + _timerSet(); + } + } +} + +// Emits EVENT_CONSOLE_CARET, indicating the new cursor position. +// `rectangle` is the cursor rectangle in buffer coordinates (rows/columns) +// `flags` can be either CONSOLE_CARET_SELECTION _or_ CONSOLE_CARET_VISIBLE (not a bitfield) +// +// +// It then also Calls ConsoleControl() with ConsoleSetCaretInfo, which goes through the kernel sets +// cciConsole on the HWND and then raises EVENT_OBJECT_LOCATIONCHANGE with OBJID_CARET, INDEXID_CONTAINER. +// The cciConsole information is then used by GetGUIThreadInfo() to populate hwndCaret and rcCaret. +// Unfortunately there's no way to know whether anyone even needs this information so we always raise this. +void AccessibilityNotifier::CursorChanged(til::point position, bool activeSelection) noexcept +{ + // Can't check for IsWinEventHookInstalled(EVENT_CONSOLE_CARET), + // because we need to emit a ConsoleControl() call regardless. + if (_msaaEnabled) + { + const auto guard = _lock.lock_exclusive(); + + _state.eventConsoleCaretPositionX = position.x; + _state.eventConsoleCaretPositionY = position.y; + _state.eventConsoleCaretSelecting = activeSelection; + _state.eventConsoleCaretPrimed = true; + + _timerSet(); + } +} + +void AccessibilityNotifier::SelectionChanged() noexcept +{ + if (_uiaProvider.load(std::memory_order_relaxed)) + { + const auto guard = _lock.lock_exclusive(); + + _state.textSelectionChanged = true; + + _timerSet(); + } +} + +// Emits EVENT_CONSOLE_UPDATE_REGION, the region of the console that changed. +void AccessibilityNotifier::RegionChanged(til::point begin, til::point end) noexcept +{ + if (begin >= end) + { + return; + } + + const auto msaa = _msaaEnabled && IsWinEventHookInstalled(EVENT_CONSOLE_UPDATE_REGION); + const auto uia = _uiaProvider.load(std::memory_order_relaxed) != nullptr; + + if (!msaa && !uia) + { + return; + } + + const auto guard = _lock.lock_exclusive(); + + if (msaa) + { + const til::HugeCoordType begX = begin.x; + const til::HugeCoordType begY = begin.y; + const til::HugeCoordType endX = end.x; + const til::HugeCoordType endY = end.y; + + const auto primed = _state.eventConsoleUpdateRegionPrimed; + + // Initialize the region (if !primed) or extend the region to the union of old and new. + if (!primed || begY < _state.eventConsoleUpdateRegionBeginY || (begY == _state.eventConsoleUpdateRegionBeginY && begX < _state.eventConsoleUpdateRegionBeginX)) + { + _state.eventConsoleUpdateRegionBeginX = begX; + _state.eventConsoleUpdateRegionBeginY = begY; + _state.eventConsoleUpdateRegionPrimed = true; + } + if (!primed || endY > _state.eventConsoleUpdateRegionEndY || (endY == _state.eventConsoleUpdateRegionEndY && endX > _state.eventConsoleUpdateRegionEndX)) + { + _state.eventConsoleUpdateRegionEndX = endX; + _state.eventConsoleUpdateRegionEndY = endY; + _state.eventConsoleUpdateRegionPrimed = true; + } + } + + if (uia) + { + _state.textChanged = true; + } + + _timerSet(); +} + +// Emits EVENT_CONSOLE_UPDATE_SCROLL. Specific to buffer scrolls and +// allows us to adjust previously cached buffer coordinates accordingly. +void AccessibilityNotifier::ScrollBuffer(til::CoordType delta) noexcept +{ + if (_msaaEnabled && IsWinEventHookInstalled(EVENT_CONSOLE_UPDATE_SCROLL)) + { + const auto guard = _lock.lock_exclusive(); + + // They say accessibility is hard, but then they design EVENT_CONSOLE_UPDATE_SCROLL + // to count _both_ viewport scrolls _and_ buffer scrolls as the same thing, + // making the information carried by the event completely useless. Don't ask me. + // + // Fun fact: conhost "v2" (Windows 10+) would raise EVENT_CONSOLE_UPDATE_SCROLL events every + // time ScrollConsoleScreenBuffer is called. People ask me why I'm balding. They don't know. + _state.eventConsoleUpdateScrollDeltaY += delta; + _state.eventConsoleUpdateScrollPrimed = true; + + if (_state.eventConsoleCaretPrimed) + { + _state.eventConsoleCaretPositionY += delta; + } + + if (_state.eventConsoleUpdateRegionPrimed) + { + _state.eventConsoleUpdateRegionBeginY += delta; + _state.eventConsoleUpdateRegionEndY += delta; + } + + _timerSet(); + } +} + +// Emits EVENT_CONSOLE_UPDATE_SCROLL. Specific to viewport scrolls. +void AccessibilityNotifier::ScrollViewport(til::point delta) noexcept +{ + if (_msaaEnabled && IsWinEventHookInstalled(EVENT_CONSOLE_UPDATE_SCROLL)) + { + const auto guard = _lock.lock_exclusive(); + + _state.eventConsoleUpdateScrollDeltaX += delta.x; + _state.eventConsoleUpdateScrollDeltaY += delta.y; + _state.eventConsoleUpdateScrollPrimed = true; + + _timerSet(); + } +} + +// Emits EVENT_CONSOLE_LAYOUT. Documentation just states "The console layout has changed." +// but it's absolutely unclear what that even means. Try to emit it when the scrollbar +// position or window size has changed... I guess. +void AccessibilityNotifier::Layout() noexcept +{ + if (_msaaEnabled && IsWinEventHookInstalled(EVENT_CONSOLE_LAYOUT)) + { + const auto guard = _lock.lock_exclusive(); + + _state.eventConsoleLayoutPrimed = true; + + _timerSet(); + } +} + +void AccessibilityNotifier::ApplicationStart(DWORD pid) const noexcept +{ + if (_msaaEnabled) + { + const auto cc = ServiceLocator::LocateConsoleControl(); + cc->NotifyWinEvent(EVENT_CONSOLE_START_APPLICATION, _hwnd, pid, 0); + } +} + +void AccessibilityNotifier::ApplicationEnd(DWORD pid) const noexcept +{ + if (_msaaEnabled) + { + const auto cc = ServiceLocator::LocateConsoleControl(); + cc->NotifyWinEvent(EVENT_CONSOLE_END_APPLICATION, _hwnd, pid, 0); + } +} + +PTP_TIMER AccessibilityNotifier::_createTimer(PTP_TIMER_CALLBACK callback) noexcept +{ + return THROW_LAST_ERROR_IF_NULL(CreateThreadpoolTimer(callback, this, nullptr)); +} + +void AccessibilityNotifier::_timerSet() noexcept +{ + if (!_delay) + { + _emitMSAA(_state); + } + else if (!_state.timerScheduled) + { + _state.timerScheduled = true; + SetThreadpoolTimerEx(_timer.get(), _delay, 0, _delayWindow); + } +} + +void AccessibilityNotifier::_timerEmitMSAA(PTP_CALLBACK_INSTANCE, PVOID context, PTP_TIMER) noexcept +{ + const auto self = static_cast(context); + State state; + + // Make a copy of _state, because UIA and MSAA are very slow (up to 1ms per call). + // Holding a lock while _emitEventsCallback would mean that the IO thread can't proceed. + // + // The only concern I have is whether calling SetThreadpoolTimerEx() again on + // _timer while we're still executing will properly schedule another run. + // The docs say to read the "Remarks" and the remarks just don't clarify it. Great. + // FWIW we can't just create two timer objects since that may (theoretically) + // just end up with two callbacks running at the same time = same problem. + { + const auto guard = self->_lock.lock_exclusive(); + + // What we want to do is + // state = self->_state; + // self->_state = {}; + // MSVC optimizes the first line with SIMD, but fails to do so for the second line. + // This forces us to use memset. memcpy is used for consistency. + static_assert(std::is_trivially_copyable_v); + memcpy(&state, &self->_state, sizeof(State)); + memset(&self->_state, 0, sizeof(State)); + } + + self->_emitMSAA(state); +} + +void AccessibilityNotifier::_emitMSAA(State& state) const noexcept +{ + const auto cc = ServiceLocator::LocateConsoleControl(); + const auto provider = _uiaProvider.load(std::memory_order_relaxed); + + if (state.eventConsoleCaretPrimed) + { + const auto x = satcast(state.eventConsoleCaretPositionX); + const auto y = satcast(state.eventConsoleCaretPositionY); + // Technically, CONSOLE_CARET_SELECTION and CONSOLE_CARET_VISIBLE are bitflags, + // however Microsoft's _own_ example code for these assumes that they're an + // enumation and also assumes that a value of 0 (= invisible cursor) is invalid. + // So, we just pretend as if the cursor is always visible. + const auto flags = state.eventConsoleCaretSelecting ? CONSOLE_CARET_SELECTION : CONSOLE_CARET_VISIBLE; + + // There's no need to check for IsWinEventHookInstalled, + // because NotifyWinEvent is very fast if no event is installed. + cc->NotifyWinEvent(EVENT_CONSOLE_CARET, _hwnd, flags, MAKELONG(x, y)); + + { + std::optional caretInfo; + + // Convert the buffer position to the equivalent screen coordinates + // required by CONSOLE_CARET_INFO, taking line rendition into account. + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.LockConsole(); + if (gci.HasActiveOutputBuffer()) + { + auto& screenInfo = gci.GetActiveOutputBuffer(); + auto& buffer = screenInfo.GetTextBuffer(); + const auto position = buffer.BufferToScreenPosition({ x, y }); + const auto viewport = screenInfo.GetViewport(); + const auto fontSize = screenInfo.GetScreenFontSize(); + const auto left = (position.x - viewport.Left()) * fontSize.width; + const auto top = (position.y - viewport.Top()) * fontSize.height; + caretInfo.emplace(CONSOLE_CARET_INFO{ + .hwnd = _hwnd, + .rc = RECT{ + left, + top, + left + fontSize.width, + top + fontSize.height, + }, + }); + } + gci.UnlockConsole(); + + if (caretInfo) + { + cc->Control(ControlType::ConsoleSetCaretInfo, &*caretInfo, sizeof(*caretInfo)); + } + } + + state.eventConsoleCaretPositionX = 0; + state.eventConsoleCaretPositionY = 0; + state.eventConsoleCaretSelecting = false; + state.eventConsoleCaretPrimed = false; + } + + if (state.eventConsoleUpdateRegionPrimed) + { + const auto begX = satcast(state.eventConsoleUpdateRegionBeginX); + const auto begY = satcast(state.eventConsoleUpdateRegionBeginY); + const auto endX = satcast(state.eventConsoleUpdateRegionEndX); + const auto endY = satcast(state.eventConsoleUpdateRegionEndY); + const auto beg = MAKELONG(begX, begY); + const auto end = MAKELONG(endX, endY); + + // Previously, we'd also emit a EVENT_CONSOLE_UPDATE_SIMPLE event for single-char updates, + // but in the 30 years since, the way fast software is written has changed: + // We now have plenty CPU power but the speed of light is still the same. + // It's much more important to batch events to avoid NotifyWinEvent's latency problems. + // EVENT_CONSOLE_UPDATE_SIMPLE is not trivially batchable and so it got removed. + // + // That said, NVDA is currently a very popular screen reader for Windows. + // IF you set its "Windows Console support" to "Legacy" AND disable + // "Use enhanced typed character support in legacy Windows Console when available" + // then it will purely rely on these WinEvents for accessibility. + // + // In this case it assumes that EVENT_CONSOLE_UPDATE_REGION is regular output + // and that EVENT_CONSOLE_UPDATE_SIMPLE is keyboard input (FYI: don't do this). + // The problem now is that it doesn't announce any EVENT_CONSOLE_UPDATE_REGION + // events where beg == end (i.e. a single character change). + // + // The good news is that if you set these two options in NVDA, it crashes whenever + // any conhost instance exits, so... maybe we don't need to work around this? :D + // + // I'll leave this code here, in case we ever need to shim EVENT_CONSOLE_UPDATE_SIMPLE. +#if 0 + LONG charAndAttr = 0; + + if (beg == end) + { + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.LockConsole(); + + if (gci.HasActiveOutputBuffer()) + { + auto& tb = gci.GetActiveOutputBuffer().GetTextBuffer(); + const auto& row = tb.GetRowByOffset(begY); + const auto glyph = row.GlyphAt(begX); + const auto attr = row.GetAttrByColumn(begX); + charAndAttr = MAKELONG(Utf16ToUcs2(glyph), attr.GetLegacyAttributes()); + } + + gci.UnlockConsole(); + } + + if (charAndAttr) + { + cc->NotifyWinEvent(EVENT_CONSOLE_UPDATE_SIMPLE, _hwnd, beg, charAndAttr); + } + else + { + cc->NotifyWinEvent(EVENT_CONSOLE_UPDATE_REGION, _hwnd, beg, end); + } +#else + cc->NotifyWinEvent(EVENT_CONSOLE_UPDATE_REGION, _hwnd, beg, end); +#endif + + state.eventConsoleUpdateRegionBeginX = 0; + state.eventConsoleUpdateRegionBeginY = 0; + state.eventConsoleUpdateRegionEndX = 0; + state.eventConsoleUpdateRegionEndY = 0; + state.eventConsoleUpdateRegionPrimed = false; + } + + if (state.eventConsoleUpdateScrollPrimed) + { + const auto dx = satcast(state.eventConsoleUpdateScrollDeltaX); + const auto dy = satcast(state.eventConsoleUpdateScrollDeltaY); + + cc->NotifyWinEvent(EVENT_CONSOLE_UPDATE_SCROLL, _hwnd, dx, dy); + + state.eventConsoleUpdateScrollDeltaX = 0; + state.eventConsoleUpdateScrollDeltaY = 0; + state.eventConsoleUpdateScrollPrimed = false; + } + + if (state.eventConsoleLayoutPrimed) + { + cc->NotifyWinEvent(EVENT_CONSOLE_LAYOUT, _hwnd, 0, 0); + state.eventConsoleLayoutPrimed = false; + } + + if (state.textSelectionChanged) + { + _emitUIAEvent(provider, UIA_Text_TextSelectionChangedEventId); + state.textSelectionChanged = false; + } + + if (state.textChanged) + { + _emitUIAEvent(provider, UIA_Text_TextChangedEventId); + state.textChanged = false; + } +} + +void AccessibilityNotifier::_emitUIAEvent(IRawElementProviderSimple* provider, EVENTID id) noexcept +{ + if (provider) + { + LOG_IF_FAILED(UiaRaiseAutomationEvent(provider, id)); + } +} diff --git a/src/host/AccessibilityNotifier.h b/src/host/AccessibilityNotifier.h new file mode 100644 index 0000000000..4aab4e9bff --- /dev/null +++ b/src/host/AccessibilityNotifier.h @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +struct IRawElementProviderSimple; +typedef int EVENTID; + +namespace Microsoft::Console +{ + // UIA and MSAA calls are both extraordinarily slow, so we got this + // handy class to batch then up and emit them on a background thread. + struct AccessibilityNotifier + { + AccessibilityNotifier(); + ~AccessibilityNotifier(); + + AccessibilityNotifier(const AccessibilityNotifier&) = delete; + AccessibilityNotifier& operator=(const AccessibilityNotifier&) = delete; + + // NOTE: It is assumed that you're holding the console lock when calling any of the member functions. + // This class uses mutexes, but those are only for synchronizing with the worker threads. + + void Initialize(HWND hwnd, DWORD msaaDelay, DWORD uiaDelay) noexcept; + void SetUIAProvider(IRawElementProviderSimple* provider) noexcept; + + void CursorChanged(til::point position, bool activeSelection) noexcept; + void SelectionChanged() noexcept; + void RegionChanged(til::point begin, til::point end) noexcept; + void ScrollBuffer(til::CoordType delta) noexcept; + void ScrollViewport(til::point delta) noexcept; + void Layout() noexcept; + void ApplicationStart(DWORD pid) const noexcept; + void ApplicationEnd(DWORD pid) const noexcept; + + private: + // !!! NOTE !!! + // * _emitEventsCallback assumes that this struct can be quickly initialized with memset(0). + // * Members are intentionally left undefined so that we can create instances on the stack without initialization. + struct State + { + // EVENT_CONSOLE_CARET / ConsoleControl(ConsoleSetCaretInfo) + til::HugeCoordType eventConsoleCaretPositionX; + til::HugeCoordType eventConsoleCaretPositionY; + bool eventConsoleCaretSelecting; + bool eventConsoleCaretPrimed; + + // EVENT_CONSOLE_UPDATE_REGION + til::HugeCoordType eventConsoleUpdateRegionBeginX; + til::HugeCoordType eventConsoleUpdateRegionBeginY; + til::HugeCoordType eventConsoleUpdateRegionEndX; + til::HugeCoordType eventConsoleUpdateRegionEndY; + bool eventConsoleUpdateRegionPrimed; + + // EVENT_CONSOLE_UPDATE_SCROLL + til::HugeCoordType eventConsoleUpdateScrollDeltaX; + til::HugeCoordType eventConsoleUpdateScrollDeltaY; + bool eventConsoleUpdateScrollPrimed; + + // EVENT_CONSOLE_LAYOUT + bool eventConsoleLayoutPrimed; + + // UIA + bool textSelectionChanged; // UIA_Text_TextSelectionChangedEventId + bool textChanged; // UIA_Text_TextChangedEventId + + bool timerScheduled; + }; + + PTP_TIMER _createTimer(PTP_TIMER_CALLBACK callback) noexcept; + void _timerSet() noexcept; + static void _timerEmitMSAA(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_TIMER timer) noexcept; + void _emitMSAA(State& msaa) const noexcept; + static void _emitUIAEvent(IRawElementProviderSimple* provider, EVENTID id) noexcept; + + // The main window, used for NotifyWinEvent / ConsoleControl(ConsoleSetCaretInfo) calls. + HWND _hwnd = nullptr; + // The current UIA provider, if any. + std::atomic _uiaProvider{ nullptr }; + // The timer object used to schedule debounced a11y events. + // It's null if the delay is set to 0. + wil::unique_threadpool_timer _timer; + // The delay to use for MSAA/UIA events, in filetime units (100ns units). + // The value will be negative because that's what SetThreadpoolTimerEx needs. + int64_t _msaaDelay = 0; + int64_t _uiaDelay = 0; + // Depending on whether we have a UIA provider or not, this points to either _msaaDelay or _uiaDelay. + FILETIME* _delay = nullptr; + // The delay window to use for SetThreadpoolTimerEx, in milliseconds. + DWORD _delayWindow = 0; + // Whether MSAA and UIA are enabled. + bool _msaaEnabled = false; + bool _uiaEnabled = false; + + // _lock protects access to _state. + wil::srwlock _lock; + State _state; + }; +} diff --git a/src/host/CursorBlinker.cpp b/src/host/CursorBlinker.cpp index 68250b5f42..992bcec9d8 100644 --- a/src/host/CursorBlinker.cpp +++ b/src/host/CursorBlinker.cpp @@ -81,50 +81,12 @@ void CursorBlinker::TimerRoutine(SCREEN_INFORMATION& ScreenInfo) const noexcept auto& buffer = ScreenInfo.GetTextBuffer(); auto& cursor = buffer.GetCursor(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - auto* const pAccessibilityNotifier = ServiceLocator::LocateAccessibilityNotifier(); if (!WI_IsFlagSet(gci.Flags, CONSOLE_HAS_FOCUS)) { goto DoScroll; } - // Update the cursor pos in USER so accessibility will work. - // Don't do all this work or send events if we don't have a notifier target. - if (pAccessibilityNotifier && cursor.HasMoved()) - { - // Convert the buffer position to the equivalent screen coordinates - // required by the notifier, taking line rendition into account. - const auto position = buffer.BufferToScreenPosition(cursor.GetPosition()); - const auto viewport = ScreenInfo.GetViewport(); - const auto fontSize = ScreenInfo.GetScreenFontSize(); - cursor.SetHasMoved(false); - - til::rect rc; - rc.left = (position.x - viewport.Left()) * fontSize.width; - rc.top = (position.y - viewport.Top()) * fontSize.height; - rc.right = rc.left + fontSize.width; - rc.bottom = rc.top + fontSize.height; - - pAccessibilityNotifier->NotifyConsoleCaretEvent(rc); - - // Send accessibility information - { - auto flags = IAccessibilityNotifier::ConsoleCaretEventFlags::CaretInvisible; - - // Flags is expected to be 2, 1, or 0. 2 in selecting (whether or not visible), 1 if just visible, 0 if invisible/noselect. - if (WI_IsFlagSet(gci.Flags, CONSOLE_SELECTING)) - { - flags = IAccessibilityNotifier::ConsoleCaretEventFlags::CaretSelection; - } - else if (cursor.IsVisible()) - { - flags = IAccessibilityNotifier::ConsoleCaretEventFlags::CaretVisible; - } - - pAccessibilityNotifier->NotifyConsoleCaretEvent(flags, MAKELONG(position.x, position.y)); - } - } - // If the DelayCursor flag has been set, wait one more tick before toggle. // This is used to guarantee the cursor is on for a finite period of time // after a move and off for a finite period of time after a WriteString. diff --git a/src/host/_output.cpp b/src/host/_output.cpp index fda7ae2792..83482c5eb4 100644 --- a/src/host/_output.cpp +++ b/src/host/_output.cpp @@ -253,12 +253,13 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon ImageSlice::EraseCells(screenInfo.GetTextBuffer(), startingCoordinate, result.cellsModified); } - if (screenBuffer.HasAccessibilityEventing()) { // Notify accessibility auto endingCoordinate = startingCoordinate; bufferSize.WalkInBounds(endingCoordinate, result.cellsModified); - screenBuffer.NotifyAccessibilityEventing(startingCoordinate.x, startingCoordinate.y, endingCoordinate.x, endingCoordinate.y); + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.RegionChanged(startingCoordinate, endingCoordinate); } return result; diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index f337d6560e..bb558470cc 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -31,6 +31,39 @@ constexpr bool controlCharPredicate(wchar_t wch) return wch < L' ' || wch == 0x007F; } +static auto raiseAccessibilityEventsOnExit(SCREEN_INFORMATION& screenInfo) +{ + const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + const auto& bufferBefore = gci.GetActiveOutputBuffer(); + const auto cursorBefore = bufferBefore.GetTextBuffer().GetCursor().GetPosition(); + + auto raise = wil::scope_exit([&bufferBefore, cursorBefore] { + // !!! NOTE !!! `bufferBefore` may now be a stale pointer, because VT + // sequences can switch between the main and alternative screen buffer. + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + const auto& bufferAfter = gci.GetActiveOutputBuffer(); + const auto cursorAfter = bufferAfter.GetTextBuffer().GetCursor().GetPosition(); + + if (&bufferBefore == &bufferAfter) + { + an.RegionChanged(cursorBefore, cursorAfter); + } + if (cursorBefore != cursorAfter) + { + an.CursorChanged(cursorAfter, false); + } + }); + + // Don't raise any events for inactive buffers. + if (&bufferBefore != &screenInfo) + { + raise.release(); + } + + return raise; +} + // Routine Description: // - This routine updates the cursor position. Its input is the non-special // cased new location of the cursor. For example, if the cursor were being @@ -76,12 +109,13 @@ static void AdjustCursorPosition(SCREEN_INFORMATION& screenInfo, _In_ til::point auto& buffer = screenInfo.GetTextBuffer(); buffer.IncrementCircularBuffer(buffer.GetCurrentAttributes()); + // TODO: This is very bad for performance. + // Track the total scroll offset as a int64 in buffer --> No need to track it here anymore. if (buffer.IsActiveBuffer()) { - if (const auto notifier = ServiceLocator::LocateAccessibilityNotifier()) - { - notifier->NotifyConsoleUpdateScrollEvent(0, -1); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.ScrollBuffer(-1); + if (const auto renderer = ServiceLocator::LocateGlobals().pRender) { static constexpr til::point delta{ 0, -1 }; @@ -104,7 +138,6 @@ static void AdjustCursorPosition(SCREEN_INFORMATION& screenInfo, _In_ til::point static bool _writeCharsLegacyUnprocessed(SCREEN_INFORMATION& screenInfo, const std::wstring_view& text, til::CoordType* psScrollY) { const auto wrapAtEOL = WI_IsFlagSet(screenInfo.OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT); - const auto hasAccessibilityEventing = screenInfo.HasAccessibilityEventing(); auto& textBuffer = screenInfo.GetTextBuffer(); bool wrapped = false; @@ -127,11 +160,6 @@ static bool _writeCharsLegacyUnprocessed(SCREEN_INFORMATION& screenInfo, const s textBuffer.SetWrapForced(cursorPosition.y, true); } - if (hasAccessibilityEventing && state.columnEnd > state.columnBegin) - { - screenInfo.NotifyAccessibilityEventing(state.columnBegin, cursorPosition.y, state.columnEnd - 1, cursorPosition.y); - } - AdjustCursorPosition(screenInfo, cursorPosition, psScrollY); } @@ -148,6 +176,7 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t auto& textBuffer = screenInfo.GetTextBuffer(); const auto width = textBuffer.GetSize().Width(); auto& cursor = textBuffer.GetCursor(); + const auto cursorPosBefore = cursor.GetPosition(); const auto wrapAtEOL = WI_IsFlagSet(screenInfo.OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT); const auto beg = text.begin(); const auto end = text.end(); @@ -156,6 +185,7 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto a11y = raiseAccessibilityEventsOnExit(screenInfo); const auto snap = screenInfo.SnapOnOutput(); // If we enter this if condition, then someone wrote text in VT mode and now switched to non-VT mode. @@ -343,6 +373,7 @@ void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str) // may change, so get the VtIo reference now, just in case. auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto a11y = raiseAccessibilityEventsOnExit(screenInfo); const auto snap = screenInfo.SnapOnOutput(); stateMachine.ProcessString(str); diff --git a/src/host/ft_uia/VirtualTerminalTests.cs b/src/host/ft_uia/VirtualTerminalTests.cs index 6210e29736..95519161eb 100644 --- a/src/host/ft_uia/VirtualTerminalTests.cs +++ b/src/host/ft_uia/VirtualTerminalTests.cs @@ -188,7 +188,7 @@ namespace Conhost.UIA.Tests Log.Comment("---Status Request Commands---"); Globals.WaitForTimeout(); app.UIRoot.SendKeys("c"); - string expectedTitle = string.Format("Response Received: {0}", "\x1b[?1;0c"); + string expectedTitle = string.Format("Response Received: {0}", "\x1b[?61;4;6;7;14;21;22;23;24;28;32;42;52c"); Globals.WaitForTimeout(); string title = app.GetWindowTitle(); diff --git a/src/host/getset.cpp b/src/host/getset.cpp index b8109bca71..3568037bc5 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -796,6 +796,8 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont // how the cmd shell's CLS command resets the buffer. buffer.UpdateBottom(); + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.CursorChanged(buffer.GetTextBuffer().GetCursor().GetPosition(), false); return S_OK; } CATCH_RETURN(); diff --git a/src/host/globals.h b/src/host/globals.h index 1fbb9da808..d23c265887 100644 --- a/src/host/globals.h +++ b/src/host/globals.h @@ -17,19 +17,18 @@ Revision History: #pragma once +#include "AccessibilityNotifier.h" +#include "ApiRoutines.h" +#include "ConsoleArguments.hpp" #include "selection.hpp" #include "server.h" -#include "ConsoleArguments.hpp" -#include "ApiRoutines.h" #include "../propslib/DelegationConfig.hpp" #include "../renderer/base/Renderer.hpp" #include "../server/DeviceComm.h" #include "../tsf/Handle.h" -#include "../server/ConDrvDeviceComm.h" #include -#include TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider); class Globals @@ -63,6 +62,7 @@ public: std::vector WordDelimiters; Microsoft::Console::Render::Renderer* pRender; Microsoft::Console::TSF::Handle tsf; + Microsoft::Console::AccessibilityNotifier accessibilityNotifier; Microsoft::Console::Render::IFontDefaultList* pFontDefaultList; bool IsHeadless() const; diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 9e51ab38c8..1b60faafd9 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -47,6 +47,7 @@ + @@ -95,6 +96,7 @@ + diff --git a/src/host/input.cpp b/src/host/input.cpp index ee09771de0..a924c7dae2 100644 --- a/src/host/input.cpp +++ b/src/host/input.cpp @@ -355,6 +355,6 @@ void ProcessCtrlEvents() // The bad news is that EndTask() returns STATUS_UNSUCCESSFUL no matter whether // the process was already dead, or if the request actually failed for some reason. // Hopefully there aren't any regressions, but we can't know without trying. - LOG_IF_NTSTATUS_FAILED(ctrl->EndTask(r.dwProcessID, EventType, CtrlFlags)); + ctrl->EndTask(r.dwProcessID, EventType, CtrlFlags); } } diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index a2e17a06cd..255188cae8 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -147,6 +147,9 @@ Source Files + + Source Files + @@ -287,6 +290,9 @@ Header Files + + Header Files + diff --git a/src/host/output.cpp b/src/host/output.cpp index 729821c841..4f5c2c94c7 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -5,10 +5,6 @@ #include "_output.h" #include "output.h" -#include "handle.h" - -#include "getset.h" -#include "misc.h" #include "../interactivity/inc/ServiceLocator.hpp" #include "../types/inc/Viewport.hpp" @@ -283,17 +279,8 @@ void ScreenBufferSizeChange(const til::size coordNewSize) // - source - The viewport describing the region where data was copied from // - fill - The viewport describing the area that was filled in with the fill character (uncovered area) // - target - The viewport describing the region where data was copied to -static void _ScrollScreen(SCREEN_INFORMATION& screenInfo, const Viewport& source, const Viewport& fill, const Viewport& target) +static void _ScrollScreen(SCREEN_INFORMATION& screenInfo, const Viewport& fill, const Viewport& target) { - if (screenInfo.IsActiveScreenBuffer()) - { - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier != nullptr) - { - pNotifier->NotifyConsoleUpdateScrollEvent(target.Origin().x - source.Left(), target.Origin().y - source.RightInclusive()); - } - } - // Get the text buffer and send it commands. // It will figure out whether or not we're active and where the messages need to go. auto& textBuffer = screenInfo.GetTextBuffer(); @@ -413,7 +400,7 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo, _CopyRectangle(screenInfo, source, target.Origin()); // Notify the renderer and accessibility as to what moved and where. - _ScrollScreen(screenInfo, source, fill, target); + _ScrollScreen(screenInfo, fill, target); } // ------ 6. FILL ------ diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 5ef852065f..8960a99467 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -408,42 +408,14 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } -// Routine Description: -// - Lets accessibility apps know when an area of the screen has changed. -// Arguments: -// - changedRect - the area that has changed. -// Return value: -// - -void ConhostInternalGetSet::NotifyAccessibilityChange(const til::rect& changedRect) -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - if (screenInfo.HasAccessibilityEventing() && changedRect) - { - screenInfo.NotifyAccessibilityEventing( - changedRect.left, - changedRect.top, - changedRect.right - 1, - changedRect.bottom - 1); - } -} - // Routine Description: // - Implements conhost-specific behavior when the buffer is rotated. // Arguments: // - delta - the number of cycles that the buffer has rotated. // Return value: // - -void ConhostInternalGetSet::NotifyBufferRotation(const int delta) +void ConhostInternalGetSet::NotifyBufferRotation(const int) { - auto& screenInfo = _io.GetActiveOutputBuffer(); - if (screenInfo.IsActiveScreenBuffer()) - { - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier) - { - pNotifier->NotifyConsoleUpdateScrollEvent(0, -delta); - } - } } void ConhostInternalGetSet::NotifyShellIntegrationMark() diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index cfb1c85f16..c80df20ffe 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -65,7 +65,6 @@ public: bool IsVtInputEnabled() const override; - void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; void NotifyShellIntegrationMark() override; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 136ccc4f1f..083aeb4ee4 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -7,7 +7,8 @@ #include "output.h" #include "../interactivity/inc/ServiceLocator.hpp" #include "../types/inc/CodepointWidthDetector.hpp" -#include "../types/inc/convert.hpp" +#include "../terminal/adapter/adaptDispatch.hpp" +#include "../terminal/parser/OutputStateMachineEngine.hpp" using namespace Microsoft::Console; using namespace Microsoft::Console::Types; @@ -19,7 +20,6 @@ using namespace Microsoft::Console::VirtualTerminal; SCREEN_INFORMATION::SCREEN_INFORMATION( _In_ IWindowMetrics* pMetrics, - _In_ IAccessibilityNotifier* pNotifier, const TextAttribute popupAttributes, const FontInfo fontInfo) : OutputMode{ ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT }, @@ -31,7 +31,6 @@ SCREEN_INFORMATION::SCREEN_INFORMATION( FillOutDbcsLeadChar{ 0 }, ScrollScale{ 1ul }, _pConsoleWindowMetrics{ pMetrics }, - _pAccessibilityNotifier{ pNotifier }, _api{ *this }, _stateMachine{ nullptr }, _viewport(Viewport::Empty()), @@ -89,11 +88,10 @@ SCREEN_INFORMATION::~SCREEN_INFORMATION() auto pMetrics = ServiceLocator::LocateWindowMetrics(); THROW_HR_IF_NULL(E_FAIL, pMetrics); - const auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); // It is possible for pNotifier to be null and that's OK. // For instance, the PTY doesn't need to send events. Just pass it along // and be sure that `SCREEN_INFORMATION` bypasses all event work if it's not there. - const auto pScreen = new SCREEN_INFORMATION(pMetrics, pNotifier, popupAttributes, fontInfo); + const auto pScreen = new SCREEN_INFORMATION(pMetrics, popupAttributes, fontInfo); // Set up viewport pScreen->_viewport = Viewport::FromDimensions({ 0, 0 }, @@ -572,70 +570,6 @@ void SCREEN_INFORMATION::UpdateFont(const FontInfo* const pfiNewFont) } } -// Routine Description: -// - Informs clients whether we have accessibility eventing so they can -// save themselves the work of performing math or lookups before calling -// `NotifyAccessibilityEventing`. -// Arguments: -// - -// Return Value: -// - True if we have an accessibility listener. False otherwise. -bool SCREEN_INFORMATION::HasAccessibilityEventing() const noexcept -{ - return _pAccessibilityNotifier; -} - -// NOTE: This method was historically used to notify accessibility apps AND -// to aggregate drawing metadata to determine whether or not to use PolyTextOut. -// After the Nov 2015 graphics refactor, the metadata drawing flag calculation is no longer necessary. -// This now only notifies accessibility apps of a change. -void SCREEN_INFORMATION::NotifyAccessibilityEventing(const til::CoordType sStartX, - const til::CoordType sStartY, - const til::CoordType sEndX, - const til::CoordType sEndY) -{ - if (!_pAccessibilityNotifier) - { - return; - } - - // Fire off a winevent to let accessibility apps know what changed. - if (IsActiveScreenBuffer()) - { - const auto coordScreenBufferSize = GetBufferSize().Dimensions(); - FAIL_FAST_IF(!(sEndX < coordScreenBufferSize.width)); - - if (sStartX == sEndX && sStartY == sEndY) - { - try - { - const auto cellData = GetCellDataAt({ sStartX, sStartY }); - const auto charAndAttr = MAKELONG(Utf16ToUcs2(cellData->Chars()), - cellData->TextAttr().GetLegacyAttributes()); - _pAccessibilityNotifier->NotifyConsoleUpdateSimpleEvent(MAKELONG(sStartX, sStartY), - charAndAttr); - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); - return; - } - } - else - { - _pAccessibilityNotifier->NotifyConsoleUpdateRegionEvent(MAKELONG(sStartX, sStartY), - MAKELONG(sEndX, sEndY)); - } - auto pConsoleWindow = ServiceLocator::LocateConsoleWindow(); - if (pConsoleWindow) - { - LOG_IF_FAILED(pConsoleWindow->SignalUia(UIA_Text_TextChangedEventId)); - // TODO MSFT 7960168 do we really need this event to not signal? - //pConsoleWindow->SignalUia(UIA_LayoutInvalidatedEventId); - } - } -} - #pragma endregion #pragma region UI_Refresh @@ -663,10 +597,7 @@ SCREEN_INFORMATION::ScrollBarState SCREEN_INFORMATION::FetchScrollBarState() WI_ClearFlag(gci.Flags, CONSOLE_UPDATING_SCROLL_BARS); // Fire off an event to let accessibility apps know the layout has changed. - if (_pAccessibilityNotifier) - { - _pAccessibilityNotifier->NotifyConsoleLayoutEvent(); - } + ServiceLocator::LocateGlobals().accessibilityNotifier.Layout(); const auto buffer = GetBufferSize(); const auto isAltBuffer = _IsAltBuffer(); @@ -1481,15 +1412,12 @@ NT_CATCH_RETURN() if (SUCCEEDED_NTSTATUS(status)) { - if (HasAccessibilityEventing()) - { - NotifyAccessibilityEventing(0, 0, coordNewScreenSize.width - 1, coordNewScreenSize.height - 1); - } - // Fire off an event to let accessibility apps know the layout has changed. - if (_pAccessibilityNotifier && IsActiveScreenBuffer()) + if (IsActiveScreenBuffer()) { - _pAccessibilityNotifier->NotifyConsoleLayoutEvent(); + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.RegionChanged({}, { coordNewScreenSize.width - 1, coordNewScreenSize.height - 1 }); + an.Layout(); } if (fDoScrollBarUpdate) @@ -1665,7 +1593,6 @@ void SCREEN_INFORMATION::SetCursorDBMode(const bool DoubleCursor) { cursor.SetDelay(true); } - cursor.SetHasMoved(true); } return STATUS_SUCCESS; diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index d713bfed60..15f7ad6518 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -19,8 +19,6 @@ Revision History: #pragma once -#include "conapi.h" -#include "settings.hpp" #include "outputStream.hpp" #include "../buffer/out/OutputCellRect.hpp" @@ -30,15 +28,10 @@ Revision History: #include "../buffer/out/textBufferTextIterator.hpp" #include "IIoProvider.hpp" -#include "outputStream.hpp" -#include "../terminal/adapter/adaptDispatch.hpp" #include "../terminal/parser/stateMachine.hpp" -#include "../terminal/parser/OutputStateMachineEngine.hpp" #include "../server/ObjectHeader.h" -#include "../interactivity/inc/IAccessibilityNotifier.hpp" -#include "../interactivity/inc/IConsoleWindow.hpp" #include "../interactivity/inc/IWindowMetrics.hpp" #include "../renderer/inc/FontInfo.hpp" @@ -110,9 +103,6 @@ public: [[nodiscard]] NTSTATUS ResizeScreenBuffer(const til::size coordNewScreenSize, const bool fDoScrollBarUpdate); - bool HasAccessibilityEventing() const noexcept; - void NotifyAccessibilityEventing(const til::CoordType sStartX, const til::CoordType sStartY, const til::CoordType sEndX, const til::CoordType sEndY); - struct ScrollBarState { til::size maxSize; @@ -236,12 +226,10 @@ public: private: SCREEN_INFORMATION(_In_ Microsoft::Console::Interactivity::IWindowMetrics* pMetrics, - _In_ Microsoft::Console::Interactivity::IAccessibilityNotifier* pNotifier, const TextAttribute popupAttributes, const FontInfo fontInfo); Microsoft::Console::Interactivity::IWindowMetrics* _pConsoleWindowMetrics; - Microsoft::Console::Interactivity::IAccessibilityNotifier* _pAccessibilityNotifier; [[nodiscard]] HRESULT _AdjustScreenBufferHelper(const til::rect* const prcClientNew, const til::size coordBufferOld, diff --git a/src/host/selection.cpp b/src/host/selection.cpp index fd815e082a..c938a89bc5 100644 --- a/src/host/selection.cpp +++ b/src/host/selection.cpp @@ -3,7 +3,6 @@ #include "precomp.h" -#include "_output.h" #include "stream.h" #include "scrolling.hpp" @@ -121,10 +120,9 @@ void Selection::_SetSelectionVisibility(const bool fMakeVisible) _PaintSelection(); } - if (const auto window = ServiceLocator::LocateConsoleWindow()) - { - LOG_IF_FAILED(window->SignalUia(UIA_Text_TextSelectionChangedEventId)); - } + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.SelectionChanged(); } // Routine Description: @@ -179,15 +177,14 @@ void Selection::InitializeMouseSelection(const til::point coordBufferPos) if (pWindow != nullptr) { pWindow->UpdateWindowText(); - LOG_IF_FAILED(pWindow->SignalUia(UIA_Text_TextSelectionChangedEventId)); + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.SelectionChanged(); } // Fire off an event to let accessibility apps know the selection has changed. - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier) - { - pNotifier->NotifyConsoleCaretEvent(IAccessibilityNotifier::ConsoleCaretEventFlags::CaretSelection, PACKCOORD(coordBufferPos)); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.CursorChanged(coordBufferPos, true); } // Routine Description: @@ -303,14 +300,9 @@ void Selection::_ExtendSelection(Selection::SelectionData* d, _In_ til::point co _PaintSelection(); // Fire off an event to let accessibility apps know the selection has changed. - if (const auto pNotifier = ServiceLocator::LocateAccessibilityNotifier()) - { - pNotifier->NotifyConsoleCaretEvent(IAccessibilityNotifier::ConsoleCaretEventFlags::CaretSelection, PACKCOORD(coordBufferPos)); - } - if (const auto window = ServiceLocator::LocateConsoleWindow()) - { - LOG_IF_FAILED(window->SignalUia(UIA_Text_TextSelectionChangedEventId)); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.CursorChanged(coordBufferPos, true); + an.SelectionChanged(); } // Routine Description: @@ -338,7 +330,9 @@ void Selection::_CancelMouseSelection() } // Mark the cursor position as changed so we'll fire off a win event. - ScreenInfo.GetTextBuffer().GetCursor().SetHasMoved(true); + // NOTE(lhecker): Why is this the only cancel function that would raise a WinEvent? Makes no sense to me. + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.CursorChanged(ScreenInfo.GetTextBuffer().GetCursor().GetPosition(), false); } // Routine Description: @@ -402,10 +396,9 @@ void Selection::ClearSelection(const bool fStartingNewSelection) { _CancelMarkSelection(); } - if (const auto window = ServiceLocator::LocateConsoleWindow()) - { - LOG_IF_FAILED(window->SignalUia(UIA_Text_TextSelectionChangedEventId)); - } + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.SelectionChanged(); auto d{ _d.write() }; wil::hide_name _d; @@ -523,7 +516,9 @@ void Selection::InitializeMarkSelection() if (pWindow != nullptr) { pWindow->UpdateWindowText(); - LOG_IF_FAILED(pWindow->SignalUia(UIA_Text_TextSelectionChangedEventId)); + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.SelectionChanged(); } } diff --git a/src/host/selection.hpp b/src/host/selection.hpp index 7ff876929f..7006291cd0 100644 --- a/src/host/selection.hpp +++ b/src/host/selection.hpp @@ -20,7 +20,6 @@ Revision History: #include "input.h" -#include "../interactivity/inc/IAccessibilityNotifier.hpp" #include "../interactivity/inc/IConsoleWindow.hpp" #include "til/generational.h" diff --git a/src/host/selectionInput.cpp b/src/host/selectionInput.cpp index d7f08d28bf..f7bcb3e637 100644 --- a/src/host/selectionInput.cpp +++ b/src/host/selectionInput.cpp @@ -4,9 +4,7 @@ #include "precomp.h" #include "../buffer/out/search.h" - #include "../interactivity/inc/ServiceLocator.hpp" -#include "../types/inc/convert.hpp" using namespace Microsoft::Console::Types; using Microsoft::Console::Interactivity::ServiceLocator; @@ -903,11 +901,13 @@ bool Selection::_HandleMarkModeSelectionNav(const INPUT_KEY_INFO* const pInputKe d->fUseAlternateSelection = false; } - cursor.SetHasMoved(true); d->coordSelectionAnchor = textBuffer.GetCursor().GetPosition(); ScreenInfo.MakeCursorVisible(d->coordSelectionAnchor); d->srSelectionRect.left = d->srSelectionRect.right = d->coordSelectionAnchor.x; d->srSelectionRect.top = d->srSelectionRect.bottom = d->coordSelectionAnchor.y; + + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.CursorChanged(d->coordSelectionAnchor, true); } return true; } diff --git a/src/host/selectionState.cpp b/src/host/selectionState.cpp index c74e21dddd..5ce107dea5 100644 --- a/src/host/selectionState.cpp +++ b/src/host/selectionState.cpp @@ -4,7 +4,6 @@ #include "precomp.h" #include "../interactivity/inc/ServiceLocator.hpp" - #include "../types/inc/viewport.hpp" using namespace Microsoft::Console::Types; diff --git a/src/host/settings.cpp b/src/host/settings.cpp index 4650fbf9eb..c194d9b69e 100644 --- a/src/host/settings.cpp +++ b/src/host/settings.cpp @@ -777,6 +777,16 @@ std::wstring_view Settings::GetAnswerbackMessage() const noexcept return _answerbackMessage; } +DWORD Settings::GetMSAADelay() const noexcept +{ + return _msaaDelay; +} + +DWORD Settings::GetUIADelay() const noexcept +{ + return _uiaDelay; +} + // Determines whether our primary renderer should be DirectX or GDI. // This is based on user preference and velocity hold back state. bool Settings::GetUseDx() const noexcept diff --git a/src/host/settings.hpp b/src/host/settings.hpp index 240872f9cc..48c5cc2979 100644 --- a/src/host/settings.hpp +++ b/src/host/settings.hpp @@ -179,6 +179,8 @@ public: std::wstring_view GetAnswerbackMessage() const noexcept; + DWORD GetMSAADelay() const noexcept; + DWORD GetUIADelay() const noexcept; bool GetUseDx() const noexcept; bool GetCopyColor() const noexcept; SettingsTextMeasurementMode GetTextMeasurementMode() const noexcept; @@ -225,6 +227,8 @@ private: std::wstring _LaunchFaceName; bool _fAllowAltF4Close; DWORD _dwVirtTermLevel; + DWORD _msaaDelay = 100; + DWORD _uiaDelay = 25; SettingsTextMeasurementMode _textMeasurement = SettingsTextMeasurementMode::Graphemes; bool _fUseDx; bool _fCopyColor; @@ -238,7 +242,7 @@ private: bool _fInterceptCopyPaste; - bool _TerminalScrolling; + bool _TerminalScrolling = true; WCHAR _answerbackMessage[32] = {}; friend class RegistrySerialization; }; diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 49551a25bd..cdb49db1bd 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -11,6 +11,7 @@ #include "../interactivity/base/ApiDetector.hpp" #include "../interactivity/base/RemoteConsoleControl.hpp" #include "../interactivity/inc/ServiceLocator.hpp" +#include "../server/ConDrvDeviceComm.h" #include "../server/DeviceHandle.h" #include "../server/IoSorter.h" #include "../types/inc/CodepointWidthDetector.hpp" @@ -73,17 +74,6 @@ try Globals.defaultTerminalMarkerCheckRequired = true; } - // Create the accessibility notifier early in the startup process. - // Only create if we're not in PTY mode. - // The notifiers use expensive legacy MSAA events and the PTY isn't even responsible - // for the terminal user interface, so we should set ourselves up to skip all - // those notifications and the mathematical calculations required to send those events - // for performance reasons. - if (!args->InConptyMode()) - { - RETURN_IF_FAILED(ServiceLocator::CreateAccessibilityNotifier()); - } - // Removed allocation of scroll buffer here. return S_OK; } diff --git a/src/host/stream.cpp b/src/host/stream.cpp index d97d629b0d..4092b8b75f 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -3,17 +3,11 @@ #include "precomp.h" -#include "_stream.h" #include "stream.h" - #include "handle.h" #include "misc.h" #include "readDataRaw.hpp" -#include "ApiRoutines.h" - -#include "../types/inc/GlyphWidth.hpp" - #include "../interactivity/inc/ServiceLocator.hpp" using Microsoft::Console::Interactivity::ServiceLocator; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 1ee380d70e..ac994cb79c 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -363,9 +363,6 @@ void TextBufferTests::TestCopyProperties() VERIFY_IS_NOT_NULL(testTextBuffer.get()); // set initial mapping values - testTextBuffer->GetCursor().SetHasMoved(false); - otherTbi.GetCursor().SetHasMoved(true); - testTextBuffer->GetCursor().SetIsVisible(false); otherTbi.GetCursor().SetIsVisible(true); @@ -382,7 +379,6 @@ void TextBufferTests::TestCopyProperties() testTextBuffer->CopyProperties(otherTbi); // test that new now contains values from other - VERIFY_IS_TRUE(testTextBuffer->GetCursor().HasMoved()); VERIFY_IS_TRUE(testTextBuffer->GetCursor().IsVisible()); VERIFY_IS_TRUE(testTextBuffer->GetCursor().IsOn()); VERIFY_IS_TRUE(testTextBuffer->GetCursor().IsDouble()); diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 95eed69167..4ac3bd7cfc 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -9,6 +9,10 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" inline constexpr CoordType CoordTypeMin = INT32_MIN; inline constexpr CoordType CoordTypeMax = INT32_MAX; + using HugeCoordType = int64_t; + inline constexpr HugeCoordType HugeCoordTypeMin = INT64_MIN; + inline constexpr HugeCoordType HugeCoordTypeMax = INT64_MAX; + namespace details { template @@ -249,6 +253,15 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" }; } + constexpr COORD unwrap_coord_clamped(const point pt) noexcept + { + constexpr short min = -32768; + constexpr short max = 32767; + const auto x = pt.x < min ? min : (pt.x > max ? max : gsl::narrow_cast(pt.x)); + const auto y = pt.y < min ? min : (pt.y > max ? max : gsl::narrow_cast(pt.y)); + return { x, y }; + } + constexpr HRESULT unwrap_coord_hr(const point pt, COORD& out) noexcept { short x = 0; diff --git a/src/interactivity/base/HostSignalInputThread.cpp b/src/interactivity/base/HostSignalInputThread.cpp index 9ea9cefff5..3bb58d2338 100644 --- a/src/interactivity/base/HostSignalInputThread.cpp +++ b/src/interactivity/base/HostSignalInputThread.cpp @@ -86,10 +86,8 @@ T HostSignalInputThread::_ReceiveTypedPacket() { case HostSignals::NotifyApp: { - auto msg = _ReceiveTypedPacket(); - - LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->NotifyConsoleApplication(msg.processId)); - + const auto msg = _ReceiveTypedPacket(); + ServiceLocator::LocateConsoleControl()->NotifyConsoleApplication(msg.processId); break; } case HostSignals::SetForeground: @@ -104,10 +102,8 @@ T HostSignalInputThread::_ReceiveTypedPacket() } case HostSignals::EndTask: { - auto msg = _ReceiveTypedPacket(); - - LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->EndTask(msg.processId, msg.eventType, msg.ctrlFlags)); - + const auto msg = _ReceiveTypedPacket(); + ServiceLocator::LocateConsoleControl()->EndTask(msg.processId, msg.eventType, msg.ctrlFlags); break; } default: diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index ecc5c2bc18..6838c2943a 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -17,7 +17,6 @@ #include "..\onecore\WindowMetrics.hpp" #endif -#include "../win32/AccessibilityNotifier.hpp" #include "../win32/ConsoleControl.hpp" #include "../win32/ConsoleInputThread.hpp" #include "../win32/WindowDpiApi.hpp" @@ -199,48 +198,6 @@ using namespace Microsoft::Console::Interactivity; return status; } -[[nodiscard]] NTSTATUS InteractivityFactory::CreateAccessibilityNotifier(_Inout_ std::unique_ptr& notifier) -{ - auto status = STATUS_SUCCESS; - - ApiLevel level; - status = ApiDetector::DetectNtUserWindow(&level); - - if (SUCCEEDED_NTSTATUS(status)) - { - std::unique_ptr newNotifier; - try - { - switch (level) - { - case ApiLevel::Win32: - newNotifier = std::make_unique(); - break; - -#ifdef BUILD_ONECORE_INTERACTIVITY - case ApiLevel::OneCore: - newNotifier = std::make_unique(); - break; -#endif - default: - status = STATUS_INVALID_LEVEL; - break; - } - } - catch (...) - { - status = NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); - } - - if (SUCCEEDED_NTSTATUS(status)) - { - notifier.swap(newNotifier); - } - } - - return status; -} - [[nodiscard]] NTSTATUS InteractivityFactory::CreateSystemConfigurationProvider(_Inout_ std::unique_ptr& provider) { auto status = STATUS_SUCCESS; diff --git a/src/interactivity/base/InteractivityFactory.hpp b/src/interactivity/base/InteractivityFactory.hpp index 7e9b912e85..46f74ea568 100644 --- a/src/interactivity/base/InteractivityFactory.hpp +++ b/src/interactivity/base/InteractivityFactory.hpp @@ -24,7 +24,6 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] NTSTATUS CreateHighDpiApi(_Inout_ std::unique_ptr& api); [[nodiscard]] NTSTATUS CreateWindowMetrics(_Inout_ std::unique_ptr& metrics); - [[nodiscard]] NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr& notifier); [[nodiscard]] NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr& provider); [[nodiscard]] NTSTATUS CreatePseudoWindow(HWND& hwnd); diff --git a/src/interactivity/base/RemoteConsoleControl.cpp b/src/interactivity/base/RemoteConsoleControl.cpp index a93c8dc003..5687fefba1 100644 --- a/src/interactivity/base/RemoteConsoleControl.cpp +++ b/src/interactivity/base/RemoteConsoleControl.cpp @@ -14,10 +14,20 @@ RemoteConsoleControl::RemoteConsoleControl(HANDLE signalPipe) : { } +void RemoteConsoleControl::Control(ControlType, PVOID, DWORD) noexcept +{ + WI_ASSERT_FAIL(); +} + +void RemoteConsoleControl::NotifyWinEvent(DWORD, HWND, LONG, LONG) noexcept +{ + WI_ASSERT_FAIL(); +} + #pragma region IConsoleControl Members template -[[nodiscard]] NTSTATUS _SendTypedPacket(HANDLE pipe, ::Microsoft::Console::HostSignals signalCode, T& payload) +void _SendTypedPacket(HANDLE pipe, ::Microsoft::Console::HostSignals signalCode, T& payload) noexcept { // To ensure it's a happy wire format, pack it tight at 1. #pragma pack(push, 1) @@ -33,21 +43,10 @@ template packet.data = payload; DWORD bytesWritten = 0; - if (!WriteFile(pipe, &packet, sizeof(packet), &bytesWritten, nullptr)) - { - const auto gle = ::GetLastError(); - NT_RETURN_NTSTATUS(static_cast(NTSTATUS_FROM_WIN32(gle))); - } - - if (bytesWritten != sizeof(packet)) - { - NT_RETURN_NTSTATUS(static_cast(NTSTATUS_FROM_WIN32(E_UNEXPECTED))); - } - - return STATUS_SUCCESS; + LOG_IF_WIN32_BOOL_FALSE(WriteFile(pipe, &packet, sizeof(packet), &bytesWritten, nullptr)); } -[[nodiscard]] NTSTATUS RemoteConsoleControl::NotifyConsoleApplication(_In_ DWORD dwProcessId) +void RemoteConsoleControl::NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept { HostSignalNotifyAppData data{}; data.sizeInBytes = sizeof(data); @@ -56,15 +55,15 @@ template return _SendTypedPacket(_pipe.get(), HostSignals::NotifyApp, data); } -[[nodiscard]] NTSTATUS RemoteConsoleControl::SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) +void RemoteConsoleControl::SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept { // GH#13211 - Apparently this API doesn't need to be forwarded to conhost at // all. Instead, just perform the ConsoleControl operation here, in proc. // This lets us avoid all sorts of strange handle duplicating weirdness. - return _control.SetForeground(hProcess, fForeground); + _control.SetForeground(hProcess, fForeground); } -[[nodiscard]] NTSTATUS RemoteConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) +void RemoteConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) noexcept { HostSignalEndTaskData data{}; data.sizeInBytes = sizeof(data); @@ -75,11 +74,11 @@ template return _SendTypedPacket(_pipe.get(), HostSignals::EndTask, data); } -[[nodiscard]] NTSTATUS RemoteConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) +void RemoteConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept { // This call doesn't need to get forwarded to the root conhost. Just handle // it in-proc, to set the owner of OpenConsole - return _control.SetWindowOwner(hwnd, processId, threadId); + _control.SetWindowOwner(hwnd, processId, threadId); } #pragma endregion diff --git a/src/interactivity/base/RemoteConsoleControl.hpp b/src/interactivity/base/RemoteConsoleControl.hpp index 902052678e..07d9df09f2 100644 --- a/src/interactivity/base/RemoteConsoleControl.hpp +++ b/src/interactivity/base/RemoteConsoleControl.hpp @@ -24,10 +24,12 @@ namespace Microsoft::Console::Interactivity RemoteConsoleControl(HANDLE signalPipe); // IConsoleControl Members - [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId); - [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground); - [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags); - [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId); + void Control(ControlType command, PVOID ptr, DWORD len) noexcept override; + void NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept override; + void NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept override; + void SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept override; + void EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) noexcept override; + void SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; private: wil::unique_handle _pipe; diff --git a/src/interactivity/base/ServiceLocator.cpp b/src/interactivity/base/ServiceLocator.cpp index 3e882022e7..3b9b8b1015 100644 --- a/src/interactivity/base/ServiceLocator.cpp +++ b/src/interactivity/base/ServiceLocator.cpp @@ -23,7 +23,6 @@ std::unique_ptr ServiceLocator::s_consoleControl; std::unique_ptr ServiceLocator::s_consoleInputThread; std::unique_ptr ServiceLocator::s_consoleWindow; std::unique_ptr ServiceLocator::s_windowMetrics; -std::unique_ptr ServiceLocator::s_accessibilityNotifier; std::unique_ptr ServiceLocator::s_highDpiApi; std::unique_ptr ServiceLocator::s_systemConfigurationProvider; void (*ServiceLocator::s_oneCoreTeardownFunction)() = nullptr; @@ -137,24 +136,6 @@ void ServiceLocator::RundownAndExit(const HRESULT hr) return status; } -[[nodiscard]] HRESULT ServiceLocator::CreateAccessibilityNotifier() -{ - // Can't create if we've already created. - if (s_accessibilityNotifier) - { - return E_UNEXPECTED; - } - - if (!s_interactivityFactory) - { - RETURN_IF_NTSTATUS_FAILED(ServiceLocator::LoadInteractivityFactory()); - } - - RETURN_IF_NTSTATUS_FAILED(s_interactivityFactory->CreateAccessibilityNotifier(s_accessibilityNotifier)); - - return S_OK; -} - #pragma endregion #pragma region Set Methods @@ -277,11 +258,6 @@ IWindowMetrics* ServiceLocator::LocateWindowMetrics() return s_windowMetrics.get(); } -IAccessibilityNotifier* ServiceLocator::LocateAccessibilityNotifier() -{ - return s_accessibilityNotifier.get(); -} - ISystemConfigurationProvider* ServiceLocator::LocateSystemConfigurationProvider() { auto status = STATUS_SUCCESS; diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj b/src/interactivity/base/lib/InteractivityBase.vcxproj index 7517d99f4f..29ba709227 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj @@ -34,7 +34,6 @@ - diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters index 3cad47ac50..c81a1b3be9 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters @@ -53,9 +53,6 @@ Header Files - - Header Files - Header Files @@ -101,5 +98,6 @@ + \ No newline at end of file diff --git a/src/interactivity/inc/IAccessibilityNotifier.hpp b/src/interactivity/inc/IAccessibilityNotifier.hpp deleted file mode 100644 index 786893dd65..0000000000 --- a/src/interactivity/inc/IAccessibilityNotifier.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- IAccessibilityNotifier.hpp - -Abstract: -- Defines accessibility notification methods used by accessibility systems to - provide accessible access to the console. - -Author(s): -- Hernan Gatta (HeGatta) 29-Mar-2017 ---*/ - -#pragma once - -namespace Microsoft::Console::Interactivity -{ - class IAccessibilityNotifier - { - public: - enum class ConsoleCaretEventFlags - { - CaretInvisible, - CaretSelection, - CaretVisible - }; - - virtual ~IAccessibilityNotifier() = default; - - virtual void NotifyConsoleCaretEvent(_In_ const til::rect& rectangle) = 0; - virtual void NotifyConsoleCaretEvent(_In_ ConsoleCaretEventFlags flags, _In_ LONG position) = 0; - virtual void NotifyConsoleUpdateScrollEvent(_In_ LONG x, _In_ LONG y) = 0; - virtual void NotifyConsoleUpdateSimpleEvent(_In_ LONG start, _In_ LONG charAndAttribute) = 0; - virtual void NotifyConsoleUpdateRegionEvent(_In_ LONG startXY, _In_ LONG endXY) = 0; - virtual void NotifyConsoleLayoutEvent() = 0; - virtual void NotifyConsoleStartApplicationEvent(_In_ DWORD processId) = 0; - virtual void NotifyConsoleEndApplicationEvent(_In_ DWORD processId) = 0; - }; -} diff --git a/src/interactivity/inc/IConsoleControl.hpp b/src/interactivity/inc/IConsoleControl.hpp index 66fccee661..ab10640119 100644 --- a/src/interactivity/inc/IConsoleControl.hpp +++ b/src/interactivity/inc/IConsoleControl.hpp @@ -17,13 +17,27 @@ Author(s): namespace Microsoft::Console::Interactivity { + enum class ControlType + { + ConsoleSetVDMCursorBounds, + ConsoleNotifyConsoleApplication, + ConsoleFullscreenSwitch, + ConsoleSetCaretInfo, + ConsoleSetReserveKeys, + ConsoleSetForeground, + ConsoleSetWindowOwner, + ConsoleEndTask, + }; + class IConsoleControl { public: virtual ~IConsoleControl() = default; - [[nodiscard]] virtual NTSTATUS NotifyConsoleApplication(DWORD dwProcessId) = 0; - [[nodiscard]] virtual NTSTATUS SetForeground(HANDLE hProcess, BOOL fForeground) = 0; - [[nodiscard]] virtual NTSTATUS EndTask(DWORD dwProcessId, DWORD dwEventType, ULONG ulCtrlFlags) = 0; - [[nodiscard]] virtual NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) = 0; + virtual void Control(ControlType command, PVOID ptr, DWORD len) noexcept = 0; + virtual void NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept = 0; + virtual void NotifyConsoleApplication(DWORD dwProcessId) noexcept = 0; + virtual void SetForeground(HANDLE hProcess, BOOL fForeground) noexcept = 0; + virtual void EndTask(DWORD dwProcessId, DWORD dwEventType, ULONG ulCtrlFlags) noexcept = 0; + virtual void SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept = 0; }; } diff --git a/src/interactivity/inc/IConsoleWindow.hpp b/src/interactivity/inc/IConsoleWindow.hpp index fba098ea78..40a5c718fe 100644 --- a/src/interactivity/inc/IConsoleWindow.hpp +++ b/src/interactivity/inc/IConsoleWindow.hpp @@ -58,7 +58,6 @@ namespace Microsoft::Console::Types virtual void VerticalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange) = 0; - [[nodiscard]] virtual HRESULT SignalUia(_In_ EVENTID id) = 0; [[nodiscard]] virtual HRESULT UiaSetTextAreaFocus() = 0; virtual til::rect GetWindowRect() const noexcept = 0; }; diff --git a/src/interactivity/inc/IInteractivityFactory.hpp b/src/interactivity/inc/IInteractivityFactory.hpp index 99fb20f282..2bc19be0d3 100644 --- a/src/interactivity/inc/IInteractivityFactory.hpp +++ b/src/interactivity/inc/IInteractivityFactory.hpp @@ -21,7 +21,6 @@ Author(s): #include "IHighDpiApi.hpp" #include "IWindowMetrics.hpp" -#include "IAccessibilityNotifier.hpp" #include "ISystemConfigurationProvider.hpp" #include @@ -37,7 +36,6 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] virtual NTSTATUS CreateHighDpiApi(_Inout_ std::unique_ptr& api) = 0; [[nodiscard]] virtual NTSTATUS CreateWindowMetrics(_Inout_ std::unique_ptr& metrics) = 0; - [[nodiscard]] virtual NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr& notifier) = 0; [[nodiscard]] virtual NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr& provider) = 0; [[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd) = 0; diff --git a/src/interactivity/inc/ServiceLocator.hpp b/src/interactivity/inc/ServiceLocator.hpp index 5ed052ca3a..8b01ea9280 100644 --- a/src/interactivity/inc/ServiceLocator.hpp +++ b/src/interactivity/inc/ServiceLocator.hpp @@ -39,9 +39,6 @@ namespace Microsoft::Console::Interactivity // In case the on-demand creation fails, the return value // is nullptr and a message is logged. - [[nodiscard]] static HRESULT CreateAccessibilityNotifier(); - static IAccessibilityNotifier* LocateAccessibilityNotifier(); - [[nodiscard]] static NTSTATUS SetConsoleControlInstance(_In_ std::unique_ptr&& control); static IConsoleControl* LocateConsoleControl(); template @@ -96,7 +93,6 @@ namespace Microsoft::Console::Interactivity static std::unique_ptr s_interactivityFactory; - static std::unique_ptr s_accessibilityNotifier; static std::unique_ptr s_consoleControl; static std::unique_ptr s_consoleInputThread; // TODO: MSFT 15344939 - some implementations of IConsoleWindow are currently singleton diff --git a/src/interactivity/onecore/AccessibilityNotifier.cpp b/src/interactivity/onecore/AccessibilityNotifier.cpp deleted file mode 100644 index 743bd03e30..0000000000 --- a/src/interactivity/onecore/AccessibilityNotifier.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "AccessibilityNotifier.hpp" - -using namespace Microsoft::Console::Interactivity::OneCore; - -void AccessibilityNotifier::NotifyConsoleCaretEvent(_In_ const til::rect& /*rectangle*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleCaretEvent(_In_ ConsoleCaretEventFlags /*flags*/, _In_ LONG /*position*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleUpdateScrollEvent(_In_ LONG /*x*/, _In_ LONG /*y*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleUpdateSimpleEvent(_In_ LONG /*start*/, _In_ LONG /*charAndAttribute*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleUpdateRegionEvent(_In_ LONG /*startXY*/, _In_ LONG /*endXY*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleLayoutEvent() noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleStartApplicationEvent(_In_ DWORD /*processId*/) noexcept -{ -} - -void AccessibilityNotifier::NotifyConsoleEndApplicationEvent(_In_ DWORD /*processId*/) noexcept -{ -} diff --git a/src/interactivity/onecore/AccessibilityNotifier.hpp b/src/interactivity/onecore/AccessibilityNotifier.hpp deleted file mode 100644 index 90c044427f..0000000000 --- a/src/interactivity/onecore/AccessibilityNotifier.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- IAccessibilityNotifier.hpp - -Abstract: -- OneCore implementation of the IAccessibilityNotifier interface. - -Author(s): -- Hernan Gatta (HeGatta) 29-Mar-2017 ---*/ - -#pragma once - -#include "../inc/IAccessibilityNotifier.hpp" - -#pragma hdrstop - -namespace Microsoft::Console::Interactivity::OneCore -{ - class AccessibilityNotifier : public IAccessibilityNotifier - { - public: - void NotifyConsoleCaretEvent(_In_ const til::rect& rectangle) noexcept override; - void NotifyConsoleCaretEvent(_In_ ConsoleCaretEventFlags flags, _In_ LONG position) noexcept override; - void NotifyConsoleUpdateScrollEvent(_In_ LONG x, _In_ LONG y) noexcept override; - void NotifyConsoleUpdateSimpleEvent(_In_ LONG start, _In_ LONG charAndAttribute) noexcept override; - void NotifyConsoleUpdateRegionEvent(_In_ LONG startXY, _In_ LONG endXY) noexcept override; - void NotifyConsoleLayoutEvent() noexcept override; - void NotifyConsoleStartApplicationEvent(_In_ DWORD processId) noexcept override; - void NotifyConsoleEndApplicationEvent(_In_ DWORD processId) noexcept override; - }; -} diff --git a/src/interactivity/onecore/ConsoleControl.cpp b/src/interactivity/onecore/ConsoleControl.cpp index 7841bfbe19..80e3735836 100644 --- a/src/interactivity/onecore/ConsoleControl.cpp +++ b/src/interactivity/onecore/ConsoleControl.cpp @@ -12,6 +12,10 @@ using namespace Microsoft::Console::Interactivity::OneCore; #pragma region IConsoleControl Members +void ConsoleControl::NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept +{ +} + [[nodiscard]] NTSTATUS ConsoleControl::NotifyConsoleApplication(_In_ DWORD /*dwProcessId*/) noexcept { return STATUS_SUCCESS; diff --git a/src/interactivity/onecore/ConsoleControl.hpp b/src/interactivity/onecore/ConsoleControl.hpp index 4b917a2f15..7f28a16800 100644 --- a/src/interactivity/onecore/ConsoleControl.hpp +++ b/src/interactivity/onecore/ConsoleControl.hpp @@ -24,6 +24,7 @@ namespace Microsoft::Console::Interactivity::OneCore { public: // IConsoleControl Members + void NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept override; [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept override; [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept override; [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override; diff --git a/src/interactivity/onecore/ConsoleWindow.cpp b/src/interactivity/onecore/ConsoleWindow.cpp index 8d8fb9dcdc..936888fcc4 100644 --- a/src/interactivity/onecore/ConsoleWindow.cpp +++ b/src/interactivity/onecore/ConsoleWindow.cpp @@ -111,11 +111,6 @@ void ConsoleWindow::VerticalScroll(const WORD /*wScrollCommand*/, const WORD /*w { } -[[nodiscard]] HRESULT ConsoleWindow::SignalUia(_In_ EVENTID /*id*/) noexcept -{ - return E_NOTIMPL; -} - [[nodiscard]] HRESULT ConsoleWindow::UiaSetTextAreaFocus() noexcept { return E_NOTIMPL; diff --git a/src/interactivity/onecore/ConsoleWindow.hpp b/src/interactivity/onecore/ConsoleWindow.hpp index 530aafd13c..9719194dfc 100644 --- a/src/interactivity/onecore/ConsoleWindow.hpp +++ b/src/interactivity/onecore/ConsoleWindow.hpp @@ -51,7 +51,6 @@ namespace Microsoft::Console::Interactivity::OneCore void HorizontalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange) noexcept override; void VerticalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange) noexcept override; - [[nodiscard]] HRESULT SignalUia(_In_ EVENTID id) noexcept override; [[nodiscard]] HRESULT UiaSetTextAreaFocus() noexcept override; til::rect GetWindowRect() const noexcept override; }; diff --git a/src/interactivity/win32/AccessibilityNotifier.cpp b/src/interactivity/win32/AccessibilityNotifier.cpp deleted file mode 100644 index 015627b953..0000000000 --- a/src/interactivity/win32/AccessibilityNotifier.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "AccessibilityNotifier.hpp" - -#include "../inc/ServiceLocator.hpp" -#include "ConsoleControl.hpp" - -using namespace Microsoft::Console::Types; -using namespace Microsoft::Console::Interactivity::Win32; - -void AccessibilityNotifier::NotifyConsoleCaretEvent(_In_ const til::rect& rectangle) -{ - const auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow != nullptr) - { - CONSOLE_CARET_INFO caretInfo; - caretInfo.hwnd = pWindow->GetWindowHandle(); - caretInfo.rc = rectangle.to_win32_rect(); - - LOG_IF_FAILED(ServiceLocator::LocateConsoleControl()->Control(ConsoleControl::ControlType::ConsoleSetCaretInfo, - &caretInfo, - sizeof(caretInfo))); - } -} - -void AccessibilityNotifier::NotifyConsoleCaretEvent(_In_ ConsoleCaretEventFlags flags, _In_ LONG position) -{ - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - DWORD dwFlags = 0; - - if (flags == ConsoleCaretEventFlags::CaretSelection) - { - dwFlags = CONSOLE_CARET_SELECTION; - } - else if (flags == ConsoleCaretEventFlags::CaretVisible) - { - dwFlags = CONSOLE_CARET_VISIBLE; - } - - // UIA event notification - static til::point previousCursorLocation; - const auto pWindow = ServiceLocator::LocateConsoleWindow(); - - if (pWindow != nullptr) - { - NotifyWinEvent(EVENT_CONSOLE_CARET, - pWindow->GetWindowHandle(), - dwFlags, - position); - - const auto& screenInfo = gci.GetActiveOutputBuffer(); - const auto& cursor = screenInfo.GetTextBuffer().GetCursor(); - const auto currentCursorPosition = cursor.GetPosition(); - if (currentCursorPosition != previousCursorLocation) - { - LOG_IF_FAILED(pWindow->SignalUia(UIA_Text_TextSelectionChangedEventId)); - } - previousCursorLocation = currentCursorPosition; - } -} - -void AccessibilityNotifier::NotifyConsoleUpdateScrollEvent(_In_ LONG x, _In_ LONG y) -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_UPDATE_SCROLL, - pWindow->GetWindowHandle(), - x, - y); - } -} - -void AccessibilityNotifier::NotifyConsoleUpdateSimpleEvent(_In_ LONG start, _In_ LONG charAndAttribute) -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_UPDATE_SIMPLE, - pWindow->GetWindowHandle(), - start, - charAndAttribute); - } -} - -void AccessibilityNotifier::NotifyConsoleUpdateRegionEvent(_In_ LONG startXY, _In_ LONG endXY) -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_UPDATE_REGION, - pWindow->GetWindowHandle(), - startXY, - endXY); - } -} - -void AccessibilityNotifier::NotifyConsoleLayoutEvent() -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_LAYOUT, - pWindow->GetWindowHandle(), - 0, - 0); - } -} - -void AccessibilityNotifier::NotifyConsoleStartApplicationEvent(_In_ DWORD processId) -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_START_APPLICATION, - pWindow->GetWindowHandle(), - processId, - 0); - } -} - -void AccessibilityNotifier::NotifyConsoleEndApplicationEvent(_In_ DWORD processId) -{ - auto pWindow = ServiceLocator::LocateConsoleWindow(); - if (pWindow) - { - NotifyWinEvent(EVENT_CONSOLE_END_APPLICATION, - pWindow->GetWindowHandle(), - processId, - 0); - } -} diff --git a/src/interactivity/win32/AccessibilityNotifier.hpp b/src/interactivity/win32/AccessibilityNotifier.hpp deleted file mode 100644 index 115633c511..0000000000 --- a/src/interactivity/win32/AccessibilityNotifier.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- IAccessibilityNotifier.hpp - -Abstract: -- Win32 implementation of the IAccessibilityNotifier interface. - -Author(s): -- Hernan Gatta (HeGatta) 29-Mar-2017 ---*/ - -#pragma once - -#include "precomp.h" - -#include "../inc/IAccessibilityNotifier.hpp" - -#pragma hdrstop - -namespace Microsoft::Console::Interactivity::Win32 -{ - class AccessibilityNotifier final : public IAccessibilityNotifier - { - public: - ~AccessibilityNotifier() = default; - - void NotifyConsoleCaretEvent(_In_ const til::rect& rectangle); - void NotifyConsoleCaretEvent(_In_ ConsoleCaretEventFlags flags, _In_ LONG position); - void NotifyConsoleUpdateScrollEvent(_In_ LONG x, _In_ LONG y); - void NotifyConsoleUpdateSimpleEvent(_In_ LONG start, _In_ LONG charAndAttribute); - void NotifyConsoleUpdateRegionEvent(_In_ LONG startXY, _In_ LONG endXY); - void NotifyConsoleLayoutEvent(); - void NotifyConsoleStartApplicationEvent(_In_ DWORD processId); - void NotifyConsoleEndApplicationEvent(_In_ DWORD processId); - }; -} diff --git a/src/interactivity/win32/ConsoleControl.cpp b/src/interactivity/win32/ConsoleControl.cpp index 833f36b1cd..a5b4ba4131 100644 --- a/src/interactivity/win32/ConsoleControl.cpp +++ b/src/interactivity/win32/ConsoleControl.cpp @@ -13,31 +13,51 @@ using namespace Microsoft::Console::Interactivity::Win32; -#pragma region IConsoleControl Members +#ifdef CON_USERPRIVAPI_INDIRECT +ConsoleControl::ConsoleControl() +{ + // NOTE: GetModuleHandleW is rather expensive, but GetProcAddress is quite cheap. + const auto user32 = GetModuleHandleW(L"user32.dll"); + _consoleControl = reinterpret_cast(GetProcAddress(user32, "ConsoleControl")); + _enterReaderModeHelper = reinterpret_cast(GetProcAddress(user32, "EnterReaderModeHelper")); + _translateMessageEx = reinterpret_cast(GetProcAddress(user32, "TranslateMessageEx")); +} +#endif -[[nodiscard]] NTSTATUS ConsoleControl::NotifyConsoleApplication(_In_ DWORD dwProcessId) +void ConsoleControl::Control(ControlType command, PVOID ptr, DWORD len) noexcept +{ +#ifdef CON_USERPRIVAPI_INDIRECT + if (_consoleControl) + { + LOG_IF_FAILED(_consoleControl(command, ptr, len)); + } +#else + LOG_IF_FAILED(::ConsoleControl(command, ptr, len)); +#endif +} + +void ConsoleControl::NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept +{ + ::NotifyWinEvent(event, hwnd, idObject, idChild); +} + +void ConsoleControl::NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept { CONSOLE_PROCESS_INFO cpi; cpi.dwProcessID = dwProcessId; cpi.dwFlags = CPI_NEWPROCESSWINDOW; - - return Control(ControlType::ConsoleNotifyConsoleApplication, - &cpi, - sizeof(CONSOLE_PROCESS_INFO)); + Control(ControlType::ConsoleNotifyConsoleApplication, &cpi, sizeof(CONSOLE_PROCESS_INFO)); } -[[nodiscard]] NTSTATUS ConsoleControl::SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) +void ConsoleControl::SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept { CONSOLESETFOREGROUND Flags; Flags.hProcess = hProcess; Flags.bForeground = fForeground; - - return Control(ControlType::ConsoleSetForeground, - &Flags, - sizeof(Flags)); + Control(ControlType::ConsoleSetForeground, &Flags, sizeof(Flags)); } -[[nodiscard]] NTSTATUS ConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) +void ConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) noexcept { auto pConsoleWindow = ServiceLocator::LocateConsoleWindow(); @@ -46,108 +66,32 @@ using namespace Microsoft::Console::Interactivity::Win32; ConsoleEndTaskParams.ConsoleEventCode = dwEventType; ConsoleEndTaskParams.ConsoleFlags = ulCtrlFlags; ConsoleEndTaskParams.hwnd = pConsoleWindow == nullptr ? nullptr : pConsoleWindow->GetWindowHandle(); - - return Control(ControlType::ConsoleEndTask, - &ConsoleEndTaskParams, - sizeof(ConsoleEndTaskParams)); + Control(ControlType::ConsoleEndTask, &ConsoleEndTaskParams, sizeof(ConsoleEndTaskParams)); } -[[nodiscard]] NTSTATUS ConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept +void ConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept { CONSOLEWINDOWOWNER ConsoleOwner; ConsoleOwner.hwnd = hwnd; ConsoleOwner.ProcessId = processId; ConsoleOwner.ThreadId = threadId; - - return Control(ConsoleControl::ControlType::ConsoleSetWindowOwner, - &ConsoleOwner, - sizeof(ConsoleOwner)); -} - -#pragma endregion - -#pragma region Public Methods - -[[nodiscard]] NTSTATUS ConsoleControl::Control(_In_ ControlType ConsoleCommand, - _In_reads_bytes_(ConsoleInformationLength) PVOID ConsoleInformation, - _In_ DWORD ConsoleInformationLength) -{ -#ifdef CON_USERPRIVAPI_INDIRECT - if (_hUser32 != nullptr) - { - typedef NTSTATUS(WINAPI * PfnConsoleControl)(ControlType Command, PVOID Information, DWORD Length); - - static auto pfn = (PfnConsoleControl)GetProcAddress(_hUser32, "ConsoleControl"); - - if (pfn != nullptr) - { - return pfn(ConsoleCommand, ConsoleInformation, ConsoleInformationLength); - } - } - - return STATUS_UNSUCCESSFUL; -#else - return ConsoleControl(ConsoleCommand, ConsoleInformation, ConsoleInformationLength); -#endif + Control(ControlType::ConsoleSetWindowOwner, &ConsoleOwner, sizeof(ConsoleOwner)); } BOOL ConsoleControl::EnterReaderModeHelper(_In_ HWND hwnd) { #ifdef CON_USERPRIVAPI_INDIRECT - if (_hUser32 != nullptr) - { - typedef BOOL(WINAPI * PfnEnterReaderModeHelper)(HWND hwnd); - - static auto pfn = (PfnEnterReaderModeHelper)GetProcAddress(_hUser32, "EnterReaderModeHelper"); - - if (pfn != nullptr) - { - return pfn(hwnd); - } - } - - return FALSE; + return _enterReaderModeHelper ? _enterReaderModeHelper(hwnd) : FALSE; #else - return EnterReaderModeHelper(hwnd); + return ::EnterReaderModeHelper(hwnd); #endif } -BOOL ConsoleControl::TranslateMessageEx(const MSG* pmsg, - _In_ UINT flags) +BOOL ConsoleControl::TranslateMessageEx(const MSG* pmsg, _In_ UINT flags) { #ifdef CON_USERPRIVAPI_INDIRECT - if (_hUser32 != nullptr) - { - typedef BOOL(WINAPI * PfnTranslateMessageEx)(const MSG* pmsg, UINT flags); - - static auto pfn = (PfnTranslateMessageEx)GetProcAddress(_hUser32, "TranslateMessageEx"); - - if (pfn != nullptr) - { - return pfn(pmsg, flags); - } - } - - return FALSE; + return _translateMessageEx ? _translateMessageEx(pmsg, flags) : FALSE; #else - return TranslateMessageEx(pmsg, flags); + return ::TranslateMessageEx(pmsg, flags); #endif } - -#pragma endregion - -#ifdef CON_USERPRIVAPI_INDIRECT -ConsoleControl::ConsoleControl() -{ - _hUser32 = LoadLibraryW(L"user32.dll"); -} - -ConsoleControl::~ConsoleControl() -{ - if (_hUser32 != nullptr) - { - FreeLibrary(_hUser32); - _hUser32 = nullptr; - } -} -#endif diff --git a/src/interactivity/win32/ConsoleControl.hpp b/src/interactivity/win32/ConsoleControl.hpp index ff087bd367..176523b1af 100644 --- a/src/interactivity/win32/ConsoleControl.hpp +++ b/src/interactivity/win32/ConsoleControl.hpp @@ -32,28 +32,13 @@ namespace Microsoft::Console::Interactivity::Win32 class ConsoleControl final : public IConsoleControl { public: - enum ControlType - { - ConsoleSetVDMCursorBounds, - ConsoleNotifyConsoleApplication, - ConsoleFullscreenSwitch, - ConsoleSetCaretInfo, - ConsoleSetReserveKeys, - ConsoleSetForeground, - ConsoleSetWindowOwner, - ConsoleEndTask, - }; - // IConsoleControl Members - [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId) override; - [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) override; - [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override; - [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; - - // Public Members - [[nodiscard]] NTSTATUS Control(_In_ ConsoleControl::ControlType ConsoleCommand, - _In_reads_bytes_(ConsoleInformationLength) PVOID ConsoleInformation, - _In_ DWORD ConsoleInformationLength); + void Control(ControlType command, PVOID ptr, DWORD len) noexcept override; + void NotifyWinEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild) noexcept override; + void NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept override; + void SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept override; + void EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) noexcept override; + void SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; BOOL EnterReaderModeHelper(_In_ HWND hwnd); @@ -62,10 +47,14 @@ namespace Microsoft::Console::Interactivity::Win32 #ifdef CON_USERPRIVAPI_INDIRECT ConsoleControl(); - ~ConsoleControl(); private: - HMODULE _hUser32; + using PfnConsoleControl = NTSTATUS(WINAPI*)(ControlType, PVOID, DWORD); + using PfnEnterReaderModeHelper = BOOL(WINAPI*)(HWND); + using PfnTranslateMessageEx = BOOL(WINAPI*)(const MSG*, UINT); + PfnConsoleControl _consoleControl = nullptr; + PfnEnterReaderModeHelper _enterReaderModeHelper = nullptr; + PfnTranslateMessageEx _translateMessageEx = nullptr; #endif }; } diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj b/src/interactivity/win32/lib/win32.LIB.vcxproj index 6d6df33f5f..2df45262f3 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj @@ -16,7 +16,6 @@ - @@ -38,7 +37,6 @@ - @@ -70,4 +68,4 @@ - + \ No newline at end of file diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters index afe3fd0fba..c654f83d23 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - Source Files @@ -71,9 +68,6 @@ - - Header Files - Header Files @@ -128,5 +122,6 @@ + - + \ No newline at end of file diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 3fa53f2f97..e96848fe3b 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -428,12 +428,8 @@ void Window::ChangeViewport(const til::inclusive_rect& NewWindow) pSelection->HideSelection(); // Fire off an event to let accessibility apps know we've scrolled. - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier != nullptr) - { - pNotifier->NotifyConsoleUpdateScrollEvent(ScreenInfo.GetViewport().Left() - NewWindow.left, - ScreenInfo.GetViewport().Top() - NewWindow.top); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.ScrollViewport({ 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); @@ -1356,20 +1352,13 @@ IRawElementProviderSimple* Window::_GetUiaProvider() if (nullptr == _pUiaProvider) { LOG_IF_FAILED(WRL::MakeAndInitialize(&_pUiaProvider, this)); + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.SetUIAProvider(_pUiaProvider->GetScreenInfoProvider()); } 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) diff --git a/src/interactivity/win32/window.hpp b/src/interactivity/win32/window.hpp index 2b36d960da..a0ad4508f2 100644 --- a/src/interactivity/win32/window.hpp +++ b/src/interactivity/win32/window.hpp @@ -82,8 +82,6 @@ namespace Microsoft::Console::Interactivity::Win32 BOOL PostUpdateWindowSize() const; BOOL PostUpdateExtendedEditKeys() const; - [[nodiscard]] HRESULT SignalUia(_In_ EVENTID id); - void SetOwner(); BOOL GetCursorPosition(_Out_ til::point* lpPoint); BOOL GetClientRectangle(_Out_ til::rect* lpRect); diff --git a/src/interactivity/win32/windowUiaProvider.cpp b/src/interactivity/win32/windowUiaProvider.cpp index 664101fc56..01e14cc56c 100644 --- a/src/interactivity/win32/windowUiaProvider.cpp +++ b/src/interactivity/win32/windowUiaProvider.cpp @@ -31,51 +31,14 @@ try } CATCH_RETURN(); -[[nodiscard]] HRESULT WindowUiaProvider::Signal(_In_ EVENTID id) +ScreenInfoUiaProvider* WindowUiaProvider::GetScreenInfoProvider() const noexcept { - auto hr = S_OK; - - // ScreenInfoUiaProvider is responsible for signaling selection - // changed events and text changed events - if (id == UIA_Text_TextSelectionChangedEventId || - id == UIA_Text_TextChangedEventId) - { - if (_pScreenInfoProvider) - { - hr = _pScreenInfoProvider->Signal(id); - } - else - { - hr = E_POINTER; - } - return hr; - } - - if (_signalEventFiring.find(id) != _signalEventFiring.end() && - _signalEventFiring[id] == true) - { - return hr; - } - - try - { - _signalEventFiring[id] = true; - } - CATCH_RETURN(); - - hr = UiaRaiseAutomationEvent(this, id); - _signalEventFiring[id] = false; - - return hr; + return _pScreenInfoProvider.Get(); } [[nodiscard]] HRESULT WindowUiaProvider::SetTextAreaFocus() { - try - { - return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId); - } - CATCH_RETURN(); + return UiaRaiseAutomationEvent(_pScreenInfoProvider.Get(), UIA_AutomationFocusChangedEventId); } #pragma region IRawElementProviderSimple @@ -188,9 +151,6 @@ IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _CO if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) { RETURN_IF_FAILED(_pScreenInfoProvider.CopyTo(ppProvider)); - - // signal that the focus changed - LOG_IF_FAILED(_pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId)); } // For the other directions (parent, next, previous) the default of nullptr is correct @@ -240,8 +200,7 @@ IFACEMETHODIMP WindowUiaProvider::GetEmbeddedFragmentRoots(_Outptr_result_mayben IFACEMETHODIMP WindowUiaProvider::SetFocus() { - RETURN_IF_FAILED(_EnsureValidHwnd()); - return Signal(UIA_AutomationFocusChangedEventId); + return S_OK; } IFACEMETHODIMP WindowUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) diff --git a/src/interactivity/win32/windowUiaProvider.hpp b/src/interactivity/win32/windowUiaProvider.hpp index 97a35aa553..f925834152 100644 --- a/src/interactivity/win32/windowUiaProvider.hpp +++ b/src/interactivity/win32/windowUiaProvider.hpp @@ -47,7 +47,7 @@ namespace Microsoft::Console::Interactivity::Win32 WindowUiaProvider& operator=(WindowUiaProvider&&) = delete; public: - [[nodiscard]] virtual HRESULT Signal(_In_ EVENTID id); + ScreenInfoUiaProvider* GetScreenInfoProvider() const noexcept; [[nodiscard]] virtual HRESULT SetTextAreaFocus(); // IRawElementProviderSimple methods @@ -78,19 +78,6 @@ namespace Microsoft::Console::Interactivity::Win32 void ChangeViewport(const til::inclusive_rect& NewWindow); protected: - // this is used to prevent the object from - // signaling an event while it is already in the - // process of signalling another event. - // This fixes a problem with JAWS where it would - // call a public method that calls - // UiaRaiseAutomationEvent to signal something - // happened, which JAWS then detects the signal - // and calls the same method in response, - // eventually overflowing the stack. - // We aren't using this as a cheap locking - // mechanism for multi-threaded code. - std::unordered_map _signalEventFiring; - [[nodiscard]] HRESULT _EnsureValidHwnd() const; const OLECHAR* AutomationIdPropertyName = L"Console Window"; diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 60fbf826e0..82f58c59a4 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -89,7 +89,7 @@ VOID SetConsoleWindowOwner(const HWND hwnd, _Inout_opt_ ConsoleProcessHandle* pP } // Comment out this line to enable UIA tree to be visible until UIAutomationCore.dll can support our scenario. - LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->SetWindowOwner(hwnd, dwProcessId, dwThreadId)); + ServiceLocator::LocateConsoleControl()->SetWindowOwner(hwnd, dwProcessId, dwThreadId); } // ---------------------------- @@ -892,11 +892,14 @@ NTSTATUS InitWindowsSubsystem(_Out_ HHOOK* phhook) // was special cased (for CSRSS) to always succeed. Thus, we ignore failure for app compat (as not having the hook isn't fatal). *phhook = SetWindowsHookExW(WH_MSGFILTER, DialogHookProc, nullptr, GetCurrentThreadId()); - SetConsoleWindowOwner(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), ProcessData); + const auto hwnd = ServiceLocator::LocateConsoleWindow()->GetWindowHandle(); + SetConsoleWindowOwner(hwnd, ProcessData); LOG_IF_FAILED(ServiceLocator::LocateConsoleWindow()->ActivateAndShow(gci.GetShowWindow())); - NotifyWinEvent(EVENT_CONSOLE_START_APPLICATION, ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), ProcessData->dwProcessId, 0); + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.Initialize(hwnd, gci.GetMSAADelay(), gci.GetUIADelay()); + an.ApplicationStart(ProcessData->dwProcessId); return STATUS_SUCCESS; } diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index 3517ddd839..9bf6f1df88 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -782,18 +782,6 @@ static constexpr TsfDataProvider s_tsfDataProvider; break; } - case EVENT_CONSOLE_CARET: - case EVENT_CONSOLE_UPDATE_REGION: - case EVENT_CONSOLE_UPDATE_SIMPLE: - case EVENT_CONSOLE_UPDATE_SCROLL: - case EVENT_CONSOLE_LAYOUT: - case EVENT_CONSOLE_START_APPLICATION: - case EVENT_CONSOLE_END_APPLICATION: - { - NotifyWinEvent(Message, hWnd, (LONG)wParam, (LONG)lParam); - break; - } - default: CallDefWin: { diff --git a/src/propslib/RegistrySerialization.cpp b/src/propslib/RegistrySerialization.cpp index 4830a54911..1fcd32aa10 100644 --- a/src/propslib/RegistrySerialization.cpp +++ b/src/propslib/RegistrySerialization.cpp @@ -64,6 +64,8 @@ const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMa { _RegPropertyType::Boolean, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) }, { _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) }, { _RegPropertyType::Dword, L"TextMeasurement", SET_FIELD_AND_SIZE(_textMeasurement) }, + { _RegPropertyType::Dword, L"MSAADelay", SET_FIELD_AND_SIZE(_msaaDelay) }, + { _RegPropertyType::Dword, L"UIADelay", SET_FIELD_AND_SIZE(_uiaDelay) }, #if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED { _RegPropertyType::Boolean, L"EnableBuiltinGlyphs", SET_FIELD_AND_SIZE(_fEnableBuiltinGlyphs) }, #endif diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index 0e5cf60da8..209b70fc70 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -456,14 +456,11 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API // ConsoleApp will be false in the AttachConsole case. if (Cac.ConsoleApp) { - LOG_IF_FAILED(ServiceLocator::LocateConsoleControl()->NotifyConsoleApplication(dwProcessId)); + ServiceLocator::LocateConsoleControl()->NotifyConsoleApplication(dwProcessId); } - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier) - { - pNotifier->NotifyConsoleStartApplicationEvent(dwProcessId); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.ApplicationStart(dwProcessId); if (WI_IsFlagClear(gci.Flags, CONSOLE_INITIALIZED)) { @@ -556,11 +553,8 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleClientDisconnectRoutine(_In_ PCONSOLE_API { const auto pProcessData = pMessage->GetProcessHandle(); - auto pNotifier = ServiceLocator::LocateAccessibilityNotifier(); - if (pNotifier) - { - pNotifier->NotifyConsoleEndApplicationEvent(pProcessData->dwProcessId); - } + auto& an = ServiceLocator::LocateGlobals().accessibilityNotifier; + an.ApplicationEnd(pProcessData->dwProcessId); Tracing::s_TraceConsoleAttachDetach(pProcessData, false); diff --git a/src/server/ProcessList.cpp b/src/server/ProcessList.cpp index e01e20bb8d..c83879dacf 100644 --- a/src/server/ProcessList.cpp +++ b/src/server/ProcessList.cpp @@ -298,5 +298,5 @@ bool ConsoleProcessList::IsEmpty() const // - void ConsoleProcessList::_ModifyProcessForegroundRights(const HANDLE hProcess, const bool fForeground) const { - LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->SetForeground(hProcess, fForeground)); + ServiceLocator::LocateConsoleControl()->SetForeground(hProcess, fForeground); } diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 3ce0e5c83d..fb613c4e12 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -84,7 +84,6 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const til::CoordType width, const til::CoordType height) = 0; - virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; virtual void NotifyShellIntegrationMark() = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 5de6cdd602..998db07716 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -160,12 +160,6 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) } const auto textPositionAfter = state.text.data(); - if (state.columnBeginDirty != state.columnEndDirty) - { - const til::rect changedRect{ state.columnBeginDirty, cursorPosition.y, state.columnEndDirty, cursorPosition.y + 1 }; - _api.NotifyAccessibilityChange(changedRect); - } - // If we're past the end of the line, we need to clamp the cursor // back into range, and if wrapping is enabled, set the delayed wrap // flag. The wrapping only occurs once another character is output. @@ -413,8 +407,7 @@ void AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // - Helper method which applies a bunch of flags that are typically set whenever // the cursor is moved. The IsOn flag is set to true, and the Delay flag to false, // to force a blinking cursor to be visible, so the user can immediately see the -// new position. The HasMoved flag is set to let the accessibility notifier know -// that there was movement that needs to be reported. +// new position. // Arguments: // - cursor - The cursor instance to be updated // Return Value: @@ -423,7 +416,6 @@ void AdaptDispatch::_ApplyCursorMovementFlags(Cursor& cursor) noexcept { cursor.SetDelay(false); cursor.SetIsOn(true); - cursor.SetHasMoved(true); } // Routine Description: @@ -726,7 +718,6 @@ void AdaptDispatch::DeleteCharacter(const VTInt count) void AdaptDispatch::_FillRect(const Page& page, const til::rect& fillRect, const std::wstring_view& fillChar, const TextAttribute& fillAttrs) const { page.Buffer().FillRect(fillRect, fillChar, fillAttrs); - _api.NotifyAccessibilityChange(fillRect); } // Routine Description: @@ -870,7 +861,6 @@ void AdaptDispatch::_SelectiveEraseRect(const Page& page, const til::rect& erase } } } - _api.NotifyAccessibilityChange(eraseRect); } } @@ -980,7 +970,6 @@ void AdaptDispatch::_ChangeRectAttributes(const Page& page, const til::rect& cha } } page.Buffer().TriggerRedraw(Viewport::FromExclusive(changeRect)); - _api.NotifyAccessibilityChange(changeRect); } } @@ -1212,7 +1201,6 @@ void AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const } while (dstView.WalkInBounds(dstPos, walkDirection)); // Copy any image content in the affected area. ImageSlice::CopyBlock(src.Buffer(), srcView.ToExclusive(), dst.Buffer(), dstView.ToExclusive()); - _api.NotifyAccessibilityChange(dstRect); } } @@ -3142,7 +3130,6 @@ void AdaptDispatch::_EraseScrollback() _api.SetViewportPosition({ page.XPanOffset(), 0 }); // Move the cursor to the same relative location. cursor.SetYPosition(row - page.Top()); - cursor.SetHasMoved(true); } //Routine Description: @@ -3194,7 +3181,6 @@ void AdaptDispatch::_EraseAll() } // Restore the relative cursor position cursor.SetYPosition(row + newPageTop); - cursor.SetHasMoved(true); // Erase all the rows in the current page. const auto eraseAttributes = _GetEraseAttributes(page); diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 5821bb28ae..c6525ab8dd 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -202,11 +202,6 @@ public: Log::Comment(L"PlayMidiNote MOCK called..."); } - void NotifyAccessibilityChange(const til::rect& /*changedRect*/) override - { - Log::Comment(L"NotifyAccessibilityChange MOCK called..."); - } - void NotifyBufferRotation(const int /*delta*/) override { Log::Comment(L"NotifyBufferRotation MOCK called..."); diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 27dfb8c3b7..52c9009ed6 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -43,29 +43,6 @@ try } CATCH_RETURN(); -[[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID eventId) -{ - auto hr = S_OK; - // check to see if we're already firing this particular event - if (_signalFiringMapping.find(eventId) != _signalFiringMapping.end() && - _signalFiringMapping[eventId] == true) - { - return hr; - } - - try - { - _signalFiringMapping[eventId] = true; - } - CATCH_RETURN(); - - IRawElementProviderSimple* pProvider = this; - hr = UiaRaiseAutomationEvent(pProvider, eventId); - _signalFiringMapping[eventId] = false; - - return hr; -} - #pragma region IRawElementProviderSimple // Implementation of IRawElementProviderSimple::get_ProviderOptions. @@ -211,7 +188,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_resul IFACEMETHODIMP ScreenInfoUiaProviderBase::SetFocus() { UiaTracing::TextProvider::SetFocus(*this); - return Signal(UIA_AutomationFocusChangedEventId); + return S_OK; } #pragma endregion diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h index 2634d468fa..c474c787ee 100644 --- a/src/types/ScreenInfoUiaProviderBase.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -47,7 +47,6 @@ namespace Microsoft::Console::Types ScreenInfoUiaProviderBase& operator=(ScreenInfoUiaProviderBase&&) = delete; ~ScreenInfoUiaProviderBase() = default; - [[nodiscard]] HRESULT Signal(_In_ EVENTID id); virtual void ChangeViewport(const til::inclusive_rect& NewWindow) = 0; // IRawElementProviderSimple methods @@ -109,19 +108,6 @@ namespace Microsoft::Console::Types std::wstring _wordDelimiters{}; private: - // this is used to prevent the object from - // signaling an event while it is already in the - // process of signalling another event. - // This fixes a problem with JAWS where it would - // call a public method that calls - // UiaRaiseAutomationEvent to signal something - // happened, which JAWS then detects the signal - // and calls the same method in response, - // eventually overflowing the stack. - // We aren't using this as a cheap locking - // mechanism for multi-threaded code. - std::unordered_map _signalFiringMapping{}; - til::size _getScreenBufferCoords() const noexcept; const TextBuffer& _getTextBuffer() const noexcept; Viewport _getViewport() const noexcept;