Fixes xbox controller crashing the Terminal (#20234)

## Summary of the Pull Request
Fixes a denial of service by a toddler with an Xbox controller wanting
to play Forza. They can impatiently press up and down and close out the
terminal window.

Issue is upstream in WinUI, but I think this gathers things into a state
where Terminal can at least recover by adding a `GettingFocus` handler
on the `TabViewItem` that cancels that gamepad focus move before it
reaches the framework handler.

## References and Relevant Issues
Issue #19671 and #20089

## Validation Steps Performed
Handed a controller to my toddler while working in terminal. I also
banged on the keys myself.

## PR Checklist
Closes #19671 
Closes #20089
This commit is contained in:
Phil Scott
2026-06-23 13:27:17 -04:00
committed by GitHub
parent 05b613cf06
commit 0c4881bc9c
2 changed files with 35 additions and 0 deletions

View File

@@ -78,4 +78,5 @@ wtl
wtt wtt
wttlog wttlog
Xamarin Xamarin
XBOX
xfgcheck xfgcheck

View File

@@ -162,6 +162,40 @@ namespace winrt::TerminalApp::implementation
} }
}); });
// BODGY: Work around a fail-fast crash in WinUI 2's TabView. When a
// game controller (e.g. an XBOX controller's d-pad/stick) moves focus
// Up or Down between two TabViewItems, MUX's TabView::OnListViewGettingFocus
// runs a GameController-only code path that calls
// DispatcherQueue.TryEnqueue, which fails with E_INVALIDARG. That
// unhandled error inside a GettingFocus callback makes XAML fail-fast,
// and the whole window vanishes. See
// microsoft-ui-xaml/blob/840c6cd4ac9d16d9aa99b24f0bd43997fe21bef8/dev/TabView/TabView.cpp#L246
//
// This GettingFocus handler is on the TabViewItem, which is closer to
// the focus source than the TabViewListView that MUX's handler lives
// on, so it runs first. Marking the event Handled - and cancelling the
// focus move, which is exactly what MUX itself does for the keyboard
// case - preempts MUX's broken handler. Keyboard navigation is left
// untouched; MUX handles that path safely.
TabViewItem().GettingFocus([](auto&&, const winrt::WUX::Input::GettingFocusEventArgs& args) {
const auto direction{ args.Direction() };
if (direction != winrt::WUX::Input::FocusNavigationDirection::Up &&
direction != winrt::WUX::Input::FocusNavigationDirection::Down)
{
return;
}
if (args.InputDevice() != winrt::WUX::Input::FocusInputDeviceKind::GameController)
{
return;
}
if (args.OldFocusedElement().try_as<winrt::MUX::Controls::TabViewItem>() &&
args.NewFocusedElement().try_as<winrt::MUX::Controls::TabViewItem>())
{
args.Cancel(true);
args.Handled(true);
}
});
UpdateTitle(); UpdateTitle();
_RecalculateAndApplyTabColor(); _RecalculateAndApplyTabColor();
} }