diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index e83f15dcc13..cd95ff2d5be 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -14,6 +14,7 @@ DERR environstrings EXPCMDFLAGS EXPCMDSTATE +FORCEMINIMIZE frac fullkbd futex @@ -81,6 +82,7 @@ schandle semver serializer shobjidl +SHOWMINIMIZED SIZENS smoothstep GETDESKWALLPAPER diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 96c6fe7f479..c3aba85a2c8 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -327,6 +327,7 @@ conattrs conbufferout concat concfg +conclnt conddkrefs condrv conechokey @@ -563,6 +564,7 @@ DECSWL DECTCEM Dedupe deduplicated +DEFAPP DEFAULTBACKGROUND DEFAULTFOREGROUND defaultsettings @@ -754,6 +756,7 @@ failfast FAILIFTHERE fallthrough FARPROC +fastlink fcb fcharset fclose diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index 0b7fce26a0b..b08862e6203 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -28,7 +28,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation struct WindowActivatedArgs : public WindowActivatedArgsT { WINRT_PROPERTY(uint64_t, PeasantID, 0); - WINRT_PROPERTY(winrt::guid, DesktopID, {}); + WINRT_PROPERTY(winrt::guid, DesktopID); WINRT_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {}); WINRT_PROPERTY(uint64_t, Hwnd, 0); diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/Remoting/packages.config +++ b/src/cascadia/Remoting/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/ShellExtension/packages.config b/src/cascadia/ShellExtension/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/ShellExtension/packages.config +++ b/src/cascadia/ShellExtension/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 0c9b513a8af..9b441a913fe 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -598,8 +598,8 @@ namespace winrt::TerminalApp::implementation // - // Important: Don't take the param by reference, since we'll be doing work // on another thread. - fire_and_forget _OpenNewWindow(const bool elevate, - const NewTerminalArgs newTerminalArgs) + fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate, + const NewTerminalArgs newTerminalArgs) { // Hop to the BG thread co_await winrt::resume_background(); diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 9c467df6e39..f76e9acc576 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -357,6 +357,9 @@ Alt+Click to split the current window + + Shift+Click to open a new window + Close diff --git a/src/cascadia/TerminalApp/TabRowControl.xaml b/src/cascadia/TerminalApp/TabRowControl.xaml index 503a805984c..a45049fca37 100644 --- a/src/cascadia/TerminalApp/TabRowControl.xaml +++ b/src/cascadia/TerminalApp/TabRowControl.xaml @@ -37,10 +37,11 @@ the MIT License. See LICENSE in the project root for license information. --> - + - + FontStyle="Italic"/> + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 363db082fd6..afb3beebe3f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -206,6 +206,13 @@ namespace winrt::TerminalApp::implementation const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) || WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); + const auto shiftState{ window.GetKeyState(VirtualKey::Shift) }; + const auto rShiftState = window.GetKeyState(VirtualKey::RightShift); + const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift); + const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) }; + // Check for DebugTap bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() && WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && @@ -218,6 +225,10 @@ namespace winrt::TerminalApp::implementation 0.5f, nullptr); } + else if (shiftPressed && !debugTap) + { + page->_OpenNewWindow(false, NewTerminalArgs()); + } else { page->_OpenNewTab(nullptr); @@ -586,11 +597,16 @@ namespace winrt::TerminalApp::implementation auto newPaneRun = WUX::Documents::Run(); newPaneRun.Text(RS_(L"NewPaneRun/Text")); newPaneRun.FontStyle(FontStyle::Italic); + auto newWindowRun = WUX::Documents::Run(); + newWindowRun.Text(RS_(L"NewWindowRun/Text")); + newWindowRun.FontStyle(FontStyle::Italic); auto textBlock = WUX::Controls::TextBlock{}; textBlock.Inlines().Append(newTabRun); textBlock.Inlines().Append(WUX::Documents::LineBreak{}); textBlock.Inlines().Append(newPaneRun); + textBlock.Inlines().Append(WUX::Documents::LineBreak{}); + textBlock.Inlines().Append(newWindowRun); auto toolTip = WUX::Controls::ToolTip{}; toolTip.Content(textBlock); @@ -608,6 +624,13 @@ namespace winrt::TerminalApp::implementation const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) || WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); + const auto shiftState{ window.GetKeyState(VirtualKey::Shift) }; + const auto rShiftState = window.GetKeyState(VirtualKey::RightShift); + const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift); + const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) }; + // Check for DebugTap bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() && WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && @@ -620,6 +643,12 @@ namespace winrt::TerminalApp::implementation 0.5f, newTerminalArgs); } + else if (shiftPressed && !debugTap) + { + // Manually fill in the evaluated profile. + newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(page->_settings.GetProfileForArgs(newTerminalArgs))); + page->_OpenNewWindow(false, newTerminalArgs); + } else { page->_OpenNewTab(newTerminalArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index b0a5c89e76c..d2b750d448c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -155,6 +155,8 @@ namespace winrt::TerminalApp::implementation void _CreateNewTabFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings); winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings); + winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); + bool _displayingCloseDialog{ false }; void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config index 4ad301cfb36..a6c68340ad1 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -2,5 +2,5 @@ - + diff --git a/src/cascadia/TerminalAzBridge/packages.config b/src/cascadia/TerminalAzBridge/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/TerminalAzBridge/packages.config +++ b/src/cascadia/TerminalAzBridge/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalConnection/packages.config b/src/cascadia/TerminalConnection/packages.config index 29ec227ea09..4397356d130 100644 --- a/src/cascadia/TerminalConnection/packages.config +++ b/src/cascadia/TerminalConnection/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 048cc40aec3..153e724ec7c 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1308,24 +1308,31 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift)); const auto ctrlEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Control)); - if (_CanSendVTMouseInput()) + auto lock = _terminal->LockForWriting(); + const auto cursorPosition = point.Position(); + const auto terminalPosition = _GetTerminalPosition(cursorPosition); + + // GH#9396: we prioritize hyper-link over VT mouse events + if (point.Properties().IsLeftButtonPressed() && ctrlEnabled && !_terminal->GetHyperlinkAtPosition(terminalPosition).empty()) + { + // Handle hyper-link only on the first click to prevent multiple activations + const auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp()); + if (clickCount == 1) + { + _HyperlinkHandler(_terminal->GetHyperlinkAtPosition(terminalPosition)); + } + } + else if (_CanSendVTMouseInput()) { _TrySendMouseEvent(point); - args.Handled(true); - return; } - - if (point.Properties().IsLeftButtonPressed()) + else if (point.Properties().IsLeftButtonPressed()) { - auto lock = _terminal->LockForWriting(); - - const auto cursorPosition = point.Position(); - const auto terminalPosition = _GetTerminalPosition(cursorPosition); - + // Update the selection appropriately // handle ALT key _terminal->SetBlockSelection(altEnabled); - auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp()); + const auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp()); // This formula enables the number of clicks to cycle properly between single-, double-, and triple-click. // To increase the number of acceptable click states, simply increment MAX_CLICK_COUNT and add another if-statement @@ -1346,61 +1353,49 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation mode = ::Terminal::SelectionExpansionMode::Line; } - if (ctrlEnabled && multiClickMapper == 1 && - !(_terminal->GetHyperlinkAtPosition(terminalPosition).empty())) + // Capture the position of the first click when no selection is active + if (mode == ::Terminal::SelectionExpansionMode::Cell && !_terminal->IsSelectionActive()) { - _HyperlinkHandler(_terminal->GetHyperlinkAtPosition(terminalPosition)); + _singleClickTouchdownPos = cursorPosition; + _lastMouseClickPosNoSelection = cursorPosition; } - else - { - // Update the selection appropriately - // Capture the position of the first click when no selection is active - if (mode == ::Terminal::SelectionExpansionMode::Cell && !_terminal->IsSelectionActive()) - { - _singleClickTouchdownPos = cursorPosition; - _lastMouseClickPosNoSelection = cursorPosition; - } + // We reset the active selection if one of the conditions apply: + // - shift is not held + // - GH#9384: the position is the same as of the first click starting the selection + // (we need to reset selection on double-click or triple-click, so it captures the word or the line, + // rather than extending the selection) + if (_terminal->IsSelectionActive() && (!shiftEnabled || _lastMouseClickPosNoSelection == cursorPosition)) + { + // Reset the selection + _terminal->ClearSelection(); + _selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update + } - // We reset the active selection if one of the conditions apply: - // - shift is not held - // - GH#9384: the position is the same as of the first click starting the selection - // (we need to reset selection on double-click or triple-click, so it captures the word or the line, - // rather than extending the selection) - if (_terminal->IsSelectionActive() && (!shiftEnabled || _lastMouseClickPosNoSelection == cursorPosition)) + if (shiftEnabled) + { + if (_terminal->IsSelectionActive()) { - // Reset the selection - _terminal->ClearSelection(); - _selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update + // If there is a selection we extend it using the selection mode + // (expand the "end"selection point) + _terminal->SetSelectionEnd(terminalPosition, mode); } - - if (shiftEnabled) + else { - if (_terminal->IsSelectionActive()) - { - // If there is a selection we extend it using the selection mode - // (expand the "end"selection point) - _terminal->SetSelectionEnd(terminalPosition, mode); - } - else - { - // If there is no selection we establish it using the selected mode - // (expand both "start" and "end" selection points) - _terminal->MultiClickSelection(terminalPosition, mode); - } - _selectionNeedsToBeCopied = true; + // If there is no selection we establish it using the selected mode + // (expand both "start" and "end" selection points) + _terminal->MultiClickSelection(terminalPosition, mode); } - - _lastMouseClickTimestamp = point.Timestamp(); - _lastMouseClickPos = cursorPosition; - _renderer->TriggerSelection(); + _selectionNeedsToBeCopied = true; } + + _renderer->TriggerSelection(); } else if (point.Properties().IsRightButtonPressed()) { - // CopyOnSelect right click always pastes if (_settings.CopyOnSelect() || !_terminal->IsSelectionActive()) { + // CopyOnSelect right click always pastes PasteTextFromClipboard(); } else @@ -1450,11 +1445,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation if (_focused && !_isReadOnly && _CanSendVTMouseInput()) { _TrySendMouseEvent(point); - args.Handled(true); - return; } - - if (_focused && point.Properties().IsLeftButtonPressed()) + else if (_focused && point.Properties().IsLeftButtonPressed()) { auto lock = _terminal->LockForWriting(); @@ -3038,7 +3030,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } // Method Description: - // - Returns the number of clicks that occurred (double and triple click support) + // - Returns the number of clicks that occurred (double and triple click support). + // Every call to this function registers a click. // Arguments: // - clickPos: the (x,y) position of a given cursor (i.e.: mouse cursor). // NOTE: origin (0,0) is top-left. @@ -3053,13 +3046,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation THROW_IF_FAILED(UInt64Sub(clickTime, _lastMouseClickTimestamp, &delta)); if (clickPos != _lastMouseClickPos || delta > _multiClickTimer) { - // exit early. This is a single click. _multiClickCounter = 1; } else { _multiClickCounter++; } + + _lastMouseClickTimestamp = clickTime; + _lastMouseClickPos = clickPos; return _multiClickCounter; } diff --git a/src/cascadia/TerminalControl/packages.config b/src/cascadia/TerminalControl/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/TerminalControl/packages.config +++ b/src/cascadia/TerminalControl/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalCore/packages.config b/src/cascadia/TerminalCore/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/TerminalCore/packages.config +++ b/src/cascadia/TerminalCore/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalSettingsEditor/packages.config b/src/cascadia/TerminalSettingsEditor/packages.config index 21a3799710c..04d3c357526 100644 --- a/src/cascadia/TerminalSettingsEditor/packages.config +++ b/src/cascadia/TerminalSettingsEditor/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/cascadia/TerminalSettingsModel/packages.config b/src/cascadia/TerminalSettingsModel/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/TerminalSettingsModel/packages.config +++ b/src/cascadia/TerminalSettingsModel/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/WinRTUtils/packages.config b/src/cascadia/WinRTUtils/packages.config index 8a013cf32b2..13c85cc69d9 100644 --- a/src/cascadia/WinRTUtils/packages.config +++ b/src/cascadia/WinRTUtils/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/WindowsTerminal/packages.config b/src/cascadia/WindowsTerminal/packages.config index d03d0841261..9efba8e3cac 100644 --- a/src/cascadia/WindowsTerminal/packages.config +++ b/src/cascadia/WindowsTerminal/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/cascadia/WindowsTerminalUniversal/packages.config b/src/cascadia/WindowsTerminalUniversal/packages.config index ac2d997a4a5..6b762882f3e 100644 --- a/src/cascadia/WindowsTerminalUniversal/packages.config +++ b/src/cascadia/WindowsTerminalUniversal/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/common.build.pre.props b/src/common.build.pre.props index 52e19f02552..2beeec1f544 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -88,7 +88,6 @@ _WINDOWS;EXTERNAL_BUILD;%(PreprocessorDefinitions) true precomp.h - $(IntDir)$(TargetName).pdb ProgramDatabase $(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;$(SolutionDir)\oss\boost\boost_1_73_0;%(AdditionalIncludeDirectories); true @@ -102,9 +101,13 @@ EXTERNAL_BUILD;_UNICODE;UNICODE;%(PreprocessorDefinitions) - $(OutDir)$(TargetName)FullPDB.pdb + $(OutDir)$(TargetName).pdb Windows true + + DebugFull @@ -119,9 +122,6 @@ Disabled _DEBUG;DBG;%(PreprocessorDefinitions) - - true - @@ -171,6 +171,6 @@ This project references git submodule(s) that are missing on this computer. Use `git submodule update --init --recursive` to download them. For more information, see https://github.com/microsoft/terminal#building-the-code. - + diff --git a/src/cppwinrt.build.post.props b/src/cppwinrt.build.post.props index 733ff13ab53..d28604580eb 100644 --- a/src/cppwinrt.build.post.props +++ b/src/cppwinrt.build.post.props @@ -3,13 +3,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 2f2933a985d..2d9b38f9362 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -8,7 +8,7 @@ - + diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 5ebacb48bbe..88a7d85c960 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -53,10 +53,10 @@ try // Check if this conhost is allowed to delegate its activities to another. // If so, look up the registered default console handler. bool isEnabled = false; - if (SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(isEnabled) && isEnabled)) + if (SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(isEnabled)) && isEnabled) { IID delegationClsid; - if (SUCCEEDED(DelegationConfig::s_GetConsole(delegationClsid))) + if (SUCCEEDED(DelegationConfig::s_GetDefaultConsoleId(delegationClsid))) { Globals.handoffConsoleClsid = delegationClsid; } diff --git a/src/inc/til/pmr.h b/src/inc/til/pmr.h index 7a0c61bc7aa..a900449c5e5 100644 --- a/src/inc/til/pmr.h +++ b/src/inc/til/pmr.h @@ -21,7 +21,7 @@ namespace til::pmr // get_default_resource below forces it to be included by the compiler. // I *believe* that if the VC++ Runtime is updated to include the PMR source, // this will safely no-op (since it's an ALTERNATENAME). -#if defined(_M_AMD64) || defined(_M_ARM64) +#if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_ARM) #pragma comment(linker, "/ALTERNATENAME:_Aligned_get_default_resource=TIL_PMR_Aligned_get_default_resource") #else #pragma comment(linker, "/ALTERNATENAME:__Aligned_get_default_resource=_TIL_PMR_Aligned_get_default_resource") diff --git a/src/propsheet/TerminalPropsheetPage.cpp b/src/propsheet/TerminalPropsheetPage.cpp index 5d1c6c6c682..1ac3a1a826f 100644 --- a/src/propsheet/TerminalPropsheetPage.cpp +++ b/src/propsheet/TerminalPropsheetPage.cpp @@ -6,6 +6,8 @@ #include "OptionsPage.h" // For InitializeCursorSize #include "ColorControl.h" #include +#include "../propslib/DelegationConfig.hpp" +#include "../types/inc/User32Utils.hpp" // From conattrs.h const COLORREF INVALID_COLOR = 0xffffffff; @@ -87,6 +89,41 @@ void _UpdateTextAndScroll(const HWND hDlg, SendDlgItemMessage(hDlg, scrollItem, UDM_SETPOS, 0, MAKELONG(value, 0)); } +void _PrepDefAppCombo(const HWND hDlg, + const int dlgItem, + const std::vector& list, + const DelegationConfig::DelegationPackage& selected) +{ + const HWND hCombo = GetDlgItem(hDlg, dlgItem); + ComboBox_ResetContent(hCombo); + + DWORD selectedIndex = 0; + for (DWORD i = 0; i < gsl::narrow(list.size()); ++i) + { + auto& item = list[i]; + + // An empty CLSID is a sentinel for the inbox console. + if (item.terminal.clsid == CLSID{ 0 }) + { + const auto name = GetStringResource(IDS_TERMINAL_DEF_INBOX); + ComboBox_AddString(hCombo, name.c_str()); + } + else + { + ComboBox_AddString(hCombo, item.terminal.name.c_str()); + } + ComboBox_SetItemData(hCombo, i, &item); + if (selected == item) + { + selectedIndex = i; + } + } + + ComboBox_SetCurSel(hCombo, selectedIndex); + + ComboBox_Enable(hCombo, TRUE); +} + bool InitTerminalDialog(const HWND hDlg) noexcept { // Initialize the global handle to this dialog @@ -181,6 +218,11 @@ bool InitTerminalDialog(const HWND hDlg) noexcept CheckDlgButton(hDlg, IDD_DISABLE_SCROLLFORWARD, gpStateInfo->TerminalScrolling); + _PrepDefAppCombo(hDlg, + IDD_TERMINAL_COMBO_DEFTERM, + g_availablePackages, + g_selectedPackage); + return true; } @@ -341,11 +383,27 @@ bool TerminalDlgCommand(const HWND hDlg, const WORD item, const WORD command) no break; } case IDD_DISABLE_SCROLLFORWARD: + { gpStateInfo->TerminalScrolling = IsDlgButtonChecked(hDlg, IDD_DISABLE_SCROLLFORWARD); UpdateApplyButton(hDlg); handled = true; break; } + case IDD_TERMINAL_COMBO_DEFTERM: + { + if (CBN_SELCHANGE == command) + { + const HWND hCombo = GetDlgItem(hDlg, IDD_TERMINAL_COMBO_DEFTERM); + const DWORD comboItem = ComboBox_GetCurSel(hCombo); + if (CB_ERR != comboItem) + { + const auto pPackage = reinterpret_cast(ComboBox_GetItemData(hCombo, comboItem)); + g_selectedPackage = *pPackage; + } + } + break; + } + } return handled; } diff --git a/src/propsheet/console.cpp b/src/propsheet/console.cpp index 8a32a15faa9..0d1a3bad089 100644 --- a/src/propsheet/console.cpp +++ b/src/propsheet/console.cpp @@ -19,6 +19,8 @@ Revision History: #include "precomp.h" +#include "conint.h" + #pragma hdrstop UINT gnCurrentPage; @@ -94,6 +96,11 @@ void SaveConsoleSettingsIfNeeded(const HWND hwnd) gpStateInfo->FaceName[0] = TEXT('\0'); } + if (g_defAppEnabled) + { + LOG_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(g_selectedPackage)); + } + if (gpStateInfo->LinkTitle != nullptr) { SetGlobalRegistryValues(); @@ -545,7 +552,14 @@ BOOL PopulatePropSheetPageArray(_Out_writes_(cPsps) PROPSHEETPAGE* pPsp, const s { pTerminalPage->dwSize = sizeof(PROPSHEETPAGE); pTerminalPage->hInstance = ghInstance; - pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL); + if (g_defAppEnabled) + { + pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL_WITH_DEFTERM); + } + else + { + pTerminalPage->pszTemplate = MAKEINTRESOURCE(DID_TERMINAL); + } pTerminalPage->pfnDlgProc = TerminalDlgProc; pTerminalPage->lParam = TERMINAL_PAGE_INDEX; pTerminalPage->dwFlags = PSP_DEFAULT; @@ -611,6 +625,15 @@ INT_PTR ConsolePropertySheet(__in HWND hWnd, __in PCONSOLE_STATE_INFO pStateInfo // since we just triggered font enumeration, recreate our font handles to adapt for DPI RecreateFontHandles(hWnd); + // + // Find the available default console/terminal packages + // + + if (SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(g_defAppEnabled)) && g_defAppEnabled) + { + LOG_IF_FAILED(DelegationConfig::s_GetAvailablePackages(g_availablePackages, g_selectedPackage)); + } + // // Get the current page number // diff --git a/src/propsheet/console.h b/src/propsheet/console.h index db33428a373..5eb7e91a01f 100644 --- a/src/propsheet/console.h +++ b/src/propsheet/console.h @@ -58,6 +58,7 @@ Revision History: // unused 16 #define IDS_TOOLTIP_OPACITY 17 #define IDS_TOOLTIP_INTERCEPT_COPY_PASTE 18 +#define IDS_TERMINAL_DEF_INBOX 19 // clang-format on void MakeAltRasterFont( diff --git a/src/propsheet/console.rc b/src/propsheet/console.rc index dd7bc9df01e..bf1c06e3f5b 100644 --- a/src/propsheet/console.rc +++ b/src/propsheet/console.rc @@ -534,6 +534,17 @@ END #define T_SCROLL_W 100 #define T_SCROLL_H 40 +// default application group box +#define T_DEFAPP_X T_CCOLOR_X +#define T_DEFAPP_Y T_SCROLL_Y +#define T_DEFAPP_W T_CCOLOR_W +#define T_DEFAPP_H T_SCROLL_H + +#define T_DEFTERM_X (T_DEFAPP_X+P_1) +#define T_DEFTERM_Y (T_DEFAPP_Y+P_2) +#define T_DEFTERM_W (T_DEFAPP_W-P_4-P_4) +#define T_DEFTERM_H 25 + #define UPDOWN_STYLES (UDS_AUTOBUDDY | UDS_SETBUDDYINT | UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_ALIGNRIGHT) DID_TERMINAL DIALOG 0, 0, 245, 226 CAPTION " Terminal " @@ -624,6 +635,98 @@ BEGIN IDD_HELP_TERMINAL, "SysLink", WS_TABSTOP, 10, 225, 200, 10 END +DID_TERMINAL_WITH_DEFTERM DIALOG 0, 0, 245, 226 +CAPTION " Terminal " +STYLE WS_VISIBLE | WS_CAPTION | WS_CHILD | DS_MODALFRAME +FONT 8,"MS Shell Dlg" +BEGIN + + // GROUPBOX text, id, x, y, width, height [, style [, extended-style]] + // CONTROL text, id, class, style, x, y, width, height [, extended-style] + + GROUPBOX "Terminal Colors", -1, T_COLORS_X, T_COLORS_Y, T_COLORS_W, T_COLORS_H, WS_GROUP + + AUTOCHECKBOX "Use Separate Foreground", IDD_USE_TERMINAL_FG, T_COLORS_X+P_1, T_COLORS_CHECK_Y, T_COLORS_FG_W, 10 + + CONTROL "", IDD_TERMINAL_FGCOLOR, "SimpleColor", WS_BORDER | WS_CHILD | WS_GROUP , + T_COLORS_X+P_2, T_COLORS_RED_Y, COLOR_SIZE, COLOR_SIZE + + LTEXT "Red:", -1, T_COLORS_FG_TEXT_X, T_COLORS_RED_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_FG_RED, T_COLORS_FG_INPUT_X, T_COLORS_RED_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_FG_REDSCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Green:", -1, T_COLORS_FG_TEXT_X, T_COLORS_GREEN_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_FG_GREEN, T_COLORS_FG_INPUT_X, T_COLORS_GREEN_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_FG_GREENSCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Blue:", -1, T_COLORS_FG_TEXT_X, T_COLORS_BLUE_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_FG_BLUE, T_COLORS_FG_INPUT_X, T_COLORS_BLUE_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_FG_BLUESCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + AUTOCHECKBOX "Use Separate Background", IDD_USE_TERMINAL_BG, T_COLORS_BG_X, T_COLORS_CHECK_Y, T_COLORS_FG_W, 10 + + CONTROL "", IDD_TERMINAL_BGCOLOR, "SimpleColor", WS_BORDER | WS_CHILD | WS_GROUP , + T_COLORS_BG_X, T_COLORS_RED_Y, COLOR_SIZE, COLOR_SIZE + + LTEXT "Red:", -1, T_COLORS_BG_TEXT_X, T_COLORS_RED_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_BG_RED, T_COLORS_BG_INPUT_X, T_COLORS_RED_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_BG_REDSCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Green:", -1, T_COLORS_BG_TEXT_X, T_COLORS_GREEN_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_BG_GREEN, T_COLORS_BG_INPUT_X, T_COLORS_GREEN_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_BG_GREENSCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Blue:", -1, T_COLORS_BG_TEXT_X, T_COLORS_BLUE_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_BG_BLUE, T_COLORS_BG_INPUT_X, T_COLORS_BLUE_Y, T_COLORS_EDIT_W, T_COLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_BG_BLUESCROLL, UPDOWN_CLASS, + UPDOWN_STYLES, 0, 0, 0, 0 + + + GROUPBOX "Cursor Shape", -1, T_CSTYLE_X, T_CSTYLE_Y, T_CSTYLE_W, T_CSTYLE_H + AUTORADIOBUTTON "Use Legacy Style", IDD_TERMINAL_LEGACY_CURSOR, T_CSTYLE_X+P_1, T_CSTYLE_R_1_Y, T_CSTYLE_R_W, T_CSTYLE_R_H, WS_TABSTOP|WS_GROUP + AUTORADIOBUTTON "Underscore", IDD_TERMINAL_UNDERSCORE, T_CSTYLE_X+P_1, T_CSTYLE_R_2_Y, T_CSTYLE_R_W, T_CSTYLE_R_H, + AUTORADIOBUTTON "Vertical Bar", IDD_TERMINAL_VERTBAR, T_CSTYLE_X+P_1, T_CSTYLE_R_3_Y, T_CSTYLE_R_W, T_CSTYLE_R_H, + AUTORADIOBUTTON "Empty Box", IDD_TERMINAL_EMPTYBOX, T_CSTYLE_X+P_1, T_CSTYLE_R_4_Y, T_CSTYLE_R_W, T_CSTYLE_R_H, + AUTORADIOBUTTON "Solid Box", IDD_TERMINAL_SOLIDBOX, T_CSTYLE_X+P_1, T_CSTYLE_R_5_Y, T_CSTYLE_R_W, T_CSTYLE_R_H, + + + GROUPBOX "Cursor Colors", -1, T_CCOLOR_X, T_CCOLOR_Y, T_CCOLOR_W, T_CCOLOR_H, WS_GROUP + + AUTORADIOBUTTON "Inverse Color", IDD_TERMINAL_INVERSE_CURSOR, T_CCOLOR_X+P_1, T_CSTYLE_R_1_Y, T_CCOLOR_R_W, T_CSTYLE_R_H, WS_TABSTOP|WS_GROUP + + AUTORADIOBUTTON "Use Color", IDD_TERMINAL_CURSOR_USECOLOR, T_CCOLOR_X+P_1, T_CSTYLE_R_2_Y, T_CCOLOR_R_W, T_CSTYLE_R_H, + + CONTROL "", IDD_TERMINAL_CURSOR_COLOR, "SimpleColor", WS_BORDER | WS_CHILD | WS_GROUP, + T_CCOLOR_X+P_2, T_CSTYLE_R_3_Y, COLOR_SIZE, COLOR_SIZE + + LTEXT "Red:", -1, T_CCOLOR_TEXT_X, T_CCOLOR_R_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_CURSOR_RED, T_CCOLOR_EDIT_X, T_CCOLOR_R_Y, T_CCOLORS_EDIT_W, T_CCOLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_CURSOR_REDSCROLL, UPDOWN_CLASS, UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Green:", -1, T_CCOLOR_TEXT_X, T_CCOLOR_G_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_CURSOR_GREEN, T_CCOLOR_EDIT_X, T_CCOLOR_G_Y, T_CCOLORS_EDIT_W, T_CCOLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_CURSOR_GREENSCROLL, UPDOWN_CLASS, UPDOWN_STYLES, 0, 0, 0, 0 + + LTEXT "Blue:", -1, T_CCOLOR_TEXT_X, T_CCOLOR_B_Y, T_COLORS_TEXT_W, 9 + EDITTEXT IDD_TERMINAL_CURSOR_BLUE, T_CCOLOR_EDIT_X, T_CCOLOR_B_Y, T_CCOLORS_EDIT_W, T_CCOLORS_EDIT_H, WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL + CONTROL "", IDD_TERMINAL_CURSOR_BLUESCROLL, UPDOWN_CLASS, UPDOWN_STYLES, 0, 0, 0, 0 + + + GROUPBOX "Terminal Scrolling", -1, T_SCROLL_X, T_SCROLL_Y, T_SCROLL_W, T_SCROLL_H + AUTOCHECKBOX "Disable Scroll-Forward", IDD_DISABLE_SCROLLFORWARD, T_SCROLL_X+P_1, T_SCROLL_Y+P_2, T_SCROLL_W-P_4-P_4, 10 + + GROUPBOX "Default Terminal Application", -1, T_DEFAPP_X, T_DEFAPP_Y, T_DEFAPP_W, T_DEFAPP_H + COMBOBOX IDD_TERMINAL_COMBO_DEFTERM, T_DEFTERM_X+P_0, T_DEFTERM_Y, T_DEFTERM_W, 10, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + + CONTROL "Find out more about experimental terminal settings", + IDD_HELP_TERMINAL, "SysLink", WS_TABSTOP, 10, 225, 200, 10 +END + // // Strings @@ -646,6 +749,7 @@ BEGIN IDS_TOOLTIP_EDIT_KEYS, "Enable enhanced keyboard editing on command line." IDS_TOOLTIP_OPACITY, "Adjust the transparency of the console window." IDS_TOOLTIP_INTERCEPT_COPY_PASTE, "Use Ctrl+Shift+C/V as copy/paste shortcuts, regardless of input mode" + IDS_TERMINAL_DEF_INBOX, "Windows Console Host" END diff --git a/src/propsheet/dialogs.h b/src/propsheet/dialogs.h index 60949a070ad..9200f9090ef 100644 --- a/src/propsheet/dialogs.h +++ b/src/propsheet/dialogs.h @@ -164,6 +164,8 @@ Revision History: #define IDD_TERMINAL_CURSOR_GREEN 630 #define IDD_TERMINAL_CURSOR_BLUE 631 #define IDD_HELP_TERMINAL 632 +#define DID_TERMINAL_WITH_DEFTERM 640 +#define IDD_TERMINAL_COMBO_DEFTERM 641 #define BM_TRUETYPE_ICON 1000 diff --git a/src/propsheet/globals.cpp b/src/propsheet/globals.cpp index 980b60a263b..e42d0bbf4f6 100644 --- a/src/propsheet/globals.cpp +++ b/src/propsheet/globals.cpp @@ -59,3 +59,7 @@ COLORREF g_fakeCursorColor = RGB(242, 242, 242); // Default bright white HWND g_hTerminalDlg = static_cast(INVALID_HANDLE_VALUE); HWND g_hOptionsDlg = static_cast(INVALID_HANDLE_VALUE); + +bool g_defAppEnabled = false; +std::vector g_availablePackages; +DelegationConfig::DelegationPackage g_selectedPackage; diff --git a/src/propsheet/globals.h b/src/propsheet/globals.h index 5ae9bb2fa16..1ecfb407e31 100644 --- a/src/propsheet/globals.h +++ b/src/propsheet/globals.h @@ -14,6 +14,7 @@ #pragma once #include "font.h" +#include "../propslib/DelegationConfig.hpp" extern HINSTANCE ghInstance; extern PCONSOLE_STATE_INFO gpStateInfo; @@ -54,3 +55,7 @@ extern COLORREF g_fakeCursorColor; extern HWND g_hTerminalDlg; extern HWND g_hOptionsDlg; + +extern bool g_defAppEnabled; +extern std::vector g_availablePackages; +extern DelegationConfig::DelegationPackage g_selectedPackage; diff --git a/src/propsheet/propsheet.vcxproj b/src/propsheet/propsheet.vcxproj index d981afa01c1..47a137dc774 100644 --- a/src/propsheet/propsheet.vcxproj +++ b/src/propsheet/propsheet.vcxproj @@ -53,6 +53,9 @@ {345fd5a4-b32b-4f29-bd1c-b033bd2c35cc} + + {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00} + diff --git a/src/propsheet/sources b/src/propsheet/sources index 10cd297de00..4a01d89ef0a 100644 --- a/src/propsheet/sources +++ b/src/propsheet/sources @@ -84,6 +84,8 @@ TARGETLIBS = \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecore_internal.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecoreuap_internal.lib \ $(ONECOREUAP_INTERNAL_SDK_LIB_PATH)\onecoreuapuuid.lib \ + $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ @@ -100,10 +102,39 @@ TARGETLIBS = \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-syscolors-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib\ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ $(ONECOREBASE_INTERNAL_LIB_VPATH_L)\ext-ms-win-appmodel-shellexecute-l1.lib \ $(ONECOREWINDOWS_INTERNAL_LIB_PATH_L)\ext-ms-win-gdi-internal-desktop-l1-1-1.lib \ + $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\propslib\$(O)\conprops.lib \ +DELAYLOAD = \ + ext-ms-win-dwmapi-ext-l1.dll; \ + ext-ms-win-edputil-policy-l1.dll; \ + ext-ms-win-uxtheme-themes-l1.dll; \ + ext-ms-win-shell32-shellfolders-l1.dll; \ + ext-ms-win-gdi-dc-l1.dll; \ + ext-ms-win-gdi-dc-create-l1.dll; \ + ext-ms-win-gdi-draw-l1.dll; \ + ext-ms-win-gdi-font-l1.dll; \ + ext-ms-win-ntuser-dialogbox-l1.dll; \ + ext-ms-win-ntuser-draw-l1.dll; \ + ext-ms-win-ntuser-keyboard-l1.dll; \ + ext-ms-win-ntuser-gui-l1.dll; \ + ext-ms-win-ntuser-misc-l1.dll; \ + ext-ms-win-ntuser-window-l1.dll; \ + ext-ms-win-rtcore-gdi-object-l1.dll; \ + ext-ms-win-rtcore-ntuser-cursor-l1.dll; \ + ext-ms-win-rtcore-ntuser-dc-access-l1.dll; \ + ext-ms-win-rtcore-ntuser-syscolors-l1.dll; \ + ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ + ext-ms-win-shell-shell32-l1.dll; \ + ext-ms-win-appmodel-shellexecute-l1.dll; \ + ext-ms-win-gdi-internal-desktop-l1.dll; \ + + +DLOAD_ERROR_HANDLER = kernelbase + # ------------------------------------- # Side-by-side Manifesting # ------------------------------------- diff --git a/src/propslib/DelegationConfig.cpp b/src/propslib/DelegationConfig.cpp index 8ad4698733f..6a6f8ae9221 100644 --- a/src/propslib/DelegationConfig.cpp +++ b/src/propslib/DelegationConfig.cpp @@ -33,7 +33,15 @@ HRESULT _lookupCatalog(PCWSTR extensionName, std::vector& vec) noexcept { vec.clear(); - auto coinit = wil::CoInitializeEx(COINIT_MULTITHREADED); + T useInbox = { 0 }; + useInbox.clsid = { 0 }; + // CLSID of 0 will be sentinel to say "inbox console" or something. + // The UI displaying this information will have to go look up appropriate strings + // to convey that message. + + vec.push_back(useInbox); + + auto coinit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED); ComPtr catalogStatics; RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_AppExtensions_AppExtensionCatalog).Get(), &catalogStatics)); @@ -59,24 +67,34 @@ HRESULT _lookupCatalog(PCWSTR extensionName, std::vector& vec) noexcept ComPtr extensionPackage; RETURN_IF_FAILED(extension->get_Package(&extensionPackage)); + ComPtr extensionPackage2; + RETURN_IF_FAILED(extensionPackage.As(&extensionPackage2)); + ComPtr extensionPackageId; RETURN_IF_FAILED(extensionPackage->get_Id(&extensionPackageId)); HString publisherId; RETURN_IF_FAILED(extensionPackageId->get_PublisherId(publisherId.GetAddressOf())); - // PackageId.Name HString name; - RETURN_IF_FAILED(extensionPackageId->get_Name(name.GetAddressOf())); - + RETURN_IF_FAILED(extensionPackage2->get_DisplayName(name.GetAddressOf())); extensionMetadata.name = std::wstring{ name.GetRawBuffer(nullptr) }; - // PackageId.Version HString publisher; - RETURN_IF_FAILED(extensionPackageId->get_Publisher(publisher.GetAddressOf())); - + RETURN_IF_FAILED(extensionPackage2->get_PublisherDisplayName(publisher.GetAddressOf())); extensionMetadata.author = std::wstring{ publisher.GetRawBuffer(nullptr) }; + HString pfn; + RETURN_IF_FAILED(extensionPackageId->get_FamilyName(pfn.GetAddressOf())); + extensionMetadata.pfn = std::wstring{ pfn.GetRawBuffer(nullptr) }; + + PackageVersion version; + RETURN_IF_FAILED(extensionPackageId->get_Version(&version)); + extensionMetadata.version.major = version.Major; + extensionMetadata.version.minor = version.Minor; + extensionMetadata.version.build = version.Build; + extensionMetadata.version.revision = version.Revision; + // Fetch the custom properties XML out of the extension information ComPtr> propertiesOperation; RETURN_IF_FAILED(extension->GetExtensionPropertiesAsync(&propertiesOperation)); @@ -149,23 +167,91 @@ try } CATCH_RETURN() -[[nodiscard]] HRESULT DelegationConfig::s_SetConsole(const DelegationConsole& console) noexcept +[[nodiscard]] HRESULT DelegationConfig::s_GetAvailablePackages(std::vector& packages, DelegationPackage& defPackage) noexcept +try +{ + packages.clear(); + + std::vector consoles; + RETURN_IF_FAILED(s_GetAvailableConsoles(consoles)); + + std::vector terminals; + RETURN_IF_FAILED(s_GetAvailableTerminals(terminals)); + + // TODO: I hate this algorithm (it's bad performance), but I couldn't + // find an AppModel interface that would let me look up all the extensions + // in one package. + for (auto& term : terminals) + { + for (auto& con : consoles) + { + if (term.IsFromSamePackage(con)) + { + DelegationPackage pkg; + pkg.terminal = term; + pkg.console = con; + packages.push_back(pkg); + break; + } + } + } + + // We should find at least one package. + RETURN_HR_IF(E_FAIL, packages.empty()); + + // We also find the default here while we have the list of available ones so + // we can return the opaque structure instead of the raw IID. + IID defCon; + RETURN_IF_FAILED(s_GetDefaultConsoleId(defCon)); + IID defTerm; + RETURN_IF_FAILED(s_GetDefaultTerminalId(defTerm)); + + // The default one is the 0th one because that's supposed to be the inbox conhost one. + DelegationPackage chosenPackage = packages.at(0); + + // Search through and find a package that matches. If we failed to match because + // it's torn across multiple or something not in the catalog, we'll offer the inbox conhost one. + for (auto& pkg : packages) + { + if (pkg.console.clsid == defCon && pkg.terminal.clsid == defTerm) + { + chosenPackage = pkg; + break; + } + } + + defPackage = chosenPackage; + + return S_OK; +} +CATCH_RETURN() + +[[nodiscard]] HRESULT DelegationConfig::s_SetDefaultConsoleById(const IID& iid) noexcept { - return s_Set(DELEGATION_CONSOLE_KEY_NAME, console.clsid); + return s_Set(DELEGATION_CONSOLE_KEY_NAME, iid); } -[[nodiscard]] HRESULT DelegationConfig::s_SetTerminal(const DelegationTerminal& terminal) noexcept +[[nodiscard]] HRESULT DelegationConfig::s_SetDefaultTerminalById(const IID& iid) noexcept { - return s_Set(DELEGATION_TERMINAL_KEY_NAME, terminal.clsid); + return s_Set(DELEGATION_TERMINAL_KEY_NAME, iid); +} + +[[nodiscard]] HRESULT DelegationConfig::s_SetDefaultByPackage(const DelegationPackage& package) noexcept +{ + RETURN_IF_FAILED(s_SetDefaultConsoleById(package.console.clsid)); + RETURN_IF_FAILED(s_SetDefaultTerminalById(package.terminal.clsid)); + return S_OK; } -[[nodiscard]] HRESULT DelegationConfig::s_GetConsole(IID& iid) noexcept +[[nodiscard]] HRESULT DelegationConfig::s_GetDefaultConsoleId(IID& iid) noexcept { + iid = { 0 }; return s_Get(DELEGATION_CONSOLE_KEY_NAME, iid); } -[[nodiscard]] HRESULT DelegationConfig::s_GetTerminal(IID& iid) noexcept +[[nodiscard]] HRESULT DelegationConfig::s_GetDefaultTerminalId(IID& iid) noexcept { + iid = { 0 }; return s_Get(DELEGATION_TERMINAL_KEY_NAME, iid); } @@ -192,7 +278,7 @@ CATCH_RETURN() RETURN_NTSTATUS(result); } - auto buffer = std::make_unique(bytesNeeded / sizeof(wchar_t)); + auto buffer = std::make_unique(bytesNeeded / sizeof(wchar_t) + 1); DWORD bytesUsed = 0; @@ -222,7 +308,7 @@ try wil::unique_cotaskmem_string str; RETURN_IF_FAILED(StringFromCLSID(clsid, &str)); - RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_SetValue(startupKey.get(), value, REG_SZ, reinterpret_cast(str.get()), gsl::narrow(wcslen(str.get() + 1) * sizeof(wchar_t)))); + RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_SetValue(startupKey.get(), value, REG_SZ, reinterpret_cast(str.get()), gsl::narrow(wcslen(str.get()) * sizeof(wchar_t)))); return S_OK; } diff --git a/src/propslib/DelegationConfig.hpp b/src/propslib/DelegationConfig.hpp index 7136395945e..9bc4ad89bb7 100644 --- a/src/propslib/DelegationConfig.hpp +++ b/src/propslib/DelegationConfig.hpp @@ -18,11 +18,46 @@ Author(s): class DelegationConfig { public: + struct PkgVersion + { + unsigned short major; + unsigned short minor; + unsigned short build; + unsigned short revision; + + bool operator==(const PkgVersion& other) const + { + return major == other.major && + minor == other.minor && + build == other.build && + revision == other.revision; + } + }; + struct DelegationBase { CLSID clsid; std::wstring name; std::wstring author; + std::wstring pfn; + PkgVersion version; + + bool IsFromSamePackage(const DelegationBase& other) const + { + return name == other.name && + author == other.author && + pfn == other.pfn && + version == other.version; + } + + bool operator==(const DelegationBase& other) const + { + return clsid == other.clsid && + name == other.name && + author == other.author && + pfn == other.pfn && + version == other.version; + } }; struct DelegationConsole : public DelegationBase @@ -33,16 +68,32 @@ class DelegationConfig { }; - [[nodiscard]] static HRESULT s_GetAvailableConsoles(std::vector& consoles) noexcept; - [[nodiscard]] static HRESULT s_GetAvailableTerminals(std::vector& terminals) noexcept; + struct DelegationPackage + { + DelegationConsole console; + DelegationTerminal terminal; + + bool operator==(const DelegationPackage& other) const + { + return console == other.console && + terminal == other.terminal; + } + }; + + [[nodiscard]] static HRESULT s_GetAvailablePackages(std::vector& packages, DelegationPackage& default) noexcept; - [[nodiscard]] static HRESULT s_SetConsole(const DelegationConsole& console) noexcept; - [[nodiscard]] static HRESULT s_SetTerminal(const DelegationTerminal& terminal) noexcept; + [[nodiscard]] static HRESULT s_SetDefaultByPackage(const DelegationPackage& pkg) noexcept; - [[nodiscard]] static HRESULT s_GetConsole(IID& iid) noexcept; - [[nodiscard]] static HRESULT s_GetTerminal(IID& iid) noexcept; + [[nodiscard]] static HRESULT s_GetDefaultConsoleId(IID& iid) noexcept; + [[nodiscard]] static HRESULT s_GetDefaultTerminalId(IID& iid) noexcept; private: + [[nodiscard]] static HRESULT s_GetAvailableConsoles(std::vector& consoles) noexcept; + [[nodiscard]] static HRESULT s_GetAvailableTerminals(std::vector& terminals) noexcept; + + [[nodiscard]] static HRESULT s_SetDefaultConsoleById(const IID& iid) noexcept; + [[nodiscard]] static HRESULT s_SetDefaultTerminalById(const IID& iid) noexcept; + [[nodiscard]] static HRESULT s_Get(PCWSTR value, IID& iid) noexcept; [[nodiscard]] static HRESULT s_Set(PCWSTR value, const CLSID clsid) noexcept; }; diff --git a/src/propslib/propslib.vcxproj.filters b/src/propslib/propslib.vcxproj.filters index 05074548724..8d0ae167385 100644 --- a/src/propslib/propslib.vcxproj.filters +++ b/src/propslib/propslib.vcxproj.filters @@ -51,4 +51,7 @@ Header Files + + + \ No newline at end of file diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index 85c8952eb63..03ca3579820 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -138,6 +138,79 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleCloseObject(_In_ PCONSOLE_API_MSG pMessag return pMessage; } +// Routine Description: +// - Uses some information about current console state and +// the incoming process state and preferences to determine +// whether we should attempt to handoff to a registered console. +static bool _shouldAttemptHandoff(const Globals& globals, + const CONSOLE_INFORMATION& gci, + CONSOLE_API_CONNECTINFO& cac) +{ + // This console is already initialized. Do not + // attempt handoff to another one. + // Note you can have a non-attach secondary connect for a child process + // that is supposed to be inheriting the existing console/window from the parent. + if (WI_IsFlagSet(gci.Flags, CONSOLE_INITIALIZED)) + { + return false; + } + + // If this is an AttachConsole message and not occurring + // because of a conclnt!ConsoleInitialize, do not handoff. + // ConsoleApp is FALSE for attach. + if (!cac.ConsoleApp) + { + return false; + } + + // If it is a PTY session, do not attempt handoff. + if (globals.launchArgs.IsHeadless()) + { + return false; + } + + // If we do not have a registered handoff, do not attempt. + if (!globals.handoffConsoleClsid) + { + return false; + } + + // If we're already a target for receiving another handoff, + // do not chain. + if (globals.handoffTarget) + { + return false; + } + + // If the client was started with CREATE_NO_WINDOW to CreateProcess, + // this function will say that it does NOT deserve a visible window. + // Return false. + if (!ConsoleConnectionDeservesVisibleWindow(&cac)) + { + return false; + } + + // If the process is giving us explicit window show information, we need + // to look at which one it is. + if (WI_IsFlagSet(cac.ConsoleInfo.GetStartupFlags(), STARTF_USESHOWWINDOW)) + { + switch (cac.ConsoleInfo.GetShowWindow()) + { + // For all hide or minimize actions, do not hand off. + case SW_HIDE: + case SW_SHOWMINIMIZED: + case SW_MINIMIZE: + case SW_SHOWMINNOACTIVE: + case SW_FORCEMINIMIZE: + return false; + // Intentionally fall through for all others + // like maximize and show to hit the true below. + } + } + + return true; +} + // Routine Description: // - Used when a client application establishes an initial connection to this console server. // - This is supposed to represent accounting for the process, making the appropriate handles, etc. @@ -165,12 +238,9 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API goto Error; } - // If we are NOT a PTY session (headless)... - // we have FOUND a CLSID for a different console to be the default startup handler... - // we are NOT already receiving an inbound console connection handoff... - // and the client app is going to end up showing a window... + // If we pass the tests... // then attempt to delegate the startup to the registered replacement. - if (!Globals.launchArgs.IsHeadless() && Globals.handoffConsoleClsid && !Globals.handoffTarget && ConsoleConnectionDeservesVisibleWindow(&Cac)) + if (_shouldAttemptHandoff(Globals, gci, Cac)) { try {