Compare commits

...

2 Commits

Author SHA1 Message Date
Carlos Zamora
2279495b00 [DEBUG] Suppress XamlUiaTextRange throw from debug output 2026-03-16 15:58:53 -07:00
Carlos Zamora
0f75840a0b Add support for Accessibility VT Sequence 2026-03-16 11:11:27 -07:00
22 changed files with 195 additions and 3 deletions

View File

@@ -145,6 +145,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnWindowSizeChanged = [this](auto&& PH1, auto&& PH2) { _terminalWindowSizeChanged(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2)); };
_terminal->SetWindowSizeChangedCallback(pfnWindowSizeChanged);
auto pfnSetAccessibilityEngineState = [this](auto&& PH1) { _terminalSetAccessibilityEngineState(std::forward<decltype(PH1)>(PH1)); };
_terminal->SetAccessibilityEngineStateCallback(pfnSetAccessibilityEngineState);
auto pfnDispatchAccessibilityAnnouncement = [this](auto&& PH1) { _terminalDispatchAccessibilityAnnouncement(std::forward<decltype(PH1)>(PH1)); };
_terminal->SetDispatchAccessibilityAnnouncementCallback(pfnDispatchAccessibilityAnnouncement);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -1685,6 +1691,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand }, bufferRow));
}
void ControlCore::_terminalSetAccessibilityEngineState(const bool enabled)
{
SetAccessibilityEngineState.raise(*this, enabled);
}
void ControlCore::_terminalDispatchAccessibilityAnnouncement(std::wstring_view announcement)
{
DispatchAccessibilityAnnouncement.raise(*this, hstring{ announcement });
}
void ControlCore::OpenCWD()
{
const auto workingDirectory = WorkingDirectory();

View File

@@ -293,6 +293,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<IInspectable, bool> SetAccessibilityEngineState;
til::typed_event<IInspectable, hstring> DispatchAccessibilityAnnouncement;
til::typed_event<> RefreshQuickFixUI;
til::typed_event<IInspectable, Control::WindowSizeChangedEventArgs> WindowSizeChanged;
@@ -334,6 +336,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const int velocity,
const std::chrono::microseconds duration);
void _terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow);
void _terminalSetAccessibilityEngineState(const bool newState);
void _terminalDispatchAccessibilityAnnouncement(std::wstring_view announcement);
void _terminalWindowSizeChanged(int32_t width, int32_t height);
void _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);

View File

@@ -197,6 +197,8 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
event Windows.Foundation.TypedEventHandler<Object, Boolean> SetAccessibilityEngineState;
event Windows.Foundation.TypedEventHandler<Object, String> DispatchAccessibilityAnnouncement;
event Windows.Foundation.TypedEventHandler<Object, Object> RefreshQuickFixUI;
event Windows.Foundation.TypedEventHandler<Object, WindowSizeChangedEventArgs> WindowSizeChanged;

View File

@@ -166,13 +166,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
void ControlInteractivity::LostFocus()
{
SetAccessibilityEngineState(false);
_core->LostFocus();
}
void ControlInteractivity::SetAccessibilityEngineState(bool enabled)
{
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Disable());
if (enabled)
{
THROW_IF_FAILED(_uiaEngine->Enable());
}
else
{
THROW_IF_FAILED(_uiaEngine->Disable());
}
}
_core->LostFocus();
}
// Method Description

View File

@@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void GotFocus();
void LostFocus();
void SetAccessibilityEngineState(bool enabled);
void UpdateSettings();
void Initialize();
Control::ControlCore Core();

View File

@@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Control
void Initialize();
void GotFocus();
void LostFocus();
void SetAccessibilityEngineState(Boolean enabled);
UInt64 Id { get; };

View File

@@ -328,6 +328,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged });
_revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested });
_revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand });
_revokers.SetAccessibilityEngineState = _core.SetAccessibilityEngineState(winrt::auto_revoke, { get_weak(), &TermControl::_setAccessibilityEngineState });
_revokers.DispatchAccessibilityAnnouncement = _core.DispatchAccessibilityAnnouncement(winrt::auto_revoke, { get_weak(), &TermControl::_dispatchAccessibilityAnnouncement });
_revokers.WindowSizeChanged = _core.WindowSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWindowSizeChanged });
_revokers.WriteToClipboard = _core.WriteToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWriteToClipboard });
@@ -4045,6 +4047,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SearchMissingCommand.raise(*this, args);
}
void TermControl::_setAccessibilityEngineState(const IInspectable& /*sender*/, bool enabled)
{
_interactivity.SetAccessibilityEngineState(enabled);
}
void TermControl::_dispatchAccessibilityAnnouncement(const IInspectable& /*sender*/, const hstring announcement) const
{
if (_automationPeer)
{
if (auto dispatcher = Dispatcher())
{
dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [automationPeer{ _automationPeer }, announcement]() {
try
{
automationPeer.RaiseNotificationEvent(
AutomationNotificationKind::Other,
AutomationNotificationProcessing::All,
announcement,
L"AccessibilityAnnouncement" /* unique name for this group of notifications */);
}
CATCH_LOG();
});
}
}
}
winrt::fire_and_forget TermControl::_bubbleWindowSizeChanged(const IInspectable& /*sender*/, Control::WindowSizeChangedEventArgs args)
{
auto weakThis{ get_weak() };

View File

@@ -436,6 +436,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _showContextMenuAt(const winrt::Windows::Foundation::Point& controlRelativePos);
void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args);
void _setAccessibilityEngineState(const IInspectable& sender, bool enabled);
void _dispatchAccessibilityAnnouncement(const IInspectable& sender, const hstring announcement) const;
winrt::fire_and_forget _bubbleWindowSizeChanged(const IInspectable& sender, Control::WindowSizeChangedEventArgs args);
til::CoordType _calculateSearchScrollOffset() const;
@@ -469,6 +471,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::ControlCore::CompletionsChanged_revoker CompletionsChanged;
Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested;
Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand;
Control::ControlCore::SetAccessibilityEngineState_revoker SetAccessibilityEngineState;
Control::ControlCore::DispatchAccessibilityAnnouncement_revoker DispatchAccessibilityAnnouncement;
Control::ControlCore::RefreshQuickFixUI_revoker RefreshQuickFixUI;
Control::ControlCore::WindowSizeChanged_revoker WindowSizeChanged;

View File

@@ -1265,6 +1265,16 @@ void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function
_pfnClearQuickFix.swap(pfn);
}
void Terminal::SetAccessibilityEngineStateCallback(std::function<void(bool)> pfn) noexcept
{
_pfnSetAccessibilityEngineState.swap(pfn);
}
void Terminal::SetDispatchAccessibilityAnnouncementCallback(std::function<void(std::wstring_view)> pfn) noexcept
{
_pfnDispatchAccessibilityAnnouncement.swap(pfn);
}
// Method Description:
// - Stores the search highlighted regions in the terminal
void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept

View File

@@ -162,6 +162,9 @@ public:
void SearchMissingCommand(const std::wstring_view command) override;
void SetAccessibilityEngineState(bool enable) override;
void DispatchAccessibilityAnnouncement(std::wstring_view announcement) override;
#pragma endregion
void ClearMark();
@@ -232,6 +235,8 @@ public:
void SetSearchMissingCommandCallback(std::function<void(std::wstring_view, const til::CoordType)> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
void SetAccessibilityEngineStateCallback(std::function<void(bool)> pfn) noexcept;
void SetDispatchAccessibilityAnnouncementCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
void SetSearchHighlightFocused(size_t focusedIdx) noexcept;
void ScrollToSearchHighlight(til::CoordType searchScrollOffset);
@@ -340,6 +345,8 @@ private:
std::function<void(std::wstring_view, const til::CoordType)> _pfnSearchMissingCommand;
std::function<void()> _pfnClearQuickFix;
std::function<void(int32_t, int32_t)> _pfnWindowSizeChanged;
std::function<void(bool)> _pfnSetAccessibilityEngineState;
std::function<void(std::wstring_view)> _pfnDispatchAccessibilityAnnouncement;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;

View File

@@ -347,6 +347,22 @@ bool Terminal::IsVtInputEnabled() const noexcept
return false;
}
void Terminal::SetAccessibilityEngineState(bool enable)
{
if (_pfnSetAccessibilityEngineState)
{
_pfnSetAccessibilityEngineState(enable);
}
}
void Terminal::DispatchAccessibilityAnnouncement(std::wstring_view announcement)
{
if (_pfnDispatchAccessibilityAnnouncement)
{
_pfnDispatchAccessibilityAnnouncement(announcement);
}
}
void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength)
{
if (_pfnCompletionsChanged)

View File

@@ -437,3 +437,11 @@ void ConhostInternalGetSet::SearchMissingCommand(std::wstring_view /*missingComm
{
// Not implemented for conhost.
}
void ConhostInternalGetSet::SetAccessibilityEngineState(bool /*enable*/)
{
// Not implemented for conhost.
}
void ConhostInternalGetSet::DispatchAccessibilityAnnouncement(std::wstring_view /*announcement*/)
{
// Not implemented for conhost.
}

View File

@@ -72,6 +72,9 @@ public:
void SearchMissingCommand(std::wstring_view missingCommand) override;
void SetAccessibilityEngineState(bool enable) override;
void DispatchAccessibilityAnnouncement(std::wstring_view announcement) override;
private:
Microsoft::Console::IIoProvider& _io;
};

View File

@@ -57,11 +57,24 @@ namespace Microsoft::Console::ErrorReporting
// Don't log anything. We just failed to trace, where will we go now?
}
// Suppress debug output (OutputDebugString) for XAML_E_NOT_SUPPORTED (0x80131515).
// ReportFailureToFallbackProvider already suppresses this from telemetry, but that
// doesn't cover the debugger output path. Setting RequestSuppressTelemetry on the
// FailureInfo prevents WIL from emitting an OutputDebugString for this expected error.
inline void __stdcall SuppressExpectedFailures(_Inout_ wil::FailureInfo* pFailure) noexcept
{
if (pFailure && pFailure->hr == 0x80131515L)
{
WI_SetFlag(pFailure->flags, wil::FailureFlags::RequestSuppressTelemetry);
}
}
__declspec(noinline) inline void EnableFallbackFailureReporting(TraceLoggingHProvider provider) noexcept
try
{
FallbackProvider = provider;
::wil::SetResultTelemetryFallback(::Microsoft::Console::ErrorReporting::ReportFailureToFallbackProvider);
::wil::details::g_pfnNotifyFailure = &SuppressExpectedFailures;
}
catch (...)
{

View File

@@ -688,6 +688,13 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
TabulationStopReport = 2
};
enum class AccessibilityEventType : VTInt
{
StopAnnouncingOutput = 0,
ResumeAnnouncingOutput = 1,
AnnounceText = 2
};
constexpr VTInt s_sDECCOLMSetColumns = 132;
constexpr VTInt s_sDECCOLMResetColumns = 80;

View File

@@ -160,6 +160,8 @@ public:
virtual void DoFinalTermAction(const std::wstring_view string) = 0;
virtual void DoAccessibilityAction(const std::wstring_view string) = 0;
virtual void DoVsCodeAction(const std::wstring_view string) = 0;
virtual void DoWTAction(const std::wstring_view string) = 0;

View File

@@ -90,5 +90,8 @@ namespace Microsoft::Console::VirtualTerminal
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
virtual void SearchMissingCommand(const std::wstring_view command) = 0;
virtual void SetAccessibilityEngineState(bool enable) = 0;
virtual void DispatchAccessibilityAnnouncement(std::wstring_view announcement) = 0;
};
}

View File

@@ -3696,6 +3696,52 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
// modify the state of that mark as we go.
}
// Method Description:
// - Performs an accessibility action
// - Not actually used in conhost
// Arguments:
// - string: contains the parameters that define which action we do
void AdaptDispatch::DoAccessibilityAction(const std::wstring_view string)
{
const auto parts = Utils::SplitString(string, L';');
unsigned int action = 0;
if (parts.size() < 1 || !Utils::StringToUint(til::at(parts, 0), action))
{
return;
}
// A helper function to make the UIA provider dispatch an announcement
// stored in parts[1]
auto dispatchAnnouncement = [&]() {
if (parts.size() > 1)
{
_api.DispatchAccessibilityAnnouncement(til::at(parts, 1));
}
};
// The structure of the message is as follows:
// `e]200;
// 0: AccessibilityEventType;
// 1: optional announcement;
switch (static_cast<DispatchTypes::AccessibilityEventType>(action))
{
case DispatchTypes::AccessibilityEventType::StopAnnouncingOutput:
_api.SetAccessibilityEngineState(false);
dispatchAnnouncement();
break;
case DispatchTypes::AccessibilityEventType::ResumeAnnouncingOutput:
_api.SetAccessibilityEngineState(true);
dispatchAnnouncement();
break;
case DispatchTypes::AccessibilityEventType::AnnounceText:
dispatchAnnouncement();
break;
default:
return;
}
}
// Method Description:
// - Performs a VsCode action
// - Currently, the actions we support are:

View File

@@ -157,6 +157,8 @@ namespace Microsoft::Console::VirtualTerminal
void DoFinalTermAction(const std::wstring_view string) override;
void DoAccessibilityAction(const std::wstring_view string) override;
void DoVsCodeAction(const std::wstring_view string) override;
void DoWTAction(const std::wstring_view string) override;

View File

@@ -147,6 +147,8 @@ public:
void DoFinalTermAction(const std::wstring_view /*string*/) override {}
void DoAccessibilityAction(const std::wstring_view /*string*/) override{};
void DoVsCodeAction(const std::wstring_view /*string*/) override {}
void DoWTAction(const std::wstring_view /*string*/) override {}

View File

@@ -895,6 +895,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
_dispatch->DoVsCodeAction(string);
break;
}
case OscActionCodes::AccessibilityAction:
{
_dispatch->DoAccessibilityAction(string);
break;
}
case OscActionCodes::WTAction:
{
_dispatch->DoWTAction(string);

View File

@@ -224,6 +224,7 @@ namespace Microsoft::Console::VirtualTerminal
ResetCursorColor = 112,
ResetHighlightColor = 117,
FinalTermAction = 133,
AccessibilityAction = 200,
VsCodeAction = 633,
ITerm2Action = 1337,
WTAction = 9001,