Skip to content

Commit

Permalink
Fire UIA Events for Output and Cursor Movement (#4826)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
This notifies automation clients (i.e.: NVDA, narrator, etc...) of new output being rendered to the screen.

## References
Close #2447 - Signaling for new output and cursor
Close #3791 - fixed by signaling cursor changes

## Detailed Description of the Pull Request / Additional comments
- Added tracing for UiaRenderer. This makes it easier to debug issues with notifying an automation client.
- Fire TextChanged automation events when new content is output to the screen.

## Validation Steps Performed
Verified with NVDA [1]

## Narrator
Narrator works _better_, but is unable to detect new output consistently. There is no harm for narrator when this change goes in.

[1] microsoft/terminal#2447 (comment)
  • Loading branch information
donno2048 committed Mar 18, 2020
1 parent c9a7f9a commit 7f5901f
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 5 deletions.
11 changes: 10 additions & 1 deletion src/cascadia/TerminalControl/TermControlAutomationPeer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "TermControlAutomationPeer.g.cpp"

#include "XamlUiaTextRange.h"
#include "..\types\UiaTracing.h"

using namespace Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
Expand Down Expand Up @@ -44,6 +45,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControlAutomationPeer::SignalSelectionChanged()
{
UiaTracing::Signal::SelectionChanged();
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
// The event that is raised when the text selection is modified.
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
Expand All @@ -58,6 +60,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControlAutomationPeer::SignalTextChanged()
{
UiaTracing::Signal::TextChanged();
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
// The event that is raised when textual content is modified.
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged);
Expand All @@ -72,9 +75,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControlAutomationPeer::SignalCursorChanged()
{
UiaTracing::Signal::CursorChanged();
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
// The event that is raised when the text was changed in an edit control.
RaiseAutomationEvent(AutomationEvents::TextEditTextChanged);
// Do NOT fire a TextEditTextChanged. Generally, an app on the other side
// will expect more information. Though you can dispatch that event
// on its own, it may result in a nullptr exception on the other side
// because no additional information was provided. Crashing the screen
// reader.
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
});
}

Expand Down
31 changes: 27 additions & 4 deletions src/renderer/uia/UiaRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
_dispatcher{ THROW_HR_IF_NULL(E_INVALIDARG, dispatcher) },
_isPainting{ false },
_selectionChanged{ false },
_textBufferChanged{ false },
_cursorChanged{ false },
_isEnabled{ true },
_prevSelection{},
RenderEngineBase()
Expand Down Expand Up @@ -56,7 +58,8 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT UiaEngine::Invalidate(const SMALL_RECT* const /*psrRegion*/) noexcept
{
return S_FALSE;
_textBufferChanged = true;
return S_OK;
}

// Routine Description:
Expand All @@ -68,6 +71,7 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
// - S_FALSE
[[nodiscard]] HRESULT UiaEngine::InvalidateCursor(const COORD* const /*pcoordCursor*/) noexcept
{
_cursorChanged = true;
return S_FALSE;
}

Expand Down Expand Up @@ -150,7 +154,8 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT UiaEngine::InvalidateAll() noexcept
{
return S_FALSE;
_textBufferChanged = true;
return S_OK;
}

// Routine Description:
Expand Down Expand Up @@ -192,10 +197,10 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
RETURN_HR_IF(S_FALSE, !_isEnabled);

// add more events here
// bool somethingToDo = _selectionChanged;
const bool somethingToDo = _selectionChanged || _textBufferChanged || _cursorChanged;

// If there's nothing to do, quick return
RETURN_HR_IF(S_FALSE, !_selectionChanged);
RETURN_HR_IF(S_FALSE, !somethingToDo);

_isPainting = true;
return S_OK;
Expand All @@ -221,8 +226,26 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
}
CATCH_LOG();
}
if (_textBufferChanged)
{
try
{
_dispatcher->SignalTextChanged();
}
CATCH_LOG();
}
if (_cursorChanged)
{
try
{
_dispatcher->SignalCursorChanged();
}
CATCH_LOG();
}

_selectionChanged = false;
_textBufferChanged = false;
_cursorChanged = false;
_prevSelection.clear();
_isPainting = false;

Expand Down
2 changes: 2 additions & 0 deletions src/renderer/uia/UiaRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ namespace Microsoft::Console::Render
bool _isEnabled;
bool _isPainting;
bool _selectionChanged;
bool _textBufferChanged;
bool _cursorChanged;

Microsoft::Console::Types::IUiaEventDispatcher* _dispatcher;

Expand Down
37 changes: 37 additions & 0 deletions src/types/UiaTracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,4 +613,41 @@ void UiaTracing::TextProvider::get_SupportedTextSelection(const ScreenInfoUiaPro
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}

void UiaTracing::Signal::SelectionChanged() noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"Signal::SelectionChanged",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}

void UiaTracing::Signal::TextChanged() noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"Signal::TextChanged",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}

void UiaTracing::Signal::CursorChanged() noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"Signal::CursorChanged",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}

#pragma warning(pop)
8 changes: 8 additions & 0 deletions src/types/UiaTracing.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ namespace Microsoft::Console::Types
static void get_SupportedTextSelection(const ScreenInfoUiaProviderBase& base, SupportedTextSelection result) noexcept;
};

class Signal final
{
public:
static void SelectionChanged() noexcept;
static void TextChanged() noexcept;
static void CursorChanged() noexcept;
};

private:
// Implement this as a singleton class.
static UiaTracing& EnsureRegistration() noexcept
Expand Down

0 comments on commit 7f5901f

Please sign in to comment.