From 0732b483acfa1253aee1e603844f9c7613031393 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Mon, 22 Jun 2026 14:16:54 -0500 Subject: [PATCH 1/7] Reflect OS PR !15916518: conhost: add ETL to our Feature Tests (#20334) This may help us diagnose the OneCore windowing failures. Related work items: MSFT-62650388 --- src/host/ft_host/testmd.definition | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/host/ft_host/testmd.definition b/src/host/ft_host/testmd.definition index 1eb5c9eeb2..3adef43b83 100644 --- a/src/host/ft_host/testmd.definition +++ b/src/host/ft_host/testmd.definition @@ -10,6 +10,10 @@ }, "Dependencies": { "Files": [ + { + "SourcePath": "$(PROJECT_ROOT)\\core\\console\\open\\src\\Terminal.wprp", + "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)" + }, { "SourcePath": "$(PROJECT_ROOT)\\core\\console\\open\\src\\ConsolePerf.wprp", "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)" @@ -23,7 +27,22 @@ "Packages": [ "Microsoft.Console.Tools.Nihilist" ] }, "Logs": [ ], - "Plugins": [ ], + "Plugins": [ + { + "Type": "Microsoft.TestInfrastructure.UniversalTest.TestMD.Plugins.Etw.EtwPlugin", + "Name": "Collecting conhost logs during FTs", + "Parameters": { + "$schema": "Microsoft.TestInfrastructure.UniversalTest.TestMD.Plugins.Etw.EtwPlugin-1.json", + "Profiles": [ + { + "ProfileFile": "Terminal.wprp", + "Profile": "DefTerm" + } + ], + "InstanceName": "conhost_FeatureTests" + } + } + ], "Profiles": [ { "Name": "Performance", From d21b580353a5df195b113037ee0b5a492bb16201 Mon Sep 17 00:00:00 2001 From: BamBamTram32 Date: Mon, 22 Jun 2026 16:18:38 -0500 Subject: [PATCH 2/7] Update README.md (#20273) Improved README accessibility alt text --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de45a7c5ff..608358e615 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![terminal-logos](https://github.com/microsoft/terminal/assets/91625426/333ddc76-8ab2-4eb4-a8c0-4d7b953b1179) +![Windows Terminal project logos and branding image](https://github.com/microsoft/terminal/assets/91625426/333ddc76-8ab2-4eb4-a8c0-4d7b953b1179) [![Terminal Build Status](https://dev.azure.com/shine-oss/terminal/_apis/build/status%2FTerminal%20CI?branchName=main)](https://dev.azure.com/shine-oss/terminal/_build/latest?definitionId=1&branchName=main) From d288fa09d628fb98f23926f5d70e1b4f573459d4 Mon Sep 17 00:00:00 2001 From: Danyal Ahmed <58849388+danyalahmed1995@users.noreply.github.com> Date: Tue, 23 Jun 2026 02:53:05 +0500 Subject: [PATCH 3/7] Fix font face keyboard selection crash (#20255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes a crash in the Settings UI font face control when selecting a font suggestion with the keyboard and pressing Enter. The font face `AutoSuggestBox` path was explicitly unfocusing the box after a chosen suggestion was submitted. During the keyboard suggestion commit path, that `Focus(FocusState::Unfocused)` call can trigger a WinUI/XAML crash before the settings model update completes. This change avoids explicitly unfocusing the `AutoSuggestBox` in the chosen-suggestion path and instead commits the selected font and moves focus to the parent container. Fixes #20245. ## Validation Manually validated with a local `CascadiaPackage` Debug x64 build: - Reproduced the crash before the fix. - Opened Settings → Defaults → Appearance → Font face. - Typed a partial font name. - Used arrow keys to select a suggestion. - Pressed Enter. - Verified Terminal no longer crashes. - Verified the selected font is applied and Save works. --- .../TerminalSettingsEditor/Appearances.cpp | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index ec66af8cd0..0cc3ed8bdf 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -12,6 +12,7 @@ #include "Appearances.g.cpp" using namespace winrt::Windows::UI::Text; +using namespace winrt::Windows::UI::Core; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Windows::UI::Xaml::Data; @@ -1221,8 +1222,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation fontSpec = fontName; } - sender.Text(fontSpec); - // Normally we'd just update the model property in LostFocus above, but because WinUI is the Ralph Wiggum // among the UI frameworks, it raises the LostFocus event _before_ the QuerySubmitted event. // So, when you press Save, the model will have the wrong font face string, because LostFocus was raised too early. @@ -1233,11 +1232,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // You can't just do IsSuggestionListOpen(false) either, because you can show the list with that property but not hide it. // So, we update the model manually and assign focus to the parent container. // - // BUT you can't just focus the parent container, because of a weird interaction with AutoSuggestBox where it'll refuse to lose - // focus if you picked a suggestion that matches the current fontSpec. So, we unfocus it first and then focus the parent container. - _updateFontName(fontSpec); - sender.Focus(FocusState::Unfocused); - FontFaceContainer().Focus(FocusState::Programmatic); + // Queue the selected-suggestion commit so AutoSuggestBox can finish processing Enter before we change its text/model. + // Do not manually unfocus the AutoSuggestBox here. Its Focus(FocusState::Unfocused) path crashes during keyboard commits. + Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis{ get_weak() }, weakSender{ winrt::make_weak(sender) }, fontSpec{ std::move(fontSpec) }]() { + if (const auto self{ weakThis.get() }) + { + if (const auto box{ weakSender.get() }) + { + box.Text(fontSpec); + } + + self->_updateFontName(fontSpec); + self->FontFaceContainer().Focus(FocusState::Programmatic); + } + }); } void Appearances::FontFaceBox_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& args) From 05b613cf06c5169fbbcbb40644568fd6d03ea2b5 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Mon, 22 Jun 2026 18:09:00 -0500 Subject: [PATCH 4/7] Pre-warm SHGetKnownFolder to avoid a deadlock in the nvidia driver (#20351) The nvidia driver attempts to use SHGetKnownFolder (which has its own lock!) under the loader lock, which occasionally triggers a deadlock when we attempt to do the same thing during settings load. Closes #20350 (superseded) Closes #20348 Co-authored-by: gleb <46842077+groovg@users.noreply.github.com> --- .github/actions/spelling/expect/expect.txt | 2 +- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 9 +++++++++ src/cascadia/WindowsTerminal/pch.h | 1 + src/host/ft_host/testmd.definition | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e93e898c01..2fd76fc989 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1139,7 +1139,7 @@ nullopts NUMSCROLL NUnit nupkg -NVIDIA +nvidia NVT OACR ocolor diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index a21ad3f02e..ab9e000389 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -544,6 +544,15 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) __assume(false); } + // !! LOAD BEARING !! + // This prevents loader lock contention with some versions of the nvidia + // driver, which calls SHGetKnownFolderPath triggering a delay load while + // under lock during application startup. See GH#20348. + { + wil::unique_cotaskmem_string localAppDataFolder; + SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localAppDataFolder); + } + _app = winrt::TerminalApp::App{}; _app.Logic().ReloadSettings(); diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 2b48aae8c9..10640ef474 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -32,6 +32,7 @@ Abstract: #include #include #include +#include // Manually include til after we include Windows.Foundation to give it winrt superpowers #define BLOCK_TIL diff --git a/src/host/ft_host/testmd.definition b/src/host/ft_host/testmd.definition index 3adef43b83..27b0545090 100644 --- a/src/host/ft_host/testmd.definition +++ b/src/host/ft_host/testmd.definition @@ -30,7 +30,7 @@ "Plugins": [ { "Type": "Microsoft.TestInfrastructure.UniversalTest.TestMD.Plugins.Etw.EtwPlugin", - "Name": "Collecting conhost logs during FTs", + "Name": "Collecting conhost logs during feature tests", "Parameters": { "$schema": "Microsoft.TestInfrastructure.UniversalTest.TestMD.Plugins.Etw.EtwPlugin-1.json", "Profiles": [ From 0c4881bc9cd2685c31026bf5d7eee9165b2da9b5 Mon Sep 17 00:00:00 2001 From: Phil Scott Date: Tue, 23 Jun 2026 13:27:17 -0400 Subject: [PATCH 5/7] 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 --- .github/actions/spelling/allow/microsoft.txt | 1 + src/cascadia/TerminalApp/Tab.cpp | 34 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index f43408388b..782754a012 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -78,4 +78,5 @@ wtl wtt wttlog Xamarin +XBOX xfgcheck diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index da4d0896ae..2ca1bb1d77 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -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() && + args.NewFocusedElement().try_as()) + { + args.Cancel(true); + args.Handled(true); + } + }); + UpdateTitle(); _RecalculateAndApplyTabColor(); } From 8824b02a782ebc0dc9843620905cc7e004f1e29e Mon Sep 17 00:00:00 2001 From: Anshika Jain Date: Tue, 23 Jun 2026 23:50:08 +0530 Subject: [PATCH 6/7] Always use MRU tab focus on tab close regardless of TabSwitcherMode (#20272) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Always focus the MRU (most recently used) tab when closing the current tab, regardless of TabSwitcherMode setting. ## References Fixes #19819 ## Detailed Description Removed the `TabSwitcherMode` check in `_RemoveTab` (TabManagement.cpp) so tab-close focus behavior is always MRU-based, decoupling it from the tab switcher preference as noted by @DHowett. ## Validation Steps - Opened 3 tabs, navigated Tab3 → Tab1, closed Tab1 - With fix: Tab3 focused (MRU) - Without fix: Tab2 focused (next in order) ## PR Checklist Closes #19819 --- src/cascadia/TerminalApp/TabManagement.cpp | 36 ++-------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 70d5d5eb8e..3a96635d43 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -541,40 +541,10 @@ namespace winrt::TerminalApp::implementation // 1. We want to customize this behavior (e.g., use MRU logic) // 2. In fullscreen (GH#5799) and focus (GH#7916) modes the _OnTabItemsChanged is not fired // 3. When rearranging tabs (GH#7916) _OnTabItemsChanged is suppressed - const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode(); - if (tabSwitchMode == TabSwitcherMode::MostRecentlyUsed) - { - const auto newSelectedTab = _mruTabs.GetAt(0); - _UpdatedSelectedTab(newSelectedTab); - _tabView.SelectedItem(newSelectedTab.TabViewItem()); - } - else - { - // We can't use - // auto selectedIndex = _tabView.SelectedIndex(); - // Because this will always return -1 in this scenario unfortunately. - // - // So, what we're going to try to do is move the focus to the tab - // to the right, within the bounds of how many tabs we have. - // - // EX: we have 4 tabs: [A, B, C, D]. If we close: - // * A (tabIndex=0): We'll want to focus tab B (now in index 0) - // * B (tabIndex=1): We'll want to focus tab C (now in index 1) - // * C (tabIndex=2): We'll want to focus tab D (now in index 2) - // * D (tabIndex=3): We'll want to focus tab C (now in index 2) - const auto newSelectedIndex = std::clamp(tabIndex, 0, _tabs.Size() - 1); - // _UpdatedSelectedTab will do the work of setting up the new tab as - // the focused one, and unfocusing all the others. - auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) }; - _UpdatedSelectedTab(newSelectedTab); - - // Also, we need to _manually_ set the SelectedItem of the tabView - // here. If we don't, then the TabView will technically not have a - // selected item at all, which can make things like ClosePane not - // work correctly. - _tabView.SelectedItem(newSelectedTab.TabViewItem()); - } + const auto newSelectedTab = _mruTabs.GetAt(0); + _UpdatedSelectedTab(newSelectedTab); + _tabView.SelectedItem(newSelectedTab.TabViewItem()); } // GH#5559 - If we were in the middle of a drag/drop, end it by clearing From d0263b86acb2b99fb92fb1a30eb32854526f736f Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 23 Jun 2026 18:00:46 -0500 Subject: [PATCH 7/7] tsm: use the much cheaper & non-async 26100 AppExtension APIs when possible (#20356) GetPublicPath doesn't require async _or_ Windows.Storage! FindAll is just the Sync version of FindAllAsync! --- .../CascadiaSettingsSerialization.cpp | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 2ac2e4c676..af32e4e955 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -395,7 +395,14 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings(bool generateExtensio try { const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); - extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + if (auto catalog2{ catalog.try_as() }) + { + extensions = catalog2.FindAll(); + } + else + { + extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + } } CATCH_LOG(); @@ -417,20 +424,37 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings(bool generateExtensio continue; } - // Likewise, getting the public folder from an extension is an async operation. - auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); - if (!foundFolder) + winrt::hstring publicFolderPath; + if (auto ext3{ ext.try_as() }) { - continue; + // Windows 11 24H2 and above support a much faster, much less + // Windows.Storage-y API. + publicFolderPath = ext3.GetPublicPath(); + if (publicFolderPath.empty()) + { + // No point in falling through to GetPublicFolderAsync; + // it won't work. + continue; + } + } + else + { + // Likewise, getting the public folder from an extension is an async operation. + auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); + if (!foundFolder) + { + continue; + } + + // the StorageFolder class has its own methods for obtaining the files within the folder + // however, all those methods are Async methods + // you may have noticed that we need to resort to clunky implementations for async operations + // (they are in extractValueFromTaskWithoutMainThreadAwait) + // so for now we will just take the folder path and access the files that way + publicFolderPath = foundFolder.Path(); } - // the StorageFolder class has its own methods for obtaining the files within the folder - // however, all those methods are Async methods - // you may have noticed that we need to resort to clunky implementations for async operations - // (they are in extractValueFromTaskWithoutMainThreadAwait) - // so for now we will just take the folder path and access the files that way - const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory); - + const auto path = buildPath(publicFolderPath, FragmentsSubDirectory); if (std::filesystem::is_directory(path)) { // MSIX does not support machine-wide scope