diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 39e4f10e9..828b3ae3c 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -11,6 +11,7 @@ BUILDBRANCH BUILDMSG BUILDNUMBER BYPOSITION +BYCOMMAND charconv CLASSNOTAVAILABLE cmdletbinding @@ -88,6 +89,7 @@ LSHIFT MENUCOMMAND MENUDATA MENUINFO +MENUITEMINFOW memicmp mptt mov diff --git a/README.md b/README.md index ca416b82c..18523b63b 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ resources useful and interesting: * Windows Terminal Launch: [Build 2019 Session](https://www.youtube.com/watch?v=KMudkRcwjCw) * Run As Radio: [Show 645 - Windows Terminal with Richard - Turner](http://www.runasradio.com/Shows/Show/645) + Turner](https://www.runasradio.com/Shows/Show/645) * Azure Devops Podcast: [Episode 54 - Kayla Cinnamon and Rich Turner on DevOps on the Windows Terminal](http://azuredevopspodcast.clear-measure.com/kayla-cinnamon-and-rich-turner-on-devops-on-the-windows-terminal-team-episode-54) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 98f3de653..874667793 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1478,6 +1478,11 @@ "description": "When set to true, trailing white-spaces will be removed from text in rectangular (block) selection while copied to your clipboard. When set to false, the white-spaces will be preserved.", "type": "boolean" }, + "trimPaste": { + "default": true, + "description": "When enabled, the Terminal will automatically trim trailing whitespace characters when pasting text", + "type": "boolean" + }, "experimental.detectURLs": { "default": true, "description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.", diff --git a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md index 37b1fe4cb..c7dbefeab 100644 --- a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md +++ b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md @@ -268,7 +268,7 @@ Today, if the deserialization of `CascadiaSettings` encounters any errors, an ex To get around this issue, when `CascadiaSettings` encounters a serialization error, it must internally record any pertinent information for that error, and return the simple `CascadiaSettings` as if nothing happened. The consumer must then call `CascadiaSettings::GetErrors()` and `CascadiaSettings::GetWarnings()` to properly - understand whether an error ocurred and how to present that to the user. + understand whether an error occurred and how to present that to the user. #### TerminalApp: Loading and Reloading Changes diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index c44724846..b99c3121d 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -12,8 +12,30 @@ static_assert(alignof(TextAttribute) == 2); // Ensure that we can memcpy() and memmove() the struct for performance. static_assert(std::is_trivially_copyable_v); -BYTE TextAttribute::s_legacyDefaultForeground = 7; -BYTE TextAttribute::s_legacyDefaultBackground = 0; +namespace +{ + constexpr std::array s_initLegacyColorMap(const BYTE defaultIndex) + { + std::array legacyColorMap; + for (auto i = 0u; i < legacyColorMap.size(); i++) + { + const auto legacyIndex = TextColor::TransposeLegacyIndex(i); + gsl::at(legacyColorMap, i) = i == defaultIndex ? TextColor{} : TextColor{ legacyIndex, true }; + } + return legacyColorMap; + } + + BYTE s_legacyDefaultForeground = 7; + BYTE s_legacyDefaultBackground = 0; + BYTE s_ansiDefaultForeground = 7; + BYTE s_ansiDefaultBackground = 0; +} + +// These maps allow for an efficient conversion from a legacy attribute index +// to a TextColor with the corresponding ANSI index, also taking into account +// the legacy index values that need to be converted to a default TextColor. +std::array TextAttribute::s_legacyForegroundColorMap = s_initLegacyColorMap(7); +std::array TextAttribute::s_legacyBackgroundColorMap = s_initLegacyColorMap(0); // Routine Description: // - Sets the legacy attributes which map to and from the default colors. @@ -23,8 +45,22 @@ BYTE TextAttribute::s_legacyDefaultBackground = 0; // - None void TextAttribute::SetLegacyDefaultAttributes(const WORD defaultAttributes) noexcept { + // First we reset the current default color map entries to what they should + // be for a regular translation from a legacy index to an ANSI TextColor. + gsl::at(s_legacyForegroundColorMap, s_legacyDefaultForeground) = TextColor{ s_ansiDefaultForeground, true }; + gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{ s_ansiDefaultBackground, true }; + + // Then we save the new default attribute values and their corresponding + // ANSI translations. We use the latter values to more efficiently handle + // the "VT Quirk" conversion below. s_legacyDefaultForeground = defaultAttributes & FG_ATTRS; s_legacyDefaultBackground = (defaultAttributes & BG_ATTRS) >> 4; + s_ansiDefaultForeground = TextColor::TransposeLegacyIndex(s_legacyDefaultForeground); + s_ansiDefaultBackground = TextColor::TransposeLegacyIndex(s_legacyDefaultBackground); + + // Finally we set the new default color map entries. + gsl::at(s_legacyForegroundColorMap, s_legacyDefaultForeground) = TextColor{}; + gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{}; } // Routine Description: @@ -55,13 +91,13 @@ TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const Te const auto bg{ attribute.GetBackground() }; auto copy{ attribute }; if (fg.IsIndex16() && - attribute.IsBold() == WI_IsFlagSet(s_legacyDefaultForeground, FOREGROUND_INTENSITY) && - fg.GetIndex() == (s_legacyDefaultForeground & ~FOREGROUND_INTENSITY)) + attribute.IsBold() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) && + fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY)) { // We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color. copy.SetDefaultForeground(); } - if (bg.IsIndex16() && bg.GetIndex() == s_legacyDefaultBackground) + if (bg.IsIndex16() && bg.GetIndex() == s_ansiDefaultBackground) { copy.SetDefaultBackground(); } diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index 00f8a2266..e973a9cd3 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -41,8 +41,8 @@ public: explicit constexpr TextAttribute(const WORD wLegacyAttr) noexcept : _wAttrLegacy{ gsl::narrow_cast(wLegacyAttr & META_ATTRS) }, - _foreground{ s_LegacyIndexOrDefault(wLegacyAttr & FG_ATTRS, s_legacyDefaultForeground) }, - _background{ s_LegacyIndexOrDefault((wLegacyAttr & BG_ATTRS) >> 4, s_legacyDefaultBackground) }, + _foreground{ gsl::at(s_legacyForegroundColorMap, wLegacyAttr & FG_ATTRS) }, + _background{ gsl::at(s_legacyBackgroundColorMap, (wLegacyAttr & BG_ATTRS) >> 4) }, _extendedAttrs{ ExtendedAttributes::Normal }, _hyperlinkId{ 0 } { @@ -167,13 +167,8 @@ public: } private: - static constexpr TextColor s_LegacyIndexOrDefault(const BYTE requestedIndex, const BYTE defaultIndex) - { - return requestedIndex == defaultIndex ? TextColor{} : TextColor{ requestedIndex, true }; - } - - static BYTE s_legacyDefaultForeground; - static BYTE s_legacyDefaultBackground; + static std::array s_legacyForegroundColorMap; + static std::array s_legacyBackgroundColorMap; uint16_t _wAttrLegacy; // sizeof: 2, alignof: 2 uint16_t _hyperlinkId; // sizeof: 2, alignof: 2 diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index 7c05dd200..88bc8657f 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -32,7 +32,7 @@ constexpr std::array CompressedRgbToIndex16 = { // A table mapping indexed colors from the 256-color palette, // down to one of the 16 colors in the legacy palette. constexpr std::array Index256ToIndex16 = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15, 0, 1, 1, 1, 9, 9, 2, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 11, 11, 3, 3, 10, 10, 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, 5, 5, 5, 5, 1, 1, 8, 8, 1, 1, 9, 9, @@ -252,11 +252,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept { return defaultIndex; } - else if (IsIndex16()) - { - return GetIndex(); - } - else if (IsIndex256()) + else if (IsIndex16() || IsIndex256()) { return til::at(Index256ToIndex16, GetIndex()); } diff --git a/src/buffer/out/TextColor.h b/src/buffer/out/TextColor.h index ad984702f..35b62ff65 100644 --- a/src/buffer/out/TextColor.h +++ b/src/buffer/out/TextColor.h @@ -48,6 +48,23 @@ enum class ColorType : BYTE struct TextColor { public: + static constexpr BYTE DARK_BLACK = 0; + static constexpr BYTE DARK_RED = 1; + static constexpr BYTE DARK_GREEN = 2; + static constexpr BYTE DARK_YELLOW = 3; + static constexpr BYTE DARK_BLUE = 4; + static constexpr BYTE DARK_MAGENTA = 5; + static constexpr BYTE DARK_CYAN = 6; + static constexpr BYTE DARK_WHITE = 7; + static constexpr BYTE BRIGHT_BLACK = 8; + static constexpr BYTE BRIGHT_RED = 9; + static constexpr BYTE BRIGHT_GREEN = 10; + static constexpr BYTE BRIGHT_YELLOW = 11; + static constexpr BYTE BRIGHT_BLUE = 12; + static constexpr BYTE BRIGHT_MAGENTA = 13; + static constexpr BYTE BRIGHT_CYAN = 14; + static constexpr BYTE BRIGHT_WHITE = 15; + constexpr TextColor() noexcept : _meta{ ColorType::IsDefault }, _red{ 0 }, @@ -96,6 +113,16 @@ public: COLORREF GetRGB() const noexcept; + static constexpr BYTE TransposeLegacyIndex(const size_t index) + { + // When converting a 16-color index in the legacy Windows order to or + // from an ANSI-compatible order, we need to swap the bits in positions + // 0 and 2. We do this by XORing the index with 00000101, but only if + // one (but not both) of those bit positions is set. + const auto oneBitSet = (index ^ (index >> 2)) & 1; + return gsl::narrow_cast(index ^ oneBitSet ^ (oneBitSet << 2)); + } + private: union { diff --git a/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp b/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp index 65aba47f7..1b61955bb 100644 --- a/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp +++ b/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp @@ -237,7 +237,7 @@ void TextAttributeTests::TestRoundtripDefaultColors() Log::Comment(L"Foreground legacy default index should map to default text color."); legacyAttribute = fgLegacyDefault | BACKGROUND_GREEN; textAttribute.SetDefaultForeground(); - textAttribute.SetIndexedBackground256(BACKGROUND_GREEN >> 4); + textAttribute.SetIndexedBackground256(TextColor::DARK_GREEN); VERIFY_ARE_EQUAL(textAttribute, TextAttribute{ legacyAttribute }); Log::Comment(L"Default foreground text color should map back to legacy default index."); @@ -245,7 +245,7 @@ void TextAttributeTests::TestRoundtripDefaultColors() Log::Comment(L"Background legacy default index should map to default text color."); legacyAttribute = FOREGROUND_GREEN | bgLegacyDefault; - textAttribute.SetIndexedForeground256(FOREGROUND_GREEN); + textAttribute.SetIndexedForeground256(TextColor::DARK_GREEN); textAttribute.SetDefaultBackground(); VERIFY_ARE_EQUAL(textAttribute, TextAttribute{ legacyAttribute }); @@ -288,7 +288,7 @@ void TextAttributeTests::TestBoldAsBright() VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true)); VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false)); - attr.SetIndexedForeground(0); + attr.SetIndexedForeground(TextColor::DARK_BLACK); VERIFY_IS_TRUE(attr.IsBold()); Log::Comment(L"Foreground should be bright black when bold is bright is enabled"); @@ -297,7 +297,7 @@ void TextAttributeTests::TestBoldAsBright() Log::Comment(L"Foreground should be dark black when bold is bright is disabled"); VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false)); - attr.SetIndexedBackground(2); + attr.SetIndexedBackground(TextColor::DARK_GREEN); VERIFY_IS_TRUE(attr.IsBold()); Log::Comment(L"background should be unaffected by 'bold is bright'"); @@ -312,7 +312,7 @@ void TextAttributeTests::TestBoldAsBright() Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing"); attr.SetBold(true); - attr.SetIndexedForeground(8); + attr.SetIndexedForeground(TextColor::BRIGHT_BLACK); VERIFY_IS_TRUE(attr.IsBold()); VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true)); VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false)); diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp index 85a9dd34a..682bee5f2 100644 --- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp @@ -80,7 +80,7 @@ namespace SettingsModelLocalTests std::array expectedCampbellTable; const auto campbellSpan = gsl::make_span(expectedCampbellTable); - Utils::InitializeCampbellColorTable(campbellSpan); + Utils::InitializeColorTable(campbellSpan); Utils::SetColorTableAlpha(campbellSpan, 0); for (size_t i = 0; i < expectedCampbellTable.size(); i++) diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index ecb40c3eb..4afa3a03b 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -508,7 +508,7 @@ namespace TerminalAppLocalTests Log::Comment(NoThrowString().Format(L"Duplicate the first pane")); result = RunOnUIThread([&page]() { - page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr); + page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); @@ -526,7 +526,7 @@ namespace TerminalAppLocalTests Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash")); result = RunOnUIThread([&page]() { - page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr); + page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); @@ -844,7 +844,7 @@ namespace TerminalAppLocalTests // | 1 | 2 | // | | | // ------------------- - page->_SplitPane(SplitDirection::Right, SplitType::Duplicate, 0.5f, nullptr); + page->_SplitPane(SplitDirection::Right, 0.5f, page->_MakePane(nullptr, true, nullptr)); secondId = tab->_activePane->Id().value(); }); Sleep(250); @@ -862,7 +862,7 @@ namespace TerminalAppLocalTests // | 3 | | // | | | // ------------------- - page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr); + page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr)); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); // Split again to make the 3rd tab thirdId = tab->_activePane->Id().value(); @@ -882,7 +882,7 @@ namespace TerminalAppLocalTests // | 3 | 4 | // | | | // ------------------- - page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr); + page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr)); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); fourthId = tab->_activePane->Id().value(); }); diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index d9d4a1ca1..3feed58a0 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -174,10 +174,9 @@ namespace winrt::TerminalApp::implementation else if (const auto& realArgs = args.ActionArgs().try_as()) { _SplitPane(realArgs.SplitDirection(), - realArgs.SplitMode(), // This is safe, we're already filtering so the value is (0, 1) ::base::saturated_cast(realArgs.SplitSize()), - realArgs.TerminalArgs()); + _MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate)); args.Handled(true); } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 047750383..697251879 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -133,18 +133,6 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD return textRun; } -// Method Description: -// - Returns whether the user is either a member of the Administrators group or -// is currently elevated. -// - This will return **FALSE** if the user has UAC disabled entirely, because -// there's no separation of power between the user and an admin in that case. -// Return Value: -// - true if the user is an administrator -static bool _isUserAdmin() noexcept -{ - return Microsoft::Console::Utils::IsElevated(); -} - namespace winrt::TerminalApp::implementation { // Function Description: @@ -196,7 +184,7 @@ namespace winrt::TerminalApp::implementation // The TerminalPage has to be constructed during our construction, to // make sure that there's a terminal page for callers of // SetTitleBarContent - _isElevated = _isUserAdmin(); + _isElevated = ::Microsoft::Console::Utils::IsElevated(); _root = winrt::make_self(); _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { @@ -316,6 +304,9 @@ namespace winrt::TerminalApp::implementation _RefreshThemeRoutine(); _ApplyStartupTaskStateChange(); + auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &AppLogic::_OpenSettingsUI)); + _SystemMenuChangeRequestedHandlers(*this, *args); + TraceLoggingWrite( g_hTerminalAppProvider, "AppCreated", @@ -898,11 +889,20 @@ namespace winrt::TerminalApp::implementation // actual file you wrote. So listen for that too. wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime, [this, settingsBasename = settingsPath.filename()](wil::FolderChangeEvent, PCWSTR fileModified) { - // DO NOT create a static reference to - // ApplicationState::SharedInstance here. See - // https://github.com/microsoft/terminal/pull/11222/files/9ff2775122a496fb8b1bcc7a0b83a64ce5b26c5f#r719627541 - // for why. ApplicationState::SharedInstance already caches it's - // own static ref. + // DO NOT create a static reference to ApplicationState::SharedInstance here. + // + // ApplicationState::SharedInstance already caches its own + // static ref. If _we_ keep a static ref to the member in + // AppState, then our reference will keep ApplicationState alive + // after the `ActionToStringMap` gets cleaned up. Then, when we + // try to persist the actions in the window state, we won't be + // able to. We'll try to look up the action and the map just + // won't exist. We'll explode, even though the Terminal is + // tearing down anyways. So we'll just die, but still inboke + // WinDBG's post-mortem debugger, who won't be able to attach to + // the process that's already exiting. + // + // So DON'T ~give a mouse a cookie~ take a static ref here. const winrt::hstring modifiedBasename{ std::filesystem::path{ fileModified }.filename().c_str() }; @@ -1037,6 +1037,11 @@ namespace winrt::TerminalApp::implementation _SettingsChangedHandlers(*this, nullptr); } + void AppLogic::_OpenSettingsUI() + { + _root->OpenSettingsUI(); + } + // Method Description: // - Returns a pointer to the global shared settings. [[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept @@ -1543,6 +1548,11 @@ namespace winrt::TerminalApp::implementation return _root->IsQuakeWindow(); } + void AppLogic::RequestExitFullscreen() + { + _root->SetFullscreen(false); + } + bool AppLogic::GetMinimizeToNotificationArea() { if constexpr (Feature_NotificationIcon::IsEnabled()) diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index e121043d1..7c1681db1 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -5,6 +5,7 @@ #include "AppLogic.g.h" #include "FindTargetWindowResult.g.h" +#include "SystemMenuChangeArgs.g.h" #include "Jumplist.h" #include "LanguageProfileNotifier.h" #include "TerminalPage.h" @@ -35,6 +36,17 @@ namespace winrt::TerminalApp::implementation FindTargetWindowResult(id, L""){}; }; + struct SystemMenuChangeArgs : SystemMenuChangeArgsT + { + WINRT_PROPERTY(winrt::hstring, Name, L""); + WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add); + WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr); + + public: + SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) : + _Name{ name }, _Action{ action }, _Handler{ handler } {}; + }; + struct AppLogic : AppLogicT { public: @@ -79,6 +91,7 @@ namespace winrt::TerminalApp::implementation void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); bool IsQuakeWindow() const noexcept; + void RequestExitFullscreen(); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); bool CenterOnLaunch(); @@ -113,6 +126,7 @@ namespace winrt::TerminalApp::implementation // -------------------------------- WinRT Events --------------------------------- TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme); TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); private: bool _isUwp{ false }; @@ -163,6 +177,7 @@ namespace winrt::TerminalApp::implementation void _RegisterSettingsChange(); fire_and_forget _DispatchReloadSettings(); void _ReloadSettings(); + void _OpenSettingsUI(); void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 674322d35..973e8aa1a 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -19,6 +19,20 @@ namespace TerminalApp String WindowName { get; }; }; + delegate void SystemMenuItemHandler(); + + enum SystemMenuChangeAction + { + Add = 0, + Remove = 1 + }; + + [default_interface] runtimeclass SystemMenuChangeArgs { + String Name { get; }; + SystemMenuChangeAction Action { get; }; + SystemMenuItemHandler Handler { get; }; + }; + [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter { AppLogic(); @@ -60,6 +74,7 @@ namespace TerminalApp void SetPersistedLayoutIdx(UInt32 idx); void SetNumberOfOpenWindows(UInt64 num); void RenameFailed(); + void RequestExitFullscreen(); Boolean IsQuakeWindow(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); @@ -110,5 +125,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler CloseRequested; event Windows.Foundation.TypedEventHandler OpenSystemMenu; event Windows.Foundation.TypedEventHandler QuitRequested; + event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 29c6fa220..8000b2067 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2426,35 +2426,32 @@ std::optional Pane::PreCalculateCanSplit(const std::shared_ptr targe } // Method Description: -// - Split the focused pane in our tree of panes, and place the given -// UserControl into the newly created pane. If we're the focused pane, then -// we'll create two new children, and place them side-by-side in our Grid. +// - The same as above, except this takes in the pane directly instead of a +// profile and control to make a pane with // Arguments: // - splitType: what type of split we want to create. -// - profile: The profile to associate with the newly created pane. -// - control: A UserControl to use in the new pane. +// - splitSize: the desired size of the split +// - newPane: the new pane // Return Value: // - The two newly created Panes, with the original pane first std::pair, std::shared_ptr> Pane::Split(SplitDirection splitType, const float splitSize, - const Profile& profile, - const Controls::UserControl& control) + std::shared_ptr newPane) { if (!_lastActive) { if (_firstChild && _firstChild->_HasFocusedChild()) { - return _firstChild->Split(splitType, splitSize, profile, control); + return _firstChild->Split(splitType, splitSize, newPane); } else if (_secondChild && _secondChild->_HasFocusedChild()) { - return _secondChild->Split(splitType, splitSize, profile, control); + return _secondChild->Split(splitType, splitSize, newPane); } return { nullptr, nullptr }; } - auto newPane = std::make_shared(profile, control); return _Split(splitType, splitSize, newPane); } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 8248abd4f..5ad95c372 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -111,8 +111,7 @@ public: std::pair, std::shared_ptr> Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType, const float splitSize, - const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, - const winrt::Windows::UI::Xaml::Controls::UserControl& control); + std::shared_ptr pane); bool ToggleSplitOrientation(); float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; std::optional PreCalculateAutoSplit(const std::shared_ptr target, diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 3e42b730a..d5b4fe275 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -69,7 +69,7 @@ namespace winrt::TerminalApp::implementation const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) }; - _CreateNewTabWithProfileAndSettings(profile, settings, existingConnection); + _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection)); const uint32_t tabCount = _tabs.Size(); const bool usedManualProfile = (newTerminalArgs != nullptr) && @@ -245,80 +245,6 @@ namespace winrt::TerminalApp::implementation _InitializeTab(newTabImpl); } - // Method Description: - // - Creates a new tab with the given settings. If the tab bar is not being - // currently displayed, it will be shown. - // Arguments: - // - profile: profile settings for this connection - // - settings: the TerminalSettings object to use to create the TerminalControl with. - // - existingConnection: optionally receives a connection from the outside world instead of attempting to create one - void TerminalPage::_CreateNewTabWithProfileAndSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings, TerminalConnection::ITerminalConnection existingConnection) - { - // Initialize the new tab - // Create a connection based on the values in our settings object if we weren't given one. - auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, settings.DefaultSettings()); - - // If we had an `existingConnection`, then this is an inbound handoff from somewhere else. - // We need to tell it about our size information so it can match the dimensions of what - // we are about to present. - if (existingConnection) - { - connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols()); - } - - TerminalConnection::ITerminalConnection debugConnection{ nullptr }; - if (_settings.GlobalSettings().DebugFeaturesEnabled()) - { - const CoreWindow window = CoreWindow::GetForCurrentThread(); - const auto rAltState = window.GetKeyState(VirtualKey::RightMenu); - const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu); - const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && - WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); - if (bothAltsPressed) - { - std::tie(connection, debugConnection) = OpenDebugTapConnection(connection); - } - } - - // Give term control a child of the settings so that any overrides go in the child - // This way, when we do a settings reload we just update the parent and the overrides remain - auto term = _InitControl(settings, connection); - WUX::Controls::UserControl controlToAdd{ term }; - - const auto& cmdline{ settings.DefaultSettings().Commandline() }; - const bool doAdminWarning = _shouldPromptForCommandline(cmdline); - if (doAdminWarning) - { - auto warningControl{ winrt::make_self(term, cmdline) }; - warningControl->PrimaryButtonClicked({ get_weak(), &TerminalPage::_adminWarningPrimaryClicked }); - warningControl->CancelButtonClicked({ get_weak(), &TerminalPage::_adminWarningCancelClicked }); - controlToAdd = *warningControl; - } - - auto newTabImpl = winrt::make_self(profile, controlToAdd); - _RegisterTerminalEvents(term); - _InitializeTab(newTabImpl); - - if (debugConnection) // this will only be set if global debugging is on and tap is active - { - auto newControl = _InitControl(settings, debugConnection); - _RegisterTerminalEvents(newControl); - // Split (auto) with the debug tap. - newTabImpl->SplitPane(SplitDirection::Automatic, 0.5f, profile, newControl); - } - - if (doAdminWarning) - { - // We know this is safe - we literally just added the - // AdminWarningPlaceholder as the controlToAdd like 20 lines up. - // - // Focus the warning here. The LayoutUpdated within the dialog - // itself isn't good enough. That, for some reason, fires _before_ - // the dialog is in the UI tree, which is useless for us. - controlToAdd.try_as()->FocusOnLaunch(); - } - } - // Method Description: // - Get the icon of the currently focused terminal control, and set its // tab's icon to that icon. @@ -388,28 +314,14 @@ namespace winrt::TerminalApp::implementation // In the future, it may be preferable to just duplicate the // current control's live settings (which will include changes // made through VT). + _CreateNewTabFromPane(_MakePane(nullptr, true, nullptr)); - if (auto profile = tab.GetFocusedProfile()) + const auto runtimeTabText{ tab.GetTabText() }; + if (!runtimeTabText.empty()) { - // TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this. - profile = GetClosestProfileForDuplicationOfProfile(profile); - const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) }; - const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory(); - const auto validWorkingDirectory = !workingDirectory.empty(); - if (validWorkingDirectory) + if (auto newTab{ _GetFocusedTabImpl() }) { - settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory); - } - - _CreateNewTabWithProfileAndSettings(profile, settingsCreateResult); - - const auto runtimeTabText{ tab.GetTabText() }; - if (!runtimeTabText.empty()) - { - if (auto newTab{ _GetFocusedTabImpl() }) - { - newTab->SetTabText(runtimeTabText); - } + newTab->SetTabText(runtimeTabText); } } } @@ -425,7 +337,7 @@ namespace winrt::TerminalApp::implementation try { _SetFocusedTab(tab); - _SplitPane(tab, SplitDirection::Automatic, SplitType::Duplicate); + _SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true)); } CATCH_LOG(); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 000ba6c51..68ca1974f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -655,6 +655,7 @@ namespace winrt::TerminalApp::implementation co_return ContentDialogResult::None; } + // TODO! get rid of me winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCommandlineApproveWarning() { if (auto presenter{ _dialogPresenter.get() }) @@ -857,14 +858,7 @@ namespace winrt::TerminalApp::implementation WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); - if (altPressed && !debugTap) - { - this->_SplitPane(SplitDirection::Automatic, - SplitType::Manual, - 0.5f, - newTerminalArgs); - } - else if (shiftPressed && !debugTap) + if (shiftPressed && !debugTap) { // Manually fill in the evaluated profile. if (newTerminalArgs.ProfileIndex() != nullptr) @@ -880,7 +874,17 @@ namespace winrt::TerminalApp::implementation } else { - LOG_IF_FAILED(this->_OpenNewTab(newTerminalArgs)); + const auto newPane = _MakePane(newTerminalArgs); + if (altPressed && !debugTap) + { + this->_SplitPane(SplitDirection::Automatic, + 0.5f, + newPane); + } + else + { + _CreateNewTabFromPane(newPane); + } } } @@ -1736,18 +1740,15 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Split the focused pane either horizontally or vertically, and place the - // given TermControl into the newly created pane. + // given pane accordingly in the tree // Arguments: + // - newPane: the pane to add to our tree of panes // - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the // new pane should be split from its parent. - // - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane. - // - newTerminalArgs: An object that may contain a blob of parameters to - // control which profile is created and with possible other - // configurations. See CascadiaSettings::BuildSettings for more details. + // - splitSize: the size of the split void TerminalPage::_SplitPane(const SplitDirection splitDirection, - const SplitType splitMode, const float splitSize, - const NewTerminalArgs& newTerminalArgs) + std::shared_ptr newPane) { const auto focusedTab{ _GetFocusedTabImpl() }; @@ -1757,126 +1758,52 @@ namespace winrt::TerminalApp::implementation return; } - _SplitPane(*focusedTab, splitDirection, splitMode, splitSize, newTerminalArgs); + _SplitPane(*focusedTab, splitDirection, splitSize, newPane); } // Method Description: // - Split the focused pane of the given tab, either horizontally or vertically, and place the - // given TermControl into the newly created pane. + // given pane accordingly // Arguments: // - tab: The tab that is going to be split. + // - newPane: the pane to add to our tree of panes // - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the // new pane should be split from its parent. - // - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane. - // - newTerminalArgs: An object that may contain a blob of parameters to - // control which profile is created and with possible other - // configurations. See CascadiaSettings::BuildSettings for more details. + // - splitSize: the size of the split void TerminalPage::_SplitPane(TerminalTab& tab, const SplitDirection splitDirection, - const SplitType splitMode, const float splitSize, - const NewTerminalArgs& newTerminalArgs) + std::shared_ptr newPane) { - try + const float contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); + const float contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); + const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight }; + + auto realSplitType = splitDirection; + if (realSplitType == SplitDirection::Automatic) { - TerminalSettingsCreateResult controlSettings{ nullptr }; - Profile profile{ nullptr }; + realSplitType = tab.PreCalculateAutoSplit(availableSpace); + } - if (splitMode == SplitType::Duplicate) + const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace); + if (!canSplit) + { + return; + } + + _UnZoomIfNeeded(); + tab.SplitPane(realSplitType, splitSize, newPane); + + // After GH#6586, the control will no longer focus itself + // automatically when it's finished being laid out. Manually focus + // the control here instead. + if (_startupState == StartupState::Initialized) + { + if (const auto control = _GetActiveControl()) { - profile = tab.GetFocusedProfile(); - if (profile) - { - // TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this. - profile = GetClosestProfileForDuplicationOfProfile(profile); - controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings); - const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory(); - const auto validWorkingDirectory = !workingDirectory.empty(); - if (validWorkingDirectory) - { - controlSettings.DefaultSettings().StartingDirectory(workingDirectory); - } - } - // TODO: GH#5047 - In the future, we should get the Profile of - // the focused pane, and use that to build a new instance of the - // settings so we can duplicate this tab/pane. - // - // Currently, if the profile doesn't exist anymore in our - // settings, we'll silently do nothing. - // - // In the future, it will be preferable to just duplicate the - // current control's settings, but we can't do that currently, - // because we won't be able to create a new instance of the - // connection without keeping an instance of the original Profile - // object around. - } - if (!profile) - { - profile = _settings.GetProfileForArgs(newTerminalArgs); - controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings); - } - - const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings()); - - const float contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); - const float contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); - const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight }; - - auto realSplitType = splitDirection; - if (realSplitType == SplitDirection::Automatic) - { - realSplitType = tab.PreCalculateAutoSplit(availableSpace); - } - - const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace); - if (!canSplit) - { - return; - } - - auto newControl = _InitControl(controlSettings, controlConnection); - WUX::Controls::UserControl controlToAdd{ newControl }; - - const auto& cmdline{ controlSettings.DefaultSettings().Commandline() }; - const auto doAdminWarning{ _shouldPromptForCommandline(cmdline) }; - if (doAdminWarning) - { - auto warningControl{ winrt::make_self(newControl, cmdline) }; - warningControl->PrimaryButtonClicked({ get_weak(), &TerminalPage::_adminWarningPrimaryClicked }); - warningControl->CancelButtonClicked({ get_weak(), &TerminalPage::_adminWarningCancelClicked }); - controlToAdd = *warningControl; - } - - // Hookup our event handlers to the new terminal - _RegisterTerminalEvents(newControl); - - _UnZoomIfNeeded(); - - tab.SplitPane(realSplitType, splitSize, profile, controlToAdd); - - // After GH#6586, the control will no longer focus itself - // automatically when it's finished being laid out. Manually focus - // the control here instead. - if (_startupState == StartupState::Initialized) - { - if (const auto& activeControl{ _GetActiveControl() }) - { - activeControl.Focus(FocusState::Programmatic); - } - } - - if (doAdminWarning) - { - // We know this is safe - we literally just added the - // AdminWarningPlaceholder as the controlToAdd like 20 lines up. - // - // Focus the warning here. The LayoutUpdated within the dialog - // itself isn't good enough. That, for some reason, fires _before_ - // the dialog is in the UI tree, which is useless for us. - controlToAdd.try_as()->FocusOnLaunch(); + control.Focus(FocusState::Programmatic); } } - CATCH_LOG(); } // Method Description: @@ -2384,7 +2311,7 @@ namespace winrt::TerminalApp::implementation { if (target == SettingsTarget::SettingsUI) { - _OpenSettingsUI(); + OpenSettingsUI(); } else { @@ -2461,6 +2388,121 @@ namespace winrt::TerminalApp::implementation return term; } + // Method Description: + // - Creates a pane and returns a shared_ptr to it + // - The caller should handle where the pane goes after creation, + // either to split an already existing pane or to create a new tab with it + // Arguments: + // - newTerminalArgs: an object that may contain a blob of parameters to + // control which profile is created and with possible other + // configurations. See CascadiaSettings::BuildSettings for more details. + // - duplicate: a boolean to indicate whether the pane we create should be + // a duplicate of the currently focused pane + // - existingConnection: optionally receives a connection from the outside + // world instead of attempting to create one + std::shared_ptr TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs, const bool duplicate, TerminalConnection::ITerminalConnection existingConnection) + { + TerminalSettingsCreateResult controlSettings{ nullptr }; + Profile profile{ nullptr }; + + if (duplicate) + { + const auto focusedTab{ _GetFocusedTabImpl() }; + if (focusedTab) + { + profile = focusedTab->GetFocusedProfile(); + if (profile) + { + // TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this. + profile = GetClosestProfileForDuplicationOfProfile(profile); + controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings); + const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory(); + const auto validWorkingDirectory = !workingDirectory.empty(); + if (validWorkingDirectory) + { + controlSettings.DefaultSettings().StartingDirectory(workingDirectory); + } + } + } + } + if (!profile) + { + profile = _settings.GetProfileForArgs(newTerminalArgs); + controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings); + } + + auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings()); + if (existingConnection) + { + connection.Resize(controlSettings.DefaultSettings().InitialRows(), controlSettings.DefaultSettings().InitialCols()); + } + + TerminalConnection::ITerminalConnection debugConnection{ nullptr }; + if (_settings.GlobalSettings().DebugFeaturesEnabled()) + { + const CoreWindow window = CoreWindow::GetForCurrentThread(); + const auto rAltState = window.GetKeyState(VirtualKey::RightMenu); + const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu); + const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && + WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); + if (bothAltsPressed) + { + std::tie(connection, debugConnection) = OpenDebugTapConnection(connection); + } + } + + const auto control = _InitControl(controlSettings, connection); + _RegisterTerminalEvents(control); + + WUX::Controls::UserControl controlToAdd{ control }; + + // Check if we should warn the user about running a new unelevated + // commandline. + const auto& cmdline{ controlSettings.DefaultSettings().Commandline() }; + const auto doAdminWarning{ _shouldPromptForCommandline(cmdline) }; + if (doAdminWarning) + { + auto warningControl{ winrt::make_self(control, cmdline) }; + warningControl->PrimaryButtonClicked({ get_weak(), &TerminalPage::_adminWarningPrimaryClicked }); + warningControl->CancelButtonClicked({ get_weak(), &TerminalPage::_adminWarningCancelClicked }); + controlToAdd = *warningControl; + } + + auto resultPane = std::make_shared(profile, controlToAdd); + + if (debugConnection) // this will only be set if global debugging is on and tap is active + { + auto newControl = _InitControl(controlSettings, debugConnection); + _RegisterTerminalEvents(newControl); + // Split (auto) with the debug tap. + auto debugPane = std::make_shared(profile, newControl); + + // Since we're doing this split directly on the pane (instead of going through TerminalTab, + // we need to handle the panes 'active' states + + // Set the pane we're splitting to active (otherwise Split will not do anything) + resultPane->SetActive(); + auto [original, _] = resultPane->Split(SplitDirection::Automatic, 0.5f, debugPane); + + // Set the non-debug pane as active + resultPane->ClearActive(); + original->SetActive(); + } + + if (doAdminWarning) + { + // We know this is safe - we literally just added the + // AdminWarningPlaceholder as the controlToAdd like 20 lines up. + // + // Focus the warning here. The LayoutUpdated within the dialog + // itself isn't good enough. That, for some reason, fires _before_ + // the dialog is in the UI tree, which is useless for us. + controlToAdd.try_as()->FocusOnLaunch(); + } + + return resultPane; + } + // Method Description: // - Hook up keybindings, and refresh the UI of the terminal. // This includes update the settings of all the tabs according @@ -2770,9 +2812,7 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::ToggleFullscreen() { - _isFullscreen = !_isFullscreen; - _UpdateTabView(); - _FullscreenChangedHandlers(*this, nullptr); + SetFullscreen(!_isFullscreen); } // Method Description: @@ -2989,6 +3029,17 @@ namespace winrt::TerminalApp::implementation return _isAlwaysOnTop; } + void TerminalPage::SetFullscreen(bool newFullscreen) + { + if (_isFullscreen == newFullscreen) + { + return; + } + _isFullscreen = newFullscreen; + _UpdateTabView(); + _FullscreenChangedHandlers(*this, nullptr); + } + HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection) { // We need to be on the UI thread in order for _OpenNewTab to run successfully. @@ -3016,7 +3067,7 @@ namespace winrt::TerminalApp::implementation const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) }; - _CreateNewTabWithProfileAndSettings(profile, settings, connection); + _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, connection)); // Request a summon of this window to the foreground _SummonWindowRequestedHandlers(*this, nullptr); @@ -3032,7 +3083,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void TerminalPage::_OpenSettingsUI() + void TerminalPage::OpenSettingsUI() { // If we're holding the settings tab's switch command, don't create a new one, switch to the existing one. if (!_settingsTab) @@ -3650,7 +3701,7 @@ namespace winrt::TerminalApp::implementation TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipInteracted", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - _OpenSettingsUI(); + OpenSettingsUI(); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index aca0da5be..2d3efb9c0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -83,6 +83,7 @@ namespace winrt::TerminalApp::implementation bool FocusMode() const; bool Fullscreen() const; bool AlwaysOnTop() const; + void SetFullscreen(bool); void SetStartupActions(std::vector& actions); @@ -121,6 +122,8 @@ namespace winrt::TerminalApp::implementation bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; + void OpenSettingsUI(); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -210,7 +213,6 @@ namespace winrt::TerminalApp::implementation void _OpenNewTabDropdown(); HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); void _CreateNewTabFromPane(std::shared_ptr pane); - void _CreateNewTabWithProfileAndSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings); winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); @@ -298,14 +300,12 @@ namespace winrt::TerminalApp::implementation void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference& rowsToScroll); void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitDirection splitType, - const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual, - const float splitSize = 0.5f, - const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr); + const float splitSize, + std::shared_ptr newPane); void _SplitPane(TerminalTab& tab, const Microsoft::Terminal::Settings::Model::SplitDirection splitType, - const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual, - const float splitSize = 0.5f, - const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr); + const float splitSize, + std::shared_ptr newPane); void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction); void _ToggleSplitOrientation(); @@ -352,6 +352,10 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); + std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, + const bool duplicate = false, + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); + void _RefreshUIForSettingsReload(); void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor); @@ -367,8 +371,6 @@ namespace winrt::TerminalApp::implementation void _UnZoomIfNeeded(); - void _OpenSettingsUI(); - static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll); static uint32_t _ReadSystemRowsToScroll(); diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 6c0705437..e4be0e54a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -153,7 +153,7 @@ diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index f2b814ba4..83fb6e28c 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -25,18 +25,6 @@ namespace winrt namespace winrt::TerminalApp::implementation { - TerminalTab::TerminalTab(const Profile& profile, const WUX::Controls::UserControl& control) - { - _rootPane = std::make_shared(profile, control, true); - - _rootPane->Id(_nextPaneId); - _activePane = _rootPane; - _mruPanes.insert(_mruPanes.begin(), _nextPaneId); - ++_nextPaneId; - - _Setup(); - } - TerminalTab::TerminalTab(std::shared_ptr rootPane) { _rootPane = rootPane; @@ -64,8 +52,9 @@ namespace winrt::TerminalApp::implementation // focus the first one. if (_activePane == nullptr) { - _rootPane->FocusPane(firstId); - _activePane = _rootPane->GetActivePane(); + const auto firstPane = _rootPane->FindPane(firstId); + firstPane->SetActive(); + _activePane = firstPane; } // If the focused pane is a leaf, add it to the MRU panes if (const auto id = _activePane->Id()) @@ -506,42 +495,53 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Split the focused pane in our tree of panes, and place the - // given TermControl into the newly created pane. + // given pane into the tree of panes according to the split // Arguments: - // - splitType: The type of split we want to create. - // - profile: The profile GUID to associate with the newly created pane. - // - control: A TermControl to use in the new pane. + // - splitType: The type of split we want to create + // - splitSize: The size of the split we want to create + // - pane: The new pane to add to the tree of panes; note that this pane + // could itself be a parent pane/the root node of a tree of panes // Return Value: // - void TerminalTab::SplitPane(SplitDirection splitType, const float splitSize, - const Profile& profile, - const WUX::Controls::UserControl& control) + std::shared_ptr pane) { + // Add the new event handlers to the new pane(s) + // and update their ids. + pane->WalkTree([&](auto p) { + _AttachEventHandlersToPane(p); + if (p->_IsLeaf()) + { + p->Id(_nextPaneId); + if (auto termControl{ p->_control.try_as() }) + { + // TODO! make sure that the event handlers are still wired up for splitting a pane that creates an unapproved commandline, I don't think they are. + _AttachEventHandlersToControl(p->Id().value(), termControl); + } + + _nextPaneId++; + } + return false; + }); // Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID const auto activePaneId = _activePane->Id(); // Depending on which direction will be split, the new pane can be // either the first or second child, but this will always return the // original pane first. - auto [original, newPane] = _activePane->Split(splitType, splitSize, profile, control); + auto [original, newPane] = _activePane->Split(splitType, splitSize, pane); + // The active pane has an id if it is a leaf if (activePaneId) { original->Id(activePaneId.value()); } - newPane->Id(_nextPaneId); - ++_nextPaneId; _activePane = original; // Add a event handlers to the new panes' GotFocus event. When the pane // gains focus, we'll mark it as the new active pane. - const auto& termControl{ control.try_as() }; - // _AttachEventHandlersToControl will immediately bail if the provided - // control is null (READ: if the pane didn't have a TermControl) - _AttachEventHandlersToControl(newPane->Id().value(), termControl); _AttachEventHandlersToPane(original); - _AttachEventHandlersToPane(newPane); // Immediately update our tracker of the focused pane now. If we're // splitting panes during startup (from a commandline), then it's diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index d4af27076..4b5fe9ef4 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -21,8 +21,6 @@ namespace winrt::TerminalApp::implementation struct TerminalTab : TerminalTabT { public: - TerminalTab(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, - const winrt::Windows::UI::Xaml::Controls::UserControl& control); TerminalTab(std::shared_ptr rootPane); // Called after construction to perform the necessary setup, which relies on weak_ptr @@ -41,8 +39,7 @@ namespace winrt::TerminalApp::implementation void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType, const float splitSize, - const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, - const winrt::Windows::UI::Xaml::Controls::UserControl& control); + std::shared_ptr newPane); void ToggleSplitOrientation(); winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath); diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 9135f8024..a6dcbd3b4 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -429,8 +429,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // EXIT POINT const auto hr = wil::ResultFromCaughtException(); + // GH#11556 - make sure to format the error code to this string as an UNSIGNED int winrt::hstring failureText{ fmt::format(std::wstring_view{ RS_(L"ProcessFailedToLaunch") }, - fmt::format(_errorFormat, hr), + fmt::format(_errorFormat, static_cast(hr)), _commandline) }; _TerminalOutputHandlers(failureText); @@ -457,6 +458,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { try { + // GH#11556 - make sure to format the error code to this string as an UNSIGNED int winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) }; _TerminalOutputHandlers(L"\r\n"); _TerminalOutputHandlers(exitText); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 3433b60d9..c12a02f6d 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1210,9 +1210,7 @@ try { const gsl::span tableView = { _colorTable.data(), _colorTable.size() }; // First set up the basic 256 colors - Utils::Initialize256ColorTable(tableView); - // Then use fill the first 16 values with the Campbell scheme - Utils::InitializeCampbellColorTable(tableView); + Utils::InitializeColorTable(tableView); // Then make sure all the values have an alpha of 255 Utils::SetColorTableAlpha(tableView, 0xff); } diff --git a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp index c70d61b05..3f34e768c 100644 --- a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp @@ -7,29 +7,6 @@ using namespace Microsoft::Console::VirtualTerminal; using namespace Microsoft::Console::VirtualTerminal::DispatchTypes; -// clang-format off -const BYTE RED_ATTR = 0x01; -const BYTE GREEN_ATTR = 0x02; -const BYTE BLUE_ATTR = 0x04; -const BYTE BRIGHT_ATTR = 0x08; -const BYTE DARK_BLACK = 0; -const BYTE DARK_RED = RED_ATTR; -const BYTE DARK_GREEN = GREEN_ATTR; -const BYTE DARK_YELLOW = RED_ATTR | GREEN_ATTR; -const BYTE DARK_BLUE = BLUE_ATTR; -const BYTE DARK_MAGENTA = RED_ATTR | BLUE_ATTR; -const BYTE DARK_CYAN = GREEN_ATTR | BLUE_ATTR; -const BYTE DARK_WHITE = RED_ATTR | GREEN_ATTR | BLUE_ATTR; -const BYTE BRIGHT_BLACK = BRIGHT_ATTR; -const BYTE BRIGHT_RED = BRIGHT_ATTR | RED_ATTR; -const BYTE BRIGHT_GREEN = BRIGHT_ATTR | GREEN_ATTR; -const BYTE BRIGHT_YELLOW = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR; -const BYTE BRIGHT_BLUE = BRIGHT_ATTR | BLUE_ATTR; -const BYTE BRIGHT_MAGENTA = BRIGHT_ATTR | RED_ATTR | BLUE_ATTR; -const BYTE BRIGHT_CYAN = BRIGHT_ATTR | GREEN_ATTR | BLUE_ATTR; -const BYTE BRIGHT_WHITE = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR | BLUE_ATTR; -// clang-format on - // Routine Description: // - Helper to parse extended graphics options, which start with 38 (FG) or 48 (BG) // These options are followed by either a 2 (RGB) or 5 (xterm index) @@ -169,100 +146,100 @@ bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept attr.SetOverlined(false); break; case ForegroundBlack: - attr.SetIndexedForeground(DARK_BLACK); + attr.SetIndexedForeground(TextColor::DARK_BLACK); break; case ForegroundBlue: - attr.SetIndexedForeground(DARK_BLUE); + attr.SetIndexedForeground(TextColor::DARK_BLUE); break; case ForegroundGreen: - attr.SetIndexedForeground(DARK_GREEN); + attr.SetIndexedForeground(TextColor::DARK_GREEN); break; case ForegroundCyan: - attr.SetIndexedForeground(DARK_CYAN); + attr.SetIndexedForeground(TextColor::DARK_CYAN); break; case ForegroundRed: - attr.SetIndexedForeground(DARK_RED); + attr.SetIndexedForeground(TextColor::DARK_RED); break; case ForegroundMagenta: - attr.SetIndexedForeground(DARK_MAGENTA); + attr.SetIndexedForeground(TextColor::DARK_MAGENTA); break; case ForegroundYellow: - attr.SetIndexedForeground(DARK_YELLOW); + attr.SetIndexedForeground(TextColor::DARK_YELLOW); break; case ForegroundWhite: - attr.SetIndexedForeground(DARK_WHITE); + attr.SetIndexedForeground(TextColor::DARK_WHITE); break; case BackgroundBlack: - attr.SetIndexedBackground(DARK_BLACK); + attr.SetIndexedBackground(TextColor::DARK_BLACK); break; case BackgroundBlue: - attr.SetIndexedBackground(DARK_BLUE); + attr.SetIndexedBackground(TextColor::DARK_BLUE); break; case BackgroundGreen: - attr.SetIndexedBackground(DARK_GREEN); + attr.SetIndexedBackground(TextColor::DARK_GREEN); break; case BackgroundCyan: - attr.SetIndexedBackground(DARK_CYAN); + attr.SetIndexedBackground(TextColor::DARK_CYAN); break; case BackgroundRed: - attr.SetIndexedBackground(DARK_RED); + attr.SetIndexedBackground(TextColor::DARK_RED); break; case BackgroundMagenta: - attr.SetIndexedBackground(DARK_MAGENTA); + attr.SetIndexedBackground(TextColor::DARK_MAGENTA); break; case BackgroundYellow: - attr.SetIndexedBackground(DARK_YELLOW); + attr.SetIndexedBackground(TextColor::DARK_YELLOW); break; case BackgroundWhite: - attr.SetIndexedBackground(DARK_WHITE); + attr.SetIndexedBackground(TextColor::DARK_WHITE); break; case BrightForegroundBlack: - attr.SetIndexedForeground(BRIGHT_BLACK); + attr.SetIndexedForeground(TextColor::BRIGHT_BLACK); break; case BrightForegroundBlue: - attr.SetIndexedForeground(BRIGHT_BLUE); + attr.SetIndexedForeground(TextColor::BRIGHT_BLUE); break; case BrightForegroundGreen: - attr.SetIndexedForeground(BRIGHT_GREEN); + attr.SetIndexedForeground(TextColor::BRIGHT_GREEN); break; case BrightForegroundCyan: - attr.SetIndexedForeground(BRIGHT_CYAN); + attr.SetIndexedForeground(TextColor::BRIGHT_CYAN); break; case BrightForegroundRed: - attr.SetIndexedForeground(BRIGHT_RED); + attr.SetIndexedForeground(TextColor::BRIGHT_RED); break; case BrightForegroundMagenta: - attr.SetIndexedForeground(BRIGHT_MAGENTA); + attr.SetIndexedForeground(TextColor::BRIGHT_MAGENTA); break; case BrightForegroundYellow: - attr.SetIndexedForeground(BRIGHT_YELLOW); + attr.SetIndexedForeground(TextColor::BRIGHT_YELLOW); break; case BrightForegroundWhite: - attr.SetIndexedForeground(BRIGHT_WHITE); + attr.SetIndexedForeground(TextColor::BRIGHT_WHITE); break; case BrightBackgroundBlack: - attr.SetIndexedBackground(BRIGHT_BLACK); + attr.SetIndexedBackground(TextColor::BRIGHT_BLACK); break; case BrightBackgroundBlue: - attr.SetIndexedBackground(BRIGHT_BLUE); + attr.SetIndexedBackground(TextColor::BRIGHT_BLUE); break; case BrightBackgroundGreen: - attr.SetIndexedBackground(BRIGHT_GREEN); + attr.SetIndexedBackground(TextColor::BRIGHT_GREEN); break; case BrightBackgroundCyan: - attr.SetIndexedBackground(BRIGHT_CYAN); + attr.SetIndexedBackground(TextColor::BRIGHT_CYAN); break; case BrightBackgroundRed: - attr.SetIndexedBackground(BRIGHT_RED); + attr.SetIndexedBackground(TextColor::BRIGHT_RED); break; case BrightBackgroundMagenta: - attr.SetIndexedBackground(BRIGHT_MAGENTA); + attr.SetIndexedBackground(TextColor::BRIGHT_MAGENTA); break; case BrightBackgroundYellow: - attr.SetIndexedBackground(BRIGHT_YELLOW); + attr.SetIndexedBackground(TextColor::BRIGHT_YELLOW); break; case BrightBackgroundWhite: - attr.SetIndexedBackground(BRIGHT_WHITE); + attr.SetIndexedBackground(TextColor::BRIGHT_WHITE); break; case ForegroundExtended: i += _SetRgbColorsHelper(options.subspan(i + 1), attr, true); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.h b/src/cascadia/TerminalSettingsEditor/Profiles.h index 2bcbb1ca3..40ecd9c45 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles.h @@ -86,8 +86,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, Commandline); OBSERVABLE_PROJECTED_SETTING(_profile, StartingDirectory); OBSERVABLE_PROJECTED_SETTING(_profile, AntialiasingMode); - OBSERVABLE_PROJECTED_SETTING(_profile, ForceFullRepaintRendering); - OBSERVABLE_PROJECTED_SETTING(_profile, SoftwareRendering); OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Foreground); OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Background); OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), SelectionBackground); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.idl b/src/cascadia/TerminalSettingsEditor/Profiles.idl index 482a19671..5d45a612c 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.idl +++ b/src/cascadia/TerminalSettingsEditor/Profiles.idl @@ -53,8 +53,6 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Commandline); OBSERVABLE_PROJECTED_PROFILE_SETTING(String, StartingDirectory); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.TextAntialiasingMode, AntialiasingMode); - OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ForceFullRepaintRendering); - OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SoftwareRendering); OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, Foreground); OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, Background); OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, SelectionBackground); diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp index dcc1677e6..d48763eca 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp @@ -16,17 +16,6 @@ static constexpr std::string_view ForegroundKey{ "foreground" }; static constexpr std::string_view BackgroundKey{ "background" }; static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" }; static constexpr std::string_view CursorColorKey{ "cursorColor" }; -static constexpr std::string_view CursorShapeKey{ "cursorShape" }; -static constexpr std::string_view CursorHeightKey{ "cursorHeight" }; -static constexpr std::string_view BackgroundImageKey{ "backgroundImage" }; -static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; -static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" }; -static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImageStretchMode" }; -static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" }; -static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" }; -static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" }; -static constexpr std::string_view IntenseTextStyleKey{ "intenseTextStyle" }; -static constexpr std::string_view AdjustIndistinguishableColorsKey{ "adjustIndistinguishableColors" }; static constexpr std::string_view LegacyAcrylicTransparencyKey{ "acrylicOpacity" }; static constexpr std::string_view OpacityKey{ "opacity" }; @@ -38,22 +27,17 @@ AppearanceConfig::AppearanceConfig(winrt::weak_ref sourceProfile) : winrt::com_ptr AppearanceConfig::CopyAppearance(const AppearanceConfig* source, winrt::weak_ref sourceProfile) { auto appearance{ winrt::make_self(std::move(sourceProfile)) }; - appearance->_BackgroundImagePath = source->_BackgroundImagePath; - appearance->_BackgroundImageOpacity = source->_BackgroundImageOpacity; - appearance->_BackgroundImageStretchMode = source->_BackgroundImageStretchMode; - appearance->_ColorSchemeName = source->_ColorSchemeName; appearance->_Foreground = source->_Foreground; appearance->_Background = source->_Background; appearance->_SelectionBackground = source->_SelectionBackground; appearance->_CursorColor = source->_CursorColor; - appearance->_CursorShape = source->_CursorShape; - appearance->_CursorHeight = source->_CursorHeight; - appearance->_BackgroundImageAlignment = source->_BackgroundImageAlignment; - appearance->_RetroTerminalEffect = source->_RetroTerminalEffect; - appearance->_PixelShaderPath = source->_PixelShaderPath; - appearance->_IntenseTextStyle = source->_IntenseTextStyle; appearance->_Opacity = source->_Opacity; - appearance->_AdjustIndistinguishableColors = source->_AdjustIndistinguishableColors; + +#define APPEARANCE_SETTINGS_COPY(type, name, jsonKey, ...) \ + appearance->_##name = source->_##name; + MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_COPY) +#undef APPEARANCE_SETTINGS_COPY + return appearance; } @@ -65,19 +49,13 @@ Json::Value AppearanceConfig::ToJson() const JsonUtils::SetValueForKey(json, BackgroundKey, _Background); JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor); - JsonUtils::SetValueForKey(json, ColorSchemeKey, _ColorSchemeName); - JsonUtils::SetValueForKey(json, CursorHeightKey, _CursorHeight); - JsonUtils::SetValueForKey(json, CursorShapeKey, _CursorShape); - JsonUtils::SetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); - JsonUtils::SetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); - JsonUtils::SetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); - JsonUtils::SetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); - JsonUtils::SetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); - JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); - JsonUtils::SetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle); - JsonUtils::SetValueForKey(json, AdjustIndistinguishableColorsKey, _AdjustIndistinguishableColors); JsonUtils::SetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); +#define APPEARANCE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \ + JsonUtils::SetValueForKey(json, jsonKey, _##name); + MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_TO_JSON) +#undef APPEARANCE_SETTINGS_TO_JSON + return json; } @@ -98,19 +76,14 @@ void AppearanceConfig::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, BackgroundKey, _Background); JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor); - JsonUtils::GetValueForKey(json, CursorHeightKey, _CursorHeight); - JsonUtils::GetValueForKey(json, ColorSchemeKey, _ColorSchemeName); - JsonUtils::GetValueForKey(json, CursorShapeKey, _CursorShape); - JsonUtils::GetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); - JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); - JsonUtils::GetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); - JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); - JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); - JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); - JsonUtils::GetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle); - JsonUtils::GetValueForKey(json, AdjustIndistinguishableColorsKey, _AdjustIndistinguishableColors); + JsonUtils::GetValueForKey(json, LegacyAcrylicTransparencyKey, _Opacity); JsonUtils::GetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); + +#define APPEARANCE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ + JsonUtils::GetValueForKey(json, jsonKey, _##name); + MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_LAYER_JSON) +#undef APPEARANCE_SETTINGS_LAYER_JSON } winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourceProfile() diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h index 6cf9a5f2d..bbc30927e 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -19,6 +19,7 @@ Author(s): #include "AppearanceConfig.g.h" #include "JsonUtils.h" #include "IInheritable.h" +#include "MTSMSettings.h" #include namespace winrt::Microsoft::Terminal::Settings::Model::implementation @@ -35,26 +36,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ExpandedBackgroundImagePath(); - INHERITABLE_SETTING(Model::IAppearanceConfig, ConvergedAlignment, BackgroundImageAlignment, ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center); - - INHERITABLE_SETTING(Model::IAppearanceConfig, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); - INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, ColorSchemeName, L"Campbell"); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Foreground, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Background, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, SelectionBackground, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, CursorColor, nullptr); - INHERITABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::CursorStyle, CursorShape, Microsoft::Terminal::Core::CursorStyle::Bar); - INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, BackgroundImagePath); - - INHERITABLE_SETTING(Model::IAppearanceConfig, double, BackgroundImageOpacity, 1.0); - INHERITABLE_SETTING(Model::IAppearanceConfig, Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch::UniformToFill); - - INHERITABLE_SETTING(Model::IAppearanceConfig, bool, RetroTerminalEffect, false); - INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, PixelShaderPath, L""); - INHERITABLE_SETTING(Model::IAppearanceConfig, Model::IntenseStyle, IntenseTextStyle, Model::IntenseStyle::Bright); INHERITABLE_SETTING(Model::IAppearanceConfig, double, Opacity, 1.0); - INHERITABLE_SETTING(Model::IAppearanceConfig, bool, AdjustIndistinguishableColors, true); +#define APPEARANCE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ + INHERITABLE_SETTING(Model::IAppearanceConfig, type, name, ##__VA_ARGS__) + MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_INITIALIZE) +#undef APPEARANCE_SETTINGS_INITIALIZE private: winrt::weak_ref _sourceProfile; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index e35b3e4b7..2d397ad55 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -287,8 +287,6 @@ Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source) DUPLICATE_SETTING_MACRO(Commandline); DUPLICATE_SETTING_MACRO(StartingDirectory); DUPLICATE_SETTING_MACRO(AntialiasingMode); - DUPLICATE_SETTING_MACRO(ForceFullRepaintRendering); - DUPLICATE_SETTING_MACRO(SoftwareRendering); DUPLICATE_SETTING_MACRO(HistorySize); DUPLICATE_SETTING_MACRO(SnapOnInput); DUPLICATE_SETTING_MACRO(AltGrAliasing); diff --git a/src/cascadia/TerminalSettingsModel/FileUtils.cpp b/src/cascadia/TerminalSettingsModel/FileUtils.cpp index 845b37567..8bd6613dc 100644 --- a/src/cascadia/TerminalSettingsModel/FileUtils.cpp +++ b/src/cascadia/TerminalSettingsModel/FileUtils.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include static constexpr std::string_view Utf8Bom{ u8"\uFEFF" }; static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" }; @@ -41,72 +43,43 @@ namespace winrt::Microsoft::Terminal::Settings::Model return baseSettingsPath; } - static bool _hasExpectedPermissions(const std::filesystem::path& path) + // Function Description: + // - Checks the permissions on this file, to make sure it can only be opened + // for writing by admins. We will be checking to see if the file is owned + // by the Builtin\Administrators group. If it's not, then it was likely + // tampered with. + // Arguments: + // - path: the path to the file to check + // Return Value: + // - true if it had the expected permissions. False otherwise. + static bool _hasElevatedOnlyPermissions(const std::filesystem::path& path) { - // If we want to only open the file if it's elevated, check the - // permissions on this file. We want to make sure that: - // * Everyone has permission to read - // * admins can do anything - // * no one else can do anything. - PACL pAcl{ nullptr }; // This doesn't need to be cleanup up apparently + // If the file is owned by the administrators group, trust the + // administrators instead of checking the DACL permissions. It's simpler + // and more flexible. - auto status = GetNamedSecurityInfo(path.c_str(), - SE_FILE_OBJECT, - DACL_SECURITY_INFORMATION, - nullptr, - nullptr, - &pAcl, - nullptr, - nullptr); + wil::unique_hlocal_security_descriptor sd; + PSID psidOwner{ nullptr }; + // The psidOwner pointer references the security descriptor, so it + // doesn't have to be freed separate from sd. + const auto status = GetNamedSecurityInfoW(path.c_str(), + SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION, + &psidOwner, + nullptr, + nullptr, + nullptr, + wil::out_param_ptr(sd)); THROW_IF_WIN32_ERROR(status); - PEXPLICIT_ACCESS pEA{ nullptr }; - DWORD count = 0; - status = GetExplicitEntriesFromAcl(pAcl, &count, &pEA); - THROW_IF_WIN32_ERROR(status); + wil::unique_any_psid psidAdmins{ nullptr }; + THROW_IF_WIN32_BOOL_FALSE( + ConvertStringSidToSidW(L"BA", wil::out_param_ptr(psidAdmins))); - auto explicitAccessCleanup = wil::scope_exit([&]() { ::LocalFree(pEA); }); - - if (count != 2) - { - return false; - } - - // Now, get the Everyone and Admins SIDS so we can make sure they're - // the ones in this file. - - wil::unique_sid everyoneSid; - wil::unique_sid adminGroupSid; - SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; - SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; - - // Create a SID for the BUILTIN\Administrators group. - THROW_IF_WIN32_BOOL_FALSE(AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroupSid)); - - // Create a well-known SID for the Everyone group. - THROW_IF_WIN32_BOOL_FALSE(AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid)); - - bool hadExpectedPermissions = true; - - // Check that the permissions are what we'd expect them to be if only - // admins can write to the file. This is basically a mirror of what we - // set up in `WriteUTF8File`. - - // For grfAccessPermissions, GENERIC_ALL turns into STANDARD_RIGHTS_ALL, - // and GENERIC_READ -> READ_CONTROL - hadExpectedPermissions &= WI_AreAllFlagsSet(pEA[0].grfAccessPermissions, STANDARD_RIGHTS_ALL); - hadExpectedPermissions &= pEA[0].grfInheritance == NO_INHERITANCE; - hadExpectedPermissions &= pEA[0].Trustee.TrusteeForm == TRUSTEE_IS_SID; - // SIDs are void*'s that happen to convert to a wchar_t - hadExpectedPermissions &= *(pEA[0].Trustee.ptstrName) == *(LPWSTR)(adminGroupSid.get()); - - // Now check the other EXPLICIT_ACCESS - hadExpectedPermissions &= WI_IsFlagSet(pEA[1].grfAccessPermissions, READ_CONTROL); - hadExpectedPermissions &= pEA[1].grfInheritance == NO_INHERITANCE; - hadExpectedPermissions &= pEA[1].Trustee.TrusteeForm == TRUSTEE_IS_SID; - hadExpectedPermissions &= *(pEA[1].Trustee.ptstrName) == *(LPWSTR)(everyoneSid.get()); - - return hadExpectedPermissions; + // Compare the owner SID to the administrators SID via + // EqualSid(psidOwner, psidAdmins). This does a low-level memory + // comparison of the SIDs. + return EqualSid(psidOwner, psidAdmins.get()); } // Tries to read a file somewhat atomically without locking it. // Strips the UTF8 BOM if it exists. @@ -114,7 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model { if (elevatedOnly) { - const bool hadExpectedPermissions{ _hasExpectedPermissions(path) }; + const bool hadExpectedPermissions{ _hasElevatedOnlyPermissions(path) }; if (!hadExpectedPermissions) { // delete the file. It's been compromised. @@ -192,61 +165,56 @@ namespace winrt::Microsoft::Terminal::Settings::Model const bool elevatedOnly) { SECURITY_ATTRIBUTES sa; + // stash the security descriptor here, so it will stay in context until + // after the call to CreateFile. If it gets cleaned up before that, then + // CreateFile will fail + wil::unique_hlocal_security_descriptor sd; if (elevatedOnly) { - // This is very vaguely taken from - // https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c-- - // With using https://docs.microsoft.com/en-us/windows/win32/secauthz/well-known-sids - // to find out that - // * SECURITY_NT_AUTHORITY+SECURITY_LOCAL_SYSTEM_RID == NT AUTHORITY\SYSTEM - // * SECURITY_NT_AUTHORITY+SECURITY_BUILTIN_DOMAIN_RID+DOMAIN_ALIAS_RID_ADMINS == BUILTIN\Administrators - // * SECURITY_WORLD_SID_AUTHORITY+SECURITY_WORLD_RID == Everyone + // Initialize the security descriptor so only admins can write the + // file. We'll initialize the SECURITY_DESCRIPTOR with a + // single entry (ACE) -- a mandatory label (i.e. a + // LABEL_SECURITY_INFORMATION) that sets the file integrity level to + // "high", with a no-write-up policy. // - // Raymond Chen recommended that I make this file only writable by - // SYSTEM, but if I did that, then even we can't write the file - // while elevated, which isn't what we want. + // When accessed from a security context at a lower integrity level, + // the no-write-up policy filters out rights that aren't in the + // object type's generic read and execute set (for the file type, + // that's FILE_GENERIC_READ | FILE_GENERIC_EXECUTE). + // + // Another option we considered here was manually setting the ACLs + // on this file such that Builtin\Admins could read&write the file, + // and all users could only read. + // + // Big thanks to @eryksun in GH#11222 for helping with this. This + // alternative method was chosen because it's considerably simpler. - wil::unique_sid everyoneSid; - wil::unique_sid adminGroupSid; - SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; - SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; - - // Create a SID for the BUILTIN\Administrators group. - THROW_IF_WIN32_BOOL_FALSE(AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroupSid)); - - // Create a well-known SID for the Everyone group. - THROW_IF_WIN32_BOOL_FALSE(AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid)); - - EXPLICIT_ACCESS ea[2]{}; - - // Grant Admins all permissions on this file - ea[0].grfAccessPermissions = GENERIC_ALL; - ea[0].grfAccessMode = SET_ACCESS; - ea[0].grfInheritance = NO_INHERITANCE; - ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; - ea[0].Trustee.ptstrName = (LPWSTR)(adminGroupSid.get()); - - // Grant Everyone the permission or read this file - ea[1].grfAccessPermissions = GENERIC_READ; - ea[1].grfAccessMode = SET_ACCESS; - ea[1].grfInheritance = NO_INHERITANCE; - ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; - ea[1].Trustee.ptstrName = (LPWSTR)(everyoneSid.get()); - - ACL acl; - PACL pAcl = &acl; - THROW_IF_WIN32_ERROR(SetEntriesInAcl(2, ea, nullptr, &pAcl)); - - SECURITY_DESCRIPTOR sd; - THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)); - THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorDacl(&sd, true, pAcl, false)); + // The required security descriptor can be created easily from the + // SDDL string: "S:(ML;;NW;;;HI)" + // (i.e. SACL:mandatory label;;no write up;;;high integrity level) + unsigned long cb; + THROW_IF_WIN32_BOOL_FALSE( + ConvertStringSecurityDescriptorToSecurityDescriptor(L"S:(ML;;NW;;;HI)", + SDDL_REVISION_1, + wil::out_param_ptr(sd), + &cb)); // Initialize a security attributes structure. sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = &sd; + sa.lpSecurityDescriptor = sd.get(); sa.bInheritHandle = false; + + // If we're running in an elevated context, when this file is + // created, it will automatically be owned by + // Builtin\Administrators, which will pass the above + // _hasElevatedOnlyPermissions check. + // + // Programs running in an elevated context will be free to write the + // file, and unelevated processes will be able to read the file. An + // unelevated process could always delete the file and rename a new + // file in it's place (a la the way `vim.exe` saves files), but if + // they do that, the new file _won't_ be owned by Administrators, + // failing the above check. } wil::unique_hfile file{ CreateFileW(path.c_str(), diff --git a/src/cascadia/TerminalSettingsModel/FontConfig.cpp b/src/cascadia/TerminalSettingsModel/FontConfig.cpp index 5d57eb355..62ebc5aac 100644 --- a/src/cascadia/TerminalSettingsModel/FontConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/FontConfig.cpp @@ -12,11 +12,6 @@ using namespace Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; static constexpr std::string_view FontInfoKey{ "font" }; -static constexpr std::string_view FontFaceKey{ "face" }; -static constexpr std::string_view FontSizeKey{ "size" }; -static constexpr std::string_view FontWeightKey{ "weight" }; -static constexpr std::string_view FontFeaturesKey{ "features" }; -static constexpr std::string_view FontAxesKey{ "axes" }; static constexpr std::string_view LegacyFontFaceKey{ "fontFace" }; static constexpr std::string_view LegacyFontSizeKey{ "fontSize" }; static constexpr std::string_view LegacyFontWeightKey{ "fontWeight" }; @@ -29,11 +24,12 @@ winrt::Microsoft::Terminal::Settings::Model::implementation::FontConfig::FontCon winrt::com_ptr FontConfig::CopyFontInfo(const FontConfig* source, winrt::weak_ref sourceProfile) { auto fontInfo{ winrt::make_self(std::move(sourceProfile)) }; - fontInfo->_FontFace = source->_FontFace; - fontInfo->_FontSize = source->_FontSize; - fontInfo->_FontWeight = source->_FontWeight; - fontInfo->_FontAxes = source->_FontAxes; - fontInfo->_FontFeatures = source->_FontFeatures; + +#define FONT_SETTINGS_COPY(type, name, jsonKey, ...) \ + fontInfo->_##name = source->_##name; + MTSM_FONT_SETTINGS(FONT_SETTINGS_COPY) +#undef FONT_SETTINGS_COPY + return fontInfo; } @@ -41,11 +37,10 @@ Json::Value FontConfig::ToJson() const { Json::Value json{ Json::ValueType::objectValue }; - JsonUtils::SetValueForKey(json, FontFaceKey, _FontFace); - JsonUtils::SetValueForKey(json, FontSizeKey, _FontSize); - JsonUtils::SetValueForKey(json, FontWeightKey, _FontWeight); - JsonUtils::SetValueForKey(json, FontAxesKey, _FontAxes); - JsonUtils::SetValueForKey(json, FontFeaturesKey, _FontFeatures); +#define FONT_SETTINGS_TO_JSON(type, name, jsonKey, ...) \ + JsonUtils::SetValueForKey(json, jsonKey, _##name); + MTSM_FONT_SETTINGS(FONT_SETTINGS_TO_JSON) +#undef FONT_SETTINGS_TO_JSON return json; } @@ -69,11 +64,10 @@ void FontConfig::LayerJson(const Json::Value& json) { // A font object is defined, use that const auto fontInfoJson = json[JsonKey(FontInfoKey)]; - JsonUtils::GetValueForKey(fontInfoJson, FontFaceKey, _FontFace); - JsonUtils::GetValueForKey(fontInfoJson, FontSizeKey, _FontSize); - JsonUtils::GetValueForKey(fontInfoJson, FontWeightKey, _FontWeight); - JsonUtils::GetValueForKey(fontInfoJson, FontFeaturesKey, _FontFeatures); - JsonUtils::GetValueForKey(fontInfoJson, FontAxesKey, _FontAxes); +#define FONT_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ + JsonUtils::GetValueForKey(fontInfoJson, jsonKey, _##name); + MTSM_FONT_SETTINGS(FONT_SETTINGS_LAYER_JSON) +#undef FONT_SETTINGS_LAYER_JSON } else { diff --git a/src/cascadia/TerminalSettingsModel/FontConfig.h b/src/cascadia/TerminalSettingsModel/FontConfig.h index 6c53ed4ea..db69fd08f 100644 --- a/src/cascadia/TerminalSettingsModel/FontConfig.h +++ b/src/cascadia/TerminalSettingsModel/FontConfig.h @@ -19,6 +19,7 @@ Author(s): #include "pch.h" #include "FontConfig.g.h" #include "JsonUtils.h" +#include "MTSMSettings.h" #include "../inc/cppwinrt_utils.h" #include "IInheritable.h" #include @@ -39,11 +40,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::Profile SourceProfile(); - INHERITABLE_SETTING(Model::FontConfig, hstring, FontFace, DEFAULT_FONT_FACE); - INHERITABLE_SETTING(Model::FontConfig, int32_t, FontSize, DEFAULT_FONT_SIZE); - INHERITABLE_SETTING(Model::FontConfig, Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT); - INHERITABLE_SETTING(Model::FontConfig, IFontAxesMap, FontAxes); - INHERITABLE_SETTING(Model::FontConfig, IFontFeatureMap, FontFeatures); +#define FONT_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ + INHERITABLE_SETTING(Model::FontConfig, type, name, ##__VA_ARGS__) + MTSM_FONT_SETTINGS(FONT_SETTINGS_INITIALIZE) +#undef FONT_SETTINGS_INITIALIZE private: winrt::weak_ref _sourceProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 2680cb857..0ffe584fc 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -18,48 +18,7 @@ using namespace winrt::Microsoft::UI::Xaml::Controls; static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" }; static constexpr std::string_view ActionsKey{ "actions" }; static constexpr std::string_view DefaultProfileKey{ "defaultProfile" }; -static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" }; -static constexpr std::string_view InitialRowsKey{ "initialRows" }; -static constexpr std::string_view InitialColsKey{ "initialCols" }; -static constexpr std::string_view InitialPositionKey{ "initialPosition" }; -static constexpr std::string_view CenterOnLaunchKey{ "centerOnLaunch" }; -static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; -static constexpr std::string_view LanguageKey{ "language" }; -static constexpr std::string_view ThemeKey{ "theme" }; -static constexpr std::string_view TabWidthModeKey{ "tabWidthMode" }; -static constexpr std::string_view UseAcrylicInTabRowKey{ "useAcrylicInTabRow" }; -static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; -static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" }; -static constexpr std::string_view InputServiceWarningKey{ "inputServiceWarning" }; -static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" }; -static constexpr std::string_view CopyFormattingKey{ "copyFormatting" }; -static constexpr std::string_view WarnAboutLargePasteKey{ "largePasteWarning" }; -static constexpr std::string_view WarnAboutMultiLinePasteKey{ "multiLinePasteWarning" }; -static constexpr std::string_view TrimPasteKey{ "trimPaste" }; -static constexpr std::string_view LaunchModeKey{ "launchMode" }; -static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" }; -static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" }; -static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" }; -static constexpr std::string_view FirstWindowPreferenceKey{ "firstWindowPreference" }; -static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" }; static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" }; -static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" }; -static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; -static constexpr std::string_view StartupActionsKey{ "startupActions" }; -static constexpr std::string_view FocusFollowMouseKey{ "focusFollowMouse" }; -static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" }; -static constexpr std::string_view TrimBlockSelectionKey{ "trimBlockSelection" }; -static constexpr std::string_view AlwaysShowNotificationIconKey{ "alwaysShowNotificationIcon" }; -static constexpr std::string_view MinimizeToNotificationAreaKey{ "minimizeToNotificationArea" }; -static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSources" }; -static constexpr std::string_view ShowAdminShieldKey{ "showAdminShield" }; - -static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; - -static constexpr std::string_view ForceFullRepaintRenderingKey{ "experimental.rendering.forceFullRepaint" }; -static constexpr std::string_view SoftwareRenderingKey{ "experimental.rendering.software" }; -static constexpr std::string_view ForceVTInputKey{ "experimental.input.forceVT" }; -static constexpr std::string_view DetectURLsKey{ "experimental.detectURLs" }; // Method Description: // - Copies any extraneous data from the parent before completing a CreateChild call @@ -86,45 +45,6 @@ void GlobalAppSettings::_FinalizeInheritance() winrt::com_ptr GlobalAppSettings::Copy() const { auto globals{ winrt::make_self() }; - globals->_InitialRows = _InitialRows; - globals->_InitialCols = _InitialCols; - globals->_AlwaysShowTabs = _AlwaysShowTabs; - globals->_ShowTitleInTitlebar = _ShowTitleInTitlebar; - globals->_ConfirmCloseAllTabs = _ConfirmCloseAllTabs; - globals->_Language = _Language; - globals->_Theme = _Theme; - globals->_TabWidthMode = _TabWidthMode; - globals->_UseAcrylicInTabRow = _UseAcrylicInTabRow; - globals->_ShowTabsInTitlebar = _ShowTabsInTitlebar; - globals->_WordDelimiters = _WordDelimiters; - globals->_InputServiceWarning = _InputServiceWarning; - globals->_CopyOnSelect = _CopyOnSelect; - globals->_CopyFormatting = _CopyFormatting; - globals->_WarnAboutLargePaste = _WarnAboutLargePaste; - globals->_WarnAboutMultiLinePaste = _WarnAboutMultiLinePaste; - globals->_TrimPaste = _TrimPaste; - globals->_InitialPosition = _InitialPosition; - globals->_CenterOnLaunch = _CenterOnLaunch; - globals->_LaunchMode = _LaunchMode; - globals->_SnapToGridOnResize = _SnapToGridOnResize; - globals->_ForceFullRepaintRendering = _ForceFullRepaintRendering; - globals->_SoftwareRendering = _SoftwareRendering; - globals->_ForceVTInput = _ForceVTInput; - globals->_DebugFeaturesEnabled = _DebugFeaturesEnabled; - globals->_StartOnUserLogin = _StartOnUserLogin; - globals->_FirstWindowPreference = _FirstWindowPreference; - globals->_AlwaysOnTop = _AlwaysOnTop; - globals->_TabSwitcherMode = _TabSwitcherMode; - globals->_DisableAnimations = _DisableAnimations; - globals->_StartupActions = _StartupActions; - globals->_FocusFollowMouse = _FocusFollowMouse; - globals->_WindowingBehavior = _WindowingBehavior; - globals->_TrimBlockSelection = _TrimBlockSelection; - globals->_DetectURLs = _DetectURLs; - globals->_MinimizeToNotificationArea = _MinimizeToNotificationArea; - globals->_AlwaysShowNotificationIcon = _AlwaysShowNotificationIcon; - globals->_DisabledProfileSources = _DisabledProfileSources; - globals->_ShowAdminShield = _ShowAdminShield; globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile; @@ -132,6 +52,11 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_actionMap = _actionMap->Copy(); globals->_keybindingsWarnings = _keybindingsWarnings; +#define GLOBAL_SETTINGS_COPY(type, name, jsonKey, ...) \ + globals->_##name = _##name; + MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_COPY) +#undef GLOBAL_SETTINGS_COPY + if (_colorSchemes) { for (auto kv : _colorSchemes) @@ -189,51 +114,15 @@ winrt::com_ptr GlobalAppSettings::FromJson(const Json::Value& void GlobalAppSettings::LayerJson(const Json::Value& json) { JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile); - JsonUtils::GetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs); - JsonUtils::GetValueForKey(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs); - JsonUtils::GetValueForKey(json, InitialRowsKey, _InitialRows); - JsonUtils::GetValueForKey(json, InitialColsKey, _InitialCols); - JsonUtils::GetValueForKey(json, InitialPositionKey, _InitialPosition); - JsonUtils::GetValueForKey(json, CenterOnLaunchKey, _CenterOnLaunch); - JsonUtils::GetValueForKey(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar); - JsonUtils::GetValueForKey(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar); - JsonUtils::GetValueForKey(json, WordDelimitersKey, _WordDelimiters); - JsonUtils::GetValueForKey(json, CopyOnSelectKey, _CopyOnSelect); - JsonUtils::GetValueForKey(json, InputServiceWarningKey, _InputServiceWarning); - JsonUtils::GetValueForKey(json, CopyFormattingKey, _CopyFormatting); - JsonUtils::GetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste); - JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste); - JsonUtils::GetValueForKey(json, TrimPasteKey, _TrimPaste); - JsonUtils::GetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference); - JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode); - JsonUtils::GetValueForKey(json, LanguageKey, _Language); - JsonUtils::GetValueForKey(json, ThemeKey, _Theme); - JsonUtils::GetValueForKey(json, TabWidthModeKey, _TabWidthMode); - JsonUtils::GetValueForKey(json, UseAcrylicInTabRowKey, _UseAcrylicInTabRow); - JsonUtils::GetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize); - // GetValueForKey will only override the current value if the key exists - JsonUtils::GetValueForKey(json, DebugFeaturesKey, _DebugFeaturesEnabled); - JsonUtils::GetValueForKey(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering); - JsonUtils::GetValueForKey(json, SoftwareRenderingKey, _SoftwareRendering); - JsonUtils::GetValueForKey(json, ForceVTInputKey, _ForceVTInput); - JsonUtils::GetValueForKey(json, EnableStartupTaskKey, _StartOnUserLogin); - JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop); // GH#8076 - when adding enum values to this key, we also changed it from // "useTabSwitcher" to "tabSwitcherMode". Continue supporting // "useTabSwitcher", but prefer "tabSwitcherMode" JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode); - JsonUtils::GetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode); - JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations); - JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); - JsonUtils::GetValueForKey(json, FocusFollowMouseKey, _FocusFollowMouse); - JsonUtils::GetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior); - JsonUtils::GetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection); - JsonUtils::GetValueForKey(json, DetectURLsKey, _DetectURLs); - JsonUtils::GetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea); - JsonUtils::GetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon); - JsonUtils::GetValueForKey(json, DisabledProfileSourcesKey, _DisabledProfileSources); - JsonUtils::GetValueForKey(json, ShowAdminShieldKey, _ShowAdminShield); +#define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ + JsonUtils::GetValueForKey(json, jsonKey, _##name); + MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_LAYER_JSON) +#undef GLOBAL_SETTINGS_LAYER_JSON static constexpr std::array bindingsKeys{ LegacyKeybindingsKey, ActionsKey }; for (const auto& jsonKey : bindingsKeys) @@ -293,48 +182,12 @@ Json::Value GlobalAppSettings::ToJson() const { Json::Value json{ Json::ValueType::objectValue }; - // clang-format off - JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile); - JsonUtils::SetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs); - JsonUtils::SetValueForKey(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs); - JsonUtils::SetValueForKey(json, InitialRowsKey, _InitialRows); - JsonUtils::SetValueForKey(json, InitialColsKey, _InitialCols); - JsonUtils::SetValueForKey(json, InitialPositionKey, _InitialPosition); - JsonUtils::SetValueForKey(json, CenterOnLaunchKey, _CenterOnLaunch); - JsonUtils::SetValueForKey(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar); - JsonUtils::SetValueForKey(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar); - JsonUtils::SetValueForKey(json, WordDelimitersKey, _WordDelimiters); - JsonUtils::SetValueForKey(json, InputServiceWarningKey, _InputServiceWarning); - JsonUtils::SetValueForKey(json, CopyOnSelectKey, _CopyOnSelect); - JsonUtils::SetValueForKey(json, CopyFormattingKey, _CopyFormatting); - JsonUtils::SetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste); - JsonUtils::SetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste); - JsonUtils::SetValueForKey(json, TrimPasteKey, _TrimPaste); - JsonUtils::SetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference); - JsonUtils::SetValueForKey(json, LaunchModeKey, _LaunchMode); - JsonUtils::SetValueForKey(json, LanguageKey, _Language); - JsonUtils::SetValueForKey(json, ThemeKey, _Theme); - JsonUtils::SetValueForKey(json, TabWidthModeKey, _TabWidthMode); - JsonUtils::SetValueForKey(json, UseAcrylicInTabRowKey, _UseAcrylicInTabRow); - JsonUtils::SetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize); - JsonUtils::SetValueForKey(json, DebugFeaturesKey, _DebugFeaturesEnabled); - JsonUtils::SetValueForKey(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering); - JsonUtils::SetValueForKey(json, SoftwareRenderingKey, _SoftwareRendering); - JsonUtils::SetValueForKey(json, ForceVTInputKey, _ForceVTInput); - JsonUtils::SetValueForKey(json, EnableStartupTaskKey, _StartOnUserLogin); - JsonUtils::SetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop); - JsonUtils::SetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode); - JsonUtils::SetValueForKey(json, DisableAnimationsKey, _DisableAnimations); - JsonUtils::SetValueForKey(json, StartupActionsKey, _StartupActions); - JsonUtils::SetValueForKey(json, FocusFollowMouseKey, _FocusFollowMouse); - JsonUtils::SetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior); - JsonUtils::SetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection); - JsonUtils::SetValueForKey(json, DetectURLsKey, _DetectURLs); - JsonUtils::SetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea); - JsonUtils::SetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon); - JsonUtils::SetValueForKey(json, DisabledProfileSourcesKey, _DisabledProfileSources); - JsonUtils::SetValueForKey(json, ShowAdminShieldKey, _ShowAdminShield); - // clang-format on + JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile); + +#define GLOBAL_SETTINGS_TO_JSON(type, name, jsonKey, ...) \ + JsonUtils::SetValueForKey(json, jsonKey, _##name); + MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_TO_JSON) +#undef GLOBAL_SETTINGS_TO_JSON json[JsonKey(ActionsKey)] = _actionMap->ToJson(); return json; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 022700264..14584b26a 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -17,6 +17,7 @@ Author(s): #include "GlobalAppSettings.g.h" #include "IInheritable.h" +#include "MTSMSettings.h" #include "ActionMap.h" #include "Command.h" @@ -61,46 +62,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DisableAnimations(!invertedDisableAnimationsValue); } - INHERITABLE_SETTING(Model::GlobalAppSettings, int32_t, InitialRows, DEFAULT_ROWS); - INHERITABLE_SETTING(Model::GlobalAppSettings, int32_t, InitialCols, DEFAULT_COLS); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowTabs, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ShowTitleInTitlebar, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ConfirmCloseAllTabs, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, Language); - INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::UI::Xaml::ElementTheme, Theme, winrt::Windows::UI::Xaml::ElementTheme::Default); - INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, UseAcrylicInTabRow, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ShowTabsInTitlebar, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, CopyOnSelect, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, InputServiceWarning, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutLargePaste, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutMultiLinePaste, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, TrimPaste, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchPosition, InitialPosition, nullptr, nullptr); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, CenterOnLaunch, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, Model::FirstWindowPreference, FirstWindowPreference, FirstWindowPreference::DefaultProfile); - INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchMode, LaunchMode, LaunchMode::DefaultMode); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, SnapToGridOnResize, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceFullRepaintRendering, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, SoftwareRendering, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceVTInput, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DebugFeaturesEnabled, debugFeaturesDefault); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, StartOnUserLogin, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysOnTop, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DisableAnimations, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, StartupActions, L""); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, FocusFollowMouse, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, Model::WindowingMode, WindowingBehavior, Model::WindowingMode::UseNew); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, TrimBlockSelection, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DetectURLs, true); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToNotificationArea, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowNotificationIcon, false); - INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, nullptr); INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L""); - INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ShowAdminShield, true); + +#define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ + INHERITABLE_SETTING(Model::GlobalAppSettings, type, name, ##__VA_ARGS__) + MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_INITIALIZE) +#undef GLOBAL_SETTINGS_INITIALIZE private: #ifdef NDEBUG diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h new file mode 100644 index 000000000..6719818e7 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -0,0 +1,96 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- MTSMSettings.h + +Abstract: +- Contains most of the settings within Terminal Settings Model (global, profile, font, appearance) +- To add a new setting to any one of those classes, simply add it to the respective list below, following the macro format + +Author(s): +- Pankaj Bhojwani - October 2021 + +--*/ +#pragma once + +// Macro format (defaultArgs are optional): +// (type, name, jsonKey, defaultArgs) + +#define MTSM_GLOBAL_SETTINGS(X) \ + X(int32_t, InitialRows, "initialRows", 30) \ + X(int32_t, InitialCols, "initialCols", 80) \ + X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \ + X(bool, CopyOnSelect, "copyOnSelect", false) \ + X(bool, FocusFollowMouse, "focusFollowMouse", false) \ + X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false) \ + X(bool, SoftwareRendering, "experimental.rendering.software", false) \ + X(bool, ForceVTInput, "experimental.input.forceVT", false) \ + X(bool, TrimBlockSelection, "trimBlockSelection", false) \ + X(bool, DetectURLs, "experimental.detectURLs", true) \ + X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \ + X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \ + X(bool, ConfirmCloseAllTabs, "confirmCloseAllTabs", true) \ + X(hstring, Language, "language") \ + X(winrt::Windows::UI::Xaml::ElementTheme, Theme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default) \ + X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \ + X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false) \ + X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true) \ + X(bool, InputServiceWarning, "inputServiceWarning", true) \ + X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, "copyFormatting", 0) \ + X(bool, WarnAboutLargePaste, "largePasteWarning", true) \ + X(bool, WarnAboutMultiLinePaste, "multiLinePasteWarning", true) \ + X(Model::LaunchPosition, InitialPosition, "initialPosition", nullptr, nullptr) \ + X(bool, CenterOnLaunch, "centerOnLaunch", false) \ + X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \ + X(Model::LaunchMode, LaunchMode, "launchMode", LaunchMode::DefaultMode) \ + X(bool, SnapToGridOnResize, "snapToGridOnResize", true) \ + X(bool, DebugFeaturesEnabled, "debugFeatures", debugFeaturesDefault) \ + X(bool, StartOnUserLogin, "startOnUserLogin", false) \ + X(bool, AlwaysOnTop, "alwaysOnTop", false) \ + X(Model::TabSwitcherMode, TabSwitcherMode, "tabSwitcherMode", Model::TabSwitcherMode::InOrder) \ + X(bool, DisableAnimations, "disableAnimations", false) \ + X(hstring, StartupActions, "startupActions", L"") \ + X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \ + X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \ + X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \ + X(winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, "disabledProfileSources", nullptr) \ + X(bool, ShowAdminShield, "showAdminShield", true) \ + X(bool, TrimPaste, "trimPaste", true) + +#define MTSM_PROFILE_SETTINGS(X) \ + X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ + X(bool, SnapOnInput, "snapOnInput", true) \ + X(bool, AltGrAliasing, "altGrAliasing", true) \ + X(bool, UseAcrylic, "useAcrylic", false) \ + X(hstring, Commandline, "commandline", L"%SystemRoot%\\System32\\cmd.exe") \ + X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \ + X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ + X(hstring, StartingDirectory, "startingDirectory") \ + X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \ + X(guid, ConnectionType, "connectionType") \ + X(hstring, Icon, "icon", L"\uE756") \ + X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Graceful) \ + X(hstring, TabTitle, "tabTitle") \ + X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) + +#define MTSM_FONT_SETTINGS(X) \ + X(hstring, FontFace, "face", DEFAULT_FONT_FACE) \ + X(int32_t, FontSize, "size", DEFAULT_FONT_SIZE) \ + X(winrt::Windows::UI::Text::FontWeight, FontWeight, "weight", DEFAULT_FONT_WEIGHT) \ + X(IFontAxesMap, FontAxes, "axes") \ + X(IFontFeatureMap, FontFeatures, "features") + +#define MTSM_APPEARANCE_SETTINGS(X) \ + X(Core::CursorStyle, CursorShape, "cursorShape", Core::CursorStyle::Bar) \ + X(uint32_t, CursorHeight, "cursorHeight", DEFAULT_CURSOR_HEIGHT) \ + X(double, BackgroundImageOpacity, "backgroundImageOpacity", 1.0) \ + X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \ + X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \ + X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \ + X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \ + X(hstring, ColorSchemeName, "colorScheme", L"Campbell") \ + X(hstring, BackgroundImagePath, "backgroundImage") \ + X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \ + X(bool, AdjustIndistinguishableColors, "adjustIndistinguishableColors", true) diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 60de4fd1c..5afc82375 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -50,6 +50,7 @@ GlobalAppSettings.idl + diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters index a99f5e654..287892c4a 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters @@ -76,6 +76,7 @@ json + diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index 287ce8970..f56e99d4a 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -26,24 +26,9 @@ static constexpr std::string_view GuidKey{ "guid" }; static constexpr std::string_view SourceKey{ "source" }; static constexpr std::string_view HiddenKey{ "hidden" }; -static constexpr std::string_view TabTitleKey{ "tabTitle" }; -static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" }; -static constexpr std::string_view HistorySizeKey{ "historySize" }; -static constexpr std::string_view SnapOnInputKey{ "snapOnInput" }; -static constexpr std::string_view AltGrAliasingKey{ "altGrAliasing" }; - -static constexpr std::string_view ConnectionTypeKey{ "connectionType" }; -static constexpr std::string_view CommandlineKey{ "commandline" }; static constexpr std::string_view FontInfoKey{ "font" }; -static constexpr std::string_view UseAcrylicKey{ "useAcrylic" }; -static constexpr std::string_view ScrollbarStateKey{ "scrollbarState" }; -static constexpr std::string_view CloseOnExitKey{ "closeOnExit" }; static constexpr std::string_view PaddingKey{ "padding" }; -static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; -static constexpr std::string_view IconKey{ "icon" }; -static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" }; static constexpr std::string_view TabColorKey{ "tabColor" }; -static constexpr std::string_view BellStyleKey{ "bellStyle" }; static constexpr std::string_view UnfocusedAppearanceKey{ "unfocusedAppearance" }; Profile::Profile(guid guid) noexcept : @@ -117,28 +102,17 @@ winrt::com_ptr Profile::CopySettings() const profile->_Name = _Name; profile->_Source = _Source; profile->_Hidden = _Hidden; - profile->_Icon = _Icon; - profile->_CloseOnExit = _CloseOnExit; - profile->_TabTitle = _TabTitle; profile->_TabColor = _TabColor; - profile->_SuppressApplicationTitle = _SuppressApplicationTitle; - profile->_UseAcrylic = _UseAcrylic; - profile->_ScrollState = _ScrollState; profile->_Padding = _Padding; - profile->_Commandline = _Commandline; - profile->_StartingDirectory = _StartingDirectory; - profile->_AntialiasingMode = _AntialiasingMode; - profile->_ForceFullRepaintRendering = _ForceFullRepaintRendering; - profile->_SoftwareRendering = _SoftwareRendering; - profile->_HistorySize = _HistorySize; - profile->_SnapOnInput = _SnapOnInput; - profile->_AltGrAliasing = _AltGrAliasing; - profile->_BellStyle = _BellStyle; - profile->_ConnectionType = _ConnectionType; profile->_Origin = _Origin; profile->_FontInfo = *fontInfo; profile->_DefaultAppearance = *defaultAppearance; +#define PROFILE_SETTINGS_COPY(type, name, jsonKey, ...) \ + profile->_##name = _##name; + MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_COPY) +#undef PROFILE_SETTINGS_COPY + if (_UnfocusedAppearance) { Model::AppearanceConfig unfocused{ nullptr }; @@ -196,31 +170,16 @@ void Profile::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, HiddenKey, _Hidden); JsonUtils::GetValueForKey(json, SourceKey, _Source); - // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" - JsonUtils::GetValueForKey(json, HistorySizeKey, _HistorySize); - JsonUtils::GetValueForKey(json, SnapOnInputKey, _SnapOnInput); - JsonUtils::GetValueForKey(json, AltGrAliasingKey, _AltGrAliasing); - JsonUtils::GetValueForKey(json, TabTitleKey, _TabTitle); - - // Control Settings - JsonUtils::GetValueForKey(json, ConnectionTypeKey, _ConnectionType); - JsonUtils::GetValueForKey(json, CommandlineKey, _Commandline); - JsonUtils::GetValueForKey(json, UseAcrylicKey, _UseAcrylic); - JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, _SuppressApplicationTitle); - JsonUtils::GetValueForKey(json, CloseOnExitKey, _CloseOnExit); - // Padding was never specified as an integer, but it was a common working mistake. // Allow it to be permissive. JsonUtils::GetValueForKey(json, PaddingKey, _Padding, JsonUtils::OptionalConverter>{}); - JsonUtils::GetValueForKey(json, ScrollbarStateKey, _ScrollState); - - JsonUtils::GetValueForKey(json, StartingDirectoryKey, _StartingDirectory); - - JsonUtils::GetValueForKey(json, IconKey, _Icon); - JsonUtils::GetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::GetValueForKey(json, TabColorKey, _TabColor); - JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle); + +#define PROFILE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ + JsonUtils::GetValueForKey(json, jsonKey, _##name); + MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_LAYER_JSON) +#undef PROFILE_SETTINGS_LAYER_JSON if (json.isMember(JsonKey(UnfocusedAppearanceKey))) { @@ -352,28 +311,15 @@ Json::Value Profile::ToJson() const JsonUtils::SetValueForKey(json, HiddenKey, writeBasicSettings ? Hidden() : _Hidden); JsonUtils::SetValueForKey(json, SourceKey, writeBasicSettings ? Source() : _Source); - // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" - JsonUtils::SetValueForKey(json, HistorySizeKey, _HistorySize); - JsonUtils::SetValueForKey(json, SnapOnInputKey, _SnapOnInput); - JsonUtils::SetValueForKey(json, AltGrAliasingKey, _AltGrAliasing); - JsonUtils::SetValueForKey(json, TabTitleKey, _TabTitle); - - // Control Settings - JsonUtils::SetValueForKey(json, ConnectionTypeKey, _ConnectionType); - JsonUtils::SetValueForKey(json, CommandlineKey, _Commandline); - JsonUtils::SetValueForKey(json, UseAcrylicKey, _UseAcrylic); - JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, _SuppressApplicationTitle); - JsonUtils::SetValueForKey(json, CloseOnExitKey, _CloseOnExit); - // PermissiveStringConverter is unnecessary for serialization JsonUtils::SetValueForKey(json, PaddingKey, _Padding); - JsonUtils::SetValueForKey(json, ScrollbarStateKey, _ScrollState); - JsonUtils::SetValueForKey(json, StartingDirectoryKey, _StartingDirectory); - JsonUtils::SetValueForKey(json, IconKey, _Icon); - JsonUtils::SetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::SetValueForKey(json, TabColorKey, _TabColor); - JsonUtils::SetValueForKey(json, BellStyleKey, _BellStyle); + +#define PROFILE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \ + JsonUtils::SetValueForKey(json, jsonKey, _##name); + MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_TO_JSON) +#undef PROFILE_SETTINGS_TO_JSON // Font settings const auto fontInfoImpl = winrt::get_self(_FontInfo); diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h index f69471332..db715f07a 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.h +++ b/src/cascadia/TerminalSettingsModel/Profile.h @@ -46,6 +46,7 @@ Author(s): #include "Profile.g.h" #include "IInheritable.h" +#include "MTSMSettings.h" #include "../inc/cppwinrt_utils.h" #include "JsonUtils.h" @@ -102,43 +103,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _FinalizeInheritance() override; + // Special fields WINRT_PROPERTY(bool, Deleted, false); WINRT_PROPERTY(OriginTag, Origin, OriginTag::None); - WINRT_PROPERTY(guid, Updates); - INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source())); + + // Nullable/optional settings + INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, TabColor, nullptr); + INHERITABLE_SETTING(Model::Profile, Model::IAppearanceConfig, UnfocusedAppearance, nullptr); + + // Settings that cannot be put in the macro because of how they are handled in ToJson/LayerJson INHERITABLE_SETTING(Model::Profile, hstring, Name, L"Default"); INHERITABLE_SETTING(Model::Profile, hstring, Source); INHERITABLE_SETTING(Model::Profile, bool, Hidden, false); - INHERITABLE_SETTING(Model::Profile, guid, ConnectionType); - - // Default Icon: Segoe MDL2 CommandPrompt icon - INHERITABLE_SETTING(Model::Profile, hstring, Icon, L"\uE756"); - - INHERITABLE_SETTING(Model::Profile, CloseOnExitMode, CloseOnExit, CloseOnExitMode::Graceful); - INHERITABLE_SETTING(Model::Profile, hstring, TabTitle); - INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, TabColor, nullptr); - INHERITABLE_SETTING(Model::Profile, bool, SuppressApplicationTitle, false); - - INHERITABLE_SETTING(Model::Profile, bool, UseAcrylic, false); - INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible); - + INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source())); INHERITABLE_SETTING(Model::Profile, hstring, Padding, DEFAULT_PADDING); - INHERITABLE_SETTING(Model::Profile, hstring, Commandline, L"%SystemRoot%\\System32\\cmd.exe"); - INHERITABLE_SETTING(Model::Profile, hstring, StartingDirectory); - - INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); - INHERITABLE_SETTING(Model::Profile, bool, ForceFullRepaintRendering, false); - INHERITABLE_SETTING(Model::Profile, bool, SoftwareRendering, false); - - INHERITABLE_SETTING(Model::Profile, int32_t, HistorySize, DEFAULT_HISTORY_SIZE); - INHERITABLE_SETTING(Model::Profile, bool, SnapOnInput, true); - INHERITABLE_SETTING(Model::Profile, bool, AltGrAliasing, true); - - INHERITABLE_SETTING(Model::Profile, Model::BellStyle, BellStyle, BellStyle::Audible); - - INHERITABLE_SETTING(Model::Profile, Model::IAppearanceConfig, UnfocusedAppearance, nullptr); +#define PROFILE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ + INHERITABLE_SETTING(Model::Profile, type, name, ##__VA_ARGS__) + MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_INITIALIZE) +#undef PROFILE_SETTINGS_INITIALIZE private: Model::IAppearanceConfig _DefaultAppearance{ winrt::make(weak_ref(*this)) }; diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 670303ff1..746caad25 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -74,8 +74,6 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(IAppearanceConfig, UnfocusedAppearance); INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.TextAntialiasingMode, AntialiasingMode); - INHERITABLE_PROFILE_SETTING(Boolean, ForceFullRepaintRendering); - INHERITABLE_PROFILE_SETTING(Boolean, SoftwareRendering); INHERITABLE_PROFILE_SETTING(Int32, HistorySize); INHERITABLE_PROFILE_SETTING(Boolean, SnapOnInput); diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index fe671a65a..2145032fd 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -2901,15 +2901,9 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs() auto defaultAttrs = si.GetAttributes(); auto conhostGreenAttrs = TextAttribute(); - - // Conhost and Terminal store attributes in different bits. - // conhostGreenAttrs.SetIndexedAttributes(std::nullopt, - // { static_cast(FOREGROUND_GREEN) }); - conhostGreenAttrs.SetIndexedBackground(FOREGROUND_GREEN); + conhostGreenAttrs.SetIndexedBackground(TextColor::DARK_GREEN); auto terminalGreenAttrs = TextAttribute(); - // terminalGreenAttrs.SetIndexedAttributes(std::nullopt, - // { static_cast(XTERM_GREEN_ATTR) }); - terminalGreenAttrs.SetIndexedBackground(XTERM_GREEN_ATTR); + terminalGreenAttrs.SetIndexedBackground(TextColor::DARK_GREEN); // Use an initial ^[[m to start printing with default-on-default sm.ProcessString(L"\x1b[m"); @@ -2942,7 +2936,7 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs() auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool isTerminal, const bool afterResize) { const auto width = viewport.width(); - // Conhost and Terminal store attributes in different bits. + // Conhost and Terminal attributes are potentially different. const auto greenAttrs = isTerminal ? terminalGreenAttrs : conhostGreenAttrs; for (short row = 0; row < tb.GetSize().Height(); row++) @@ -3034,13 +3028,11 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground() auto defaultAttrs = si.GetAttributes(); auto conhostBlueAttrs = defaultAttrs; - - // Conhost and Terminal store attributes in different bits. - conhostBlueAttrs.SetIndexedForeground(FOREGROUND_GREEN); - conhostBlueAttrs.SetIndexedBackground(FOREGROUND_BLUE); + conhostBlueAttrs.SetIndexedForeground(TextColor::DARK_GREEN); + conhostBlueAttrs.SetIndexedBackground(TextColor::DARK_BLUE); auto terminalBlueAttrs = TextAttribute(); - terminalBlueAttrs.SetIndexedForeground(XTERM_GREEN_ATTR); - terminalBlueAttrs.SetIndexedBackground(XTERM_BLUE_ATTR); + terminalBlueAttrs.SetIndexedForeground(TextColor::DARK_GREEN); + terminalBlueAttrs.SetIndexedBackground(TextColor::DARK_BLUE); // We're going to print 4 more rows than the entire height of the viewport, // causing the buffer to circle 4 times. This is 2 extra iterations of the @@ -3083,7 +3075,7 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground() const auto width = viewport.width(); const auto isTerminal = viewport.top() != 0; - // Conhost and Terminal store attributes in different bits. + // Conhost and Terminal attributes are potentially different. const auto blueAttrs = isTerminal ? terminalBlueAttrs : conhostBlueAttrs; for (short row = 0; row < viewport.bottom() - 2; row++) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0ceb487e3..36d489212 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -84,6 +84,7 @@ AppHost::AppHost() noexcept : _window->WindowMoved({ this, &AppHost::_WindowMoved }); _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); + _window->ShouldExitFullscreen({ &_logic, &winrt::TerminalApp::AppLogic::RequestExitFullscreen }); _window->MakeWindow(); _GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) { @@ -325,6 +326,7 @@ void AppHost::Initialize() _logic.FocusModeChanged({ this, &AppHost::_FocusModeChanged }); _logic.AlwaysOnTopChanged({ this, &AppHost::_AlwaysOnTopChanged }); _logic.RaiseVisualBell({ this, &AppHost::_RaiseVisualBell }); + _logic.SystemMenuChangeRequested({ this, &AppHost::_SystemMenuChangeRequested }); _logic.Create(); @@ -1276,6 +1278,27 @@ void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&, _window->OpenSystemMenu(std::nullopt, std::nullopt); } +void AppHost::_SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::TerminalApp::SystemMenuChangeArgs& args) +{ + switch (args.Action()) + { + case winrt::TerminalApp::SystemMenuChangeAction::Add: + { + auto handler = args.Handler(); + _window->AddToSystemMenu(args.Name(), [handler]() { handler(); }); + break; + } + case winrt::TerminalApp::SystemMenuChangeAction::Remove: + { + _window->RemoveFromSystemMenu(args.Name()); + break; + } + default: + { + } + } +} + // Method Description: // - Creates a Notification Icon and hooks up its handlers // Arguments: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 149c65705..6675e8209 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -96,6 +96,9 @@ private: void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::TerminalApp::SystemMenuChangeArgs& args); + winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 16ac019af..8ea325a42 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -24,6 +24,7 @@ using namespace ::Microsoft::Console::Types; using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; #define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS" +#define IDM_SYSTEM_MENU_BEGIN 0x1000 const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); @@ -321,6 +322,8 @@ void IslandWindow::Initialize() } } + _systemMenuNextItemId = IDM_SYSTEM_MENU_BEGIN; + // Enable vintage opacity by removing the XAML emergency backstop, GH#603. // We don't really care if this failed or not. TerminalTrySetTransparentBackground(true); @@ -608,6 +611,20 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize _NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam); return 0; } + case WM_SYSCOMMAND: + { + if (wparam == SC_RESTORE && _fullscreen) + { + _ShouldExitFullscreenHandlers(); + return 0; + } + auto search = _systemMenuItems.find(LOWORD(wparam)); + if (search != _systemMenuItems.end()) + { + search->second.callback(); + } + break; + } default: // We'll want to receive this message when explorer.exe restarts // so that we can re-add our icon to the notification area. @@ -1717,5 +1734,50 @@ void IslandWindow::OpenSystemMenu(const std::optional mouseX, const std::op } } +void IslandWindow::AddToSystemMenu(const winrt::hstring& itemLabel, winrt::delegate callback) +{ + const HMENU systemMenu = GetSystemMenu(_window.get(), FALSE); + UINT wID = _systemMenuNextItemId; + + MENUITEMINFOW item; + item.cbSize = sizeof(MENUITEMINFOW); + item.fMask = MIIM_STATE | MIIM_ID | MIIM_STRING; + item.fState = MF_ENABLED; + item.wID = wID; + item.dwTypeData = const_cast(itemLabel.c_str()); + item.cch = static_cast(itemLabel.size()); + + if (LOG_LAST_ERROR_IF(!InsertMenuItemW(systemMenu, wID, FALSE, &item))) + { + return; + } + _systemMenuItems.insert({ wID, { itemLabel, callback } }); + _systemMenuNextItemId++; +} + +void IslandWindow::RemoveFromSystemMenu(const winrt::hstring& itemLabel) +{ + const HMENU systemMenu = GetSystemMenu(_window.get(), FALSE); + int itemCount = GetMenuItemCount(systemMenu); + if (LOG_LAST_ERROR_IF(itemCount == -1)) + { + return; + } + + auto it = std::find_if(_systemMenuItems.begin(), _systemMenuItems.end(), [&itemLabel](const std::pair& elem) { + return elem.second.label == itemLabel; + }); + if (it == _systemMenuItems.end()) + { + return; + } + + if (LOG_LAST_ERROR_IF(!DeleteMenu(systemMenu, it->first, MF_BYCOMMAND))) + { + return; + } + _systemMenuItems.erase(it->first); +} + DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 8f6b283c1..15d652551 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -8,6 +8,12 @@ void SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept; +struct SystemMenuItemInfo +{ + winrt::hstring label; + winrt::delegate callback; +}; + class IslandWindow : public BaseWindow { @@ -54,6 +60,8 @@ public: void SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept; void OpenSystemMenu(const std::optional mouseX, const std::optional mouseY) const noexcept; + void AddToSystemMenu(const winrt::hstring& itemLabel, winrt::delegate callback); + void RemoveFromSystemMenu(const winrt::hstring& itemLabel); DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); @@ -65,6 +73,7 @@ public: WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate); WINRT_CALLBACK(NotifyNotificationIconMenuItemSelected, winrt::delegate); WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate); + WINRT_CALLBACK(ShouldExitFullscreen, winrt::delegate); WINRT_CALLBACK(WindowMoved, winrt::delegate); @@ -131,6 +140,9 @@ protected: bool _minimizeToNotificationArea{ false }; + std::unordered_map _systemMenuItems; + UINT _systemMenuNextItemId; + private: // This minimum width allows for width the tabs fit static constexpr long minimumWidth = 460L; diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 60dd263a2..7eee80e77 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -843,7 +843,11 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept HPAINTBUFFER buf = BeginBufferedPaint(hdc.get(), &rcRest, BPBF_TOPDOWNDIB, ¶ms, &opaqueDc); if (!buf || !opaqueDc) { - winrt::throw_last_error(); + // MSFT:34673647 - BeginBufferedPaint can fail, but it probably + // shouldn't bring the whole Terminal down with it. So don't + // throw_last_error here. + LOG_LAST_ERROR(); + return 0; } ::FillRect(opaqueDc, &rcRest, _backgroundBrush.get()); diff --git a/src/host/conattrs.cpp b/src/host/conattrs.cpp deleted file mode 100644 index 07d1f21f2..000000000 --- a/src/host/conattrs.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- conattrs.cpp - -Abstract: -- Defines common operations on console attributes, especially in regards to - finding the nearest color from a color table. - -Author(s): -- Mike Griese (migrie) 01-Sept-2017 ---*/ - -#include "precomp.h" -#include "../inc/conattrs.hpp" -#include - -// Function Description: -// - Converts the value of a xterm color table index to the windows color table equivalent. -// Arguments: -// - xtermTableEntry: the xterm color table index -// Return Value: -// - The windows color table equivalent. -WORD XtermToWindowsIndex(const size_t xtermTableEntry) noexcept -{ - const bool fRed = WI_IsFlagSet(xtermTableEntry, XTERM_RED_ATTR); - const bool fGreen = WI_IsFlagSet(xtermTableEntry, XTERM_GREEN_ATTR); - const bool fBlue = WI_IsFlagSet(xtermTableEntry, XTERM_BLUE_ATTR); - const bool fBright = WI_IsFlagSet(xtermTableEntry, XTERM_BRIGHT_ATTR); - - return (fRed ? WINDOWS_RED_ATTR : 0x0) + - (fGreen ? WINDOWS_GREEN_ATTR : 0x0) + - (fBlue ? WINDOWS_BLUE_ATTR : 0x0) + - (fBright ? WINDOWS_BRIGHT_ATTR : 0x0); -} - -// Function Description: -// - Converts the value of a xterm color table index to the windows color table -// equivalent. The range of values is [0, 255], where the lowest 16 are -// mapped to the equivalent Windows index, and the rest of the values are -// passed through. -// Arguments: -// - xtermTableEntry: the xterm color table index -// Return Value: -// - The windows color table equivalent. -WORD Xterm256ToWindowsIndex(const size_t xtermTableEntry) noexcept -{ - return xtermTableEntry < 16 ? XtermToWindowsIndex(xtermTableEntry) : - static_cast(xtermTableEntry); -} diff --git a/src/host/consoleInformation.cpp b/src/host/consoleInformation.cpp index 112905921..51ed7de9b 100644 --- a/src/host/consoleInformation.cpp +++ b/src/host/consoleInformation.cpp @@ -231,7 +231,7 @@ InputBuffer* const CONSOLE_INFORMATION::GetActiveInputBuffer() const COLORREF CONSOLE_INFORMATION::GetDefaultForeground() const noexcept { const auto fg = GetDefaultForegroundColor(); - return fg != INVALID_COLOR ? fg : GetColorTableEntry(LOBYTE(GetFillAttribute()) & FG_ATTRS); + return fg != INVALID_COLOR ? fg : GetLegacyColorTableEntry(LOBYTE(GetFillAttribute()) & FG_ATTRS); } // Method Description: @@ -246,23 +246,37 @@ COLORREF CONSOLE_INFORMATION::GetDefaultForeground() const noexcept COLORREF CONSOLE_INFORMATION::GetDefaultBackground() const noexcept { const auto bg = GetDefaultBackgroundColor(); - return bg != INVALID_COLOR ? bg : GetColorTableEntry((LOBYTE(GetFillAttribute()) & BG_ATTRS) >> 4); + return bg != INVALID_COLOR ? bg : GetLegacyColorTableEntry((LOBYTE(GetFillAttribute()) & BG_ATTRS) >> 4); } // Method Description: // - Get the colors of a particular text attribute, using our color table, // and our configured default attributes. // Arguments: -// - attr: the TextAttribute to retrieve the foreground color of. +// - attr: the TextAttribute to retrieve the foreground and background color of. // Return Value: // - The color values of the attribute's foreground and background. std::pair CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept +{ + return LookupAttributeColors(attr, GetDefaultForeground(), GetDefaultBackground()); +} + +// Method Description: +// - Get the colors of a particular text attribute, using our color table, +// and the given default color values. +// Arguments: +// - attr: the TextAttribute to retrieve the foreground and background color of. +// - defaultFg: the COLORREF to use for a default foreground color. +// - defaultBg: the COLORREF to use for a default background color. +// Return Value: +// - The color values of the attribute's foreground and background. +std::pair CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr, const COLORREF defaultFg, const COLORREF defaultBg) const noexcept { _blinkingState.RecordBlinkingUsage(attr); return attr.CalculateRgbColors( GetColorTable(), - GetDefaultForeground(), - GetDefaultBackground(), + defaultFg, + defaultBg, IsScreenReversed(), _blinkingState.IsBlinkingFaint()); } diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 6203aef0e..4f85d4a5b 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -605,7 +605,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont for (size_t i = 0; i < std::size(data.ColorTable); i++) { - gci.SetColorTableEntry(i, data.ColorTable[i]); + gci.SetLegacyColorTableEntry(i, data.ColorTable[i]); } context.SetDefaultAttributes(TextAttribute{ data.wAttributes }, TextAttribute{ data.wPopupAttributes }); @@ -1978,7 +1978,7 @@ void DoSrvPrivateMoveToBottom(SCREEN_INFORMATION& screenInfo) Globals& g = ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); - value = gci.GetColorTableEntry(::Xterm256ToWindowsIndex(index)); + value = gci.GetColorTableEntry(index); return S_OK; } @@ -2004,7 +2004,7 @@ void DoSrvPrivateMoveToBottom(SCREEN_INFORMATION& screenInfo) Globals& g = ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); - gci.SetColorTableEntry(::Xterm256ToWindowsIndex(index), value); + gci.SetColorTableEntry(index, value); // Update the screen colors if we're not a pty // No need to force a redraw in pty mode. diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 190eef792..7645ced09 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -7,7 +7,6 @@ - diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index e8877f96c..11256b620 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -138,9 +138,6 @@ Source Files - - Source Files - Source Files diff --git a/src/host/registry.cpp b/src/host/registry.cpp index 9b022d983..49bd06423 100644 --- a/src/host/registry.cpp +++ b/src/host/registry.cpp @@ -310,7 +310,7 @@ void Registry::LoadFromRegistry(_In_ PCWSTR const pwszConsoleTitle) nullptr); if (NT_SUCCESS(Status)) { - _pSettings->SetColorTableEntry(i, dwValue); + _pSettings->SetLegacyColorTableEntry(i, dwValue); } } diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index c860b3da3..1eb6090d7 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -110,6 +110,8 @@ void RenderData::UnlockConsole() noexcept const TextAttribute RenderData::GetDefaultBrushColors() noexcept { const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + _defaultForeground = gci.GetDefaultForeground(); + _defaultBackground = gci.GetDefaultBackground(); return gci.GetActiveOutputBuffer().GetAttributes(); } @@ -362,7 +364,7 @@ const std::vector RenderData::GetPatternId(const COORD /*location*/) con std::pair RenderData::GetAttributeColors(const TextAttribute& attr) const noexcept { const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - return gci.LookupAttributeColors(attr); + return gci.LookupAttributeColors(attr, _defaultForeground, _defaultBackground); } #pragma endregion diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index b1699531d..9b5ab7f6d 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -15,6 +15,7 @@ Author(s): #pragma once #include "../renderer/inc/IRenderData.hpp" +#include "../types/inc/colorTable.hpp" #include "../types/IUiaData.h" class RenderData final : @@ -71,4 +72,8 @@ public: void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute attr); const bool IsUiaDataInitialized() const noexcept override { return true; } #pragma endregion + +private: + COLORREF _defaultForeground = gsl::at(Microsoft::Console::Utils::CampbellColorTable(), 7); + COLORREF _defaultBackground = gsl::at(Microsoft::Console::Utils::CampbellColorTable(), 0); }; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 320e1148d..cc06df4ca 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -364,7 +364,7 @@ void SCREEN_INFORMATION::GetScreenBufferInformation(_Out_ PCOORD pcoordSize, // the copy length must be constant for now to keep OACR happy with buffer overruns. for (size_t i = 0; i < COLOR_TABLE_SIZE; i++) { - lpColorTable[i] = gci.GetColorTableEntry(i); + lpColorTable[i] = gci.GetLegacyColorTableEntry(i); } *pcoordMaximumWindowSize = GetMaxWindowSizeInCharacters(); diff --git a/src/host/selectionInput.cpp b/src/host/selectionInput.cpp index 70b4d4f38..cab5aa88b 100644 --- a/src/host/selectionInput.cpp +++ b/src/host/selectionInput.cpp @@ -661,13 +661,15 @@ bool Selection::_HandleColorSelection(const INPUT_KEY_INFO* const pInputKeyInfo) if (fAltPressed || fCtrlPressed) { TextAttribute selectionAttr; - const BYTE colorIndex = gsl::narrow_cast(wVirtualKeyCode - '0' + 6); + // The key number corresponds to the Windows color table order, so the value + // need to be transposed to obtain the index in an ANSI-compatible order. + const auto colorIndex = TextColor::TransposeLegacyIndex(wVirtualKeyCode - '0' + 6); if (fCtrlPressed) { // Setting background color. Set fg color to black. selectionAttr.SetIndexedBackground256(colorIndex); - selectionAttr.SetIndexedForeground256(0); + selectionAttr.SetIndexedForeground256(TextColor::DARK_BLACK); } else { diff --git a/src/host/server.h b/src/host/server.h index 91ea54537..2d8fa0d15 100644 --- a/src/host/server.h +++ b/src/host/server.h @@ -127,6 +127,7 @@ public: COLORREF GetDefaultForeground() const noexcept; COLORREF GetDefaultBackground() const noexcept; std::pair LookupAttributeColors(const TextAttribute& attr) const noexcept; + std::pair LookupAttributeColors(const TextAttribute& attr, const COLORREF defaultFg, const COLORREF defaultBg) const noexcept; void SetTitle(const std::wstring_view newTitle); void SetTitlePrefix(const std::wstring_view newTitlePrefix); diff --git a/src/host/settings.cpp b/src/host/settings.cpp index a9fb5d093..d1443cb32 100644 --- a/src/host/settings.cpp +++ b/src/host/settings.cpp @@ -82,8 +82,7 @@ Settings::Settings() : _CursorType = CursorType::Legacy; gsl::span tableView = { _colorTable.data(), _colorTable.size() }; - ::Microsoft::Console::Utils::Initialize256ColorTable(tableView); - ::Microsoft::Console::Utils::InitializeCampbellColorTableForConhost(tableView); + ::Microsoft::Console::Utils::InitializeColorTable(tableView); } // Routine Description: @@ -122,8 +121,8 @@ void Settings::ApplyDesktopSpecificDefaults() _uNumberOfHistoryBuffers = 4; _bHistoryNoDup = FALSE; - gsl::span tableView = { _colorTable.data(), _colorTable.size() }; - ::Microsoft::Console::Utils::InitializeCampbellColorTableForConhost(tableView); + gsl::span tableView = { _colorTable.data(), 16 }; + ::Microsoft::Console::Utils::InitializeColorTable(tableView); _fTrimLeadingZeros = false; _fEnableColorSelection = false; @@ -222,7 +221,7 @@ void Settings::InitFromStateInfo(_In_ PCONSOLE_STATE_INFO pStateInfo) _uNumberOfHistoryBuffers = pStateInfo->NumberOfHistoryBuffers; for (size_t i = 0; i < std::size(pStateInfo->ColorTable); i++) { - SetColorTableEntry(i, pStateInfo->ColorTable[i]); + SetLegacyColorTableEntry(i, pStateInfo->ColorTable[i]); } _uCodePage = pStateInfo->CodePage; _bWrapText = !!pStateInfo->fWrapText; @@ -267,7 +266,7 @@ CONSOLE_STATE_INFO Settings::CreateConsoleStateInfo() const csi.NumberOfHistoryBuffers = _uNumberOfHistoryBuffers; for (size_t i = 0; i < std::size(csi.ColorTable); i++) { - csi.ColorTable[i] = GetColorTableEntry(i); + csi.ColorTable[i] = GetLegacyColorTableEntry(i); } csi.CodePage = _uCodePage; csi.fWrapText = !!_bWrapText; @@ -726,11 +725,6 @@ void Settings::SetHistoryNoDup(const bool bHistoryNoDup) _bHistoryNoDup = bHistoryNoDup; } -void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue) -{ - _colorTable.at(index) = ColorValue; -} - bool Settings::IsStartupTitleIsLinkNameSet() const { return WI_IsFlagSet(_dwStartupFlags, STARTF_TITLEISLINKNAME); @@ -746,11 +740,26 @@ void Settings::UnsetStartupFlag(const DWORD dwFlagToUnset) _dwStartupFlags &= ~dwFlagToUnset; } +void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue) +{ + _colorTable.at(index) = ColorValue; +} + COLORREF Settings::GetColorTableEntry(const size_t index) const { return _colorTable.at(index); } +void Settings::SetLegacyColorTableEntry(const size_t index, const COLORREF ColorValue) +{ + _colorTable.at(TextColor::TransposeLegacyIndex(index)) = ColorValue; +} + +COLORREF Settings::GetLegacyColorTableEntry(const size_t index) const +{ + return _colorTable.at(TextColor::TransposeLegacyIndex(index)); +} + COLORREF Settings::GetCursorColor() const noexcept { return _CursorColor; diff --git a/src/host/settings.hpp b/src/host/settings.hpp index e8468da6b..896de0e20 100644 --- a/src/host/settings.hpp +++ b/src/host/settings.hpp @@ -167,6 +167,8 @@ public: void SetColorTableEntry(const size_t index, const COLORREF ColorValue); COLORREF GetColorTableEntry(const size_t index) const; + void SetLegacyColorTableEntry(const size_t index, const COLORREF ColorValue); + COLORREF GetLegacyColorTableEntry(const size_t index) const; COLORREF GetCursorColor() const noexcept; CursorType GetCursorType() const noexcept; diff --git a/src/host/sources.inc b/src/host/sources.inc index 3ee3b70c3..700fda72a 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -89,7 +89,6 @@ SOURCES = \ ..\utf8ToWideCharParser.cpp \ ..\conareainfo.cpp \ ..\conimeinfo.cpp \ - ..\conattrs.cpp \ ..\ConsoleArguments.cpp \ ..\CommandNumberPopup.cpp \ ..\CommandListPopup.cpp \ diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 0e31c2b32..83ba0c502 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "precomp.h" @@ -1634,74 +1634,74 @@ void ScreenBufferTests::VtSetColorTable() L"Process some valid sequences for setting the table")); stateMachine.ProcessString(L"\x1b]4;0;rgb:1/1/1\x7"); - VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), gci.GetColorTableEntry(::XtermToWindowsIndex(0))); + VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), gci.GetColorTableEntry(0)); stateMachine.ProcessString(L"\x1b]4;1;rgb:1/23/1\x7"); - VERIFY_ARE_EQUAL(RGB(0x11, 0x23, 0x11), gci.GetColorTableEntry(::XtermToWindowsIndex(1))); + VERIFY_ARE_EQUAL(RGB(0x11, 0x23, 0x11), gci.GetColorTableEntry(1)); stateMachine.ProcessString(L"\x1b]4;2;rgb:1/23/12\x7"); - VERIFY_ARE_EQUAL(RGB(0x11, 0x23, 0x12), gci.GetColorTableEntry(::XtermToWindowsIndex(2))); + VERIFY_ARE_EQUAL(RGB(0x11, 0x23, 0x12), gci.GetColorTableEntry(2)); stateMachine.ProcessString(L"\x1b]4;3;rgb:12/23/12\x7"); - VERIFY_ARE_EQUAL(RGB(0x12, 0x23, 0x12), gci.GetColorTableEntry(::XtermToWindowsIndex(3))); + VERIFY_ARE_EQUAL(RGB(0x12, 0x23, 0x12), gci.GetColorTableEntry(3)); stateMachine.ProcessString(L"\x1b]4;4;rgb:ff/a1/1b\x7"); - VERIFY_ARE_EQUAL(RGB(0xff, 0xa1, 0x1b), gci.GetColorTableEntry(::XtermToWindowsIndex(4))); + VERIFY_ARE_EQUAL(RGB(0xff, 0xa1, 0x1b), gci.GetColorTableEntry(4)); stateMachine.ProcessString(L"\x1b]4;5;rgb:ff/a1/1b\x1b\\"); - VERIFY_ARE_EQUAL(RGB(0xff, 0xa1, 0x1b), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(0xff, 0xa1, 0x1b), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"Try a bunch of invalid sequences.")); Log::Comment(NoThrowString().Format( L"First start by setting an entry to a known value to compare to.")); stateMachine.ProcessString(L"\x1b]4;5;rgb:09/09/09\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: Missing the first component")); stateMachine.ProcessString(L"\x1b]4;5;rgb:/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: too many components")); stateMachine.ProcessString(L"\x1b]4;5;rgb:1/1/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: no second component")); stateMachine.ProcessString(L"\x1b]4;5;rgb:1//1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: no components")); stateMachine.ProcessString(L"\x1b]4;5;rgb://\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: no third component")); stateMachine.ProcessString(L"\x1b]4;5;rgb:1/11/\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: rgbi is not a supported color space")); stateMachine.ProcessString(L"\x1b]4;5;rgbi:1/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: cmyk is not a supported color space")); stateMachine.ProcessString(L"\x1b]4;5;cmyk:1/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: no table index should do nothing")); stateMachine.ProcessString(L"\x1b]4;;rgb:1/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); Log::Comment(NoThrowString().Format( L"invalid: need to specify a color space")); stateMachine.ProcessString(L"\x1b]4;5;1/1/1\x1b\\"); - VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(::XtermToWindowsIndex(5))); + VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5)); } void ScreenBufferTests::ResizeTraditionalDoesNotDoubleFreeAttrRows() @@ -1912,8 +1912,8 @@ void ScreenBufferTests::VtEraseAllPersistCursorFillColor() L"The viewport should be full of dark_red on bright_blue")); auto expectedAttr = TextAttribute{}; - expectedAttr.SetIndexedForeground((BYTE)XtermToWindowsIndex(1)); - expectedAttr.SetIndexedBackground((BYTE)XtermToWindowsIndex(12)); + expectedAttr.SetIndexedForeground(TextColor::DARK_RED); + expectedAttr.SetIndexedBackground(TextColor::BRIGHT_BLUE); stateMachine.ProcessString(L"\x1b[31;104m"); VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes()); @@ -2256,8 +2256,8 @@ void ScreenBufferTests::SetDefaultsIndividuallyBothDefault() COLORREF magenta = RGB(255, 0, 255); COLORREF yellow = RGB(255, 255, 0); - COLORREF brightGreen = gci.GetColorTableEntry(::XtermToWindowsIndex(10)); - COLORREF darkBlue = gci.GetColorTableEntry(::XtermToWindowsIndex(4)); + COLORREF brightGreen = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); + COLORREF darkBlue = gci.GetColorTableEntry(TextColor::DARK_BLUE); gci.SetDefaultForegroundColor(yellow); gci.SetDefaultBackgroundColor(magenta); @@ -2292,8 +2292,8 @@ void ScreenBufferTests::SetDefaultsIndividuallyBothDefault() // See the log comment above for description of these values. TextAttribute expectedDefaults{}; TextAttribute expectedTwo; - expectedTwo.SetIndexedForeground((BYTE)XtermToWindowsIndex(10)); - expectedTwo.SetIndexedBackground((BYTE)XtermToWindowsIndex(4)); + expectedTwo.SetIndexedForeground(TextColor::BRIGHT_GREEN); + expectedTwo.SetIndexedBackground(TextColor::DARK_BLUE); TextAttribute expectedThree = expectedTwo; expectedThree.SetDefaultForeground(); // Four is the same as Defaults @@ -2705,7 +2705,7 @@ void ScreenBufferTests::SetGlobalColorTable() VERIFY_SUCCEEDED(mainBuffer.SetViewportOrigin(true, COORD({ 0, 0 }), true)); mainCursor.SetPosition({ 0, 0 }); - const COLORREF originalRed = gci.GetColorTableEntry(4); + const COLORREF originalRed = gci.GetColorTableEntry(TextColor::DARK_RED); const COLORREF testColor = RGB(0x11, 0x22, 0x33); VERIFY_ARE_NOT_EQUAL(originalRed, testColor); @@ -3209,10 +3209,10 @@ void ScreenBufferTests::DontResetColorsAboveVirtualBottom() L"cursor=%s", VerifyOutputTraits::ToString(cursor.GetPosition()).GetBuffer())); Log::Comment(NoThrowString().Format( L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); - const auto darkRed = gci.GetColorTableEntry(::XtermToWindowsIndex(1)); - const auto darkBlue = gci.GetColorTableEntry(::XtermToWindowsIndex(4)); - const auto darkBlack = gci.GetColorTableEntry(::XtermToWindowsIndex(0)); - const auto darkWhite = gci.GetColorTableEntry(::XtermToWindowsIndex(7)); + const auto darkRed = gci.GetColorTableEntry(TextColor::DARK_RED); + const auto darkBlue = gci.GetColorTableEntry(TextColor::DARK_BLUE); + const auto darkBlack = gci.GetColorTableEntry(TextColor::DARK_BLACK); + const auto darkWhite = gci.GetColorTableEntry(TextColor::DARK_WHITE); stateMachine.ProcessString(L"\x1b[31;44m"); stateMachine.ProcessString(L"X"); stateMachine.ProcessString(L"\x1b[m"); @@ -6168,8 +6168,8 @@ void ScreenBufferTests::TestWriteConsoleVTQuirkMode() /* Write red on blue, verify that it comes through */ { TextAttribute vtRedOnBlueAttribute{}; - vtRedOnBlueAttribute.SetForeground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(1)), false }); - vtRedOnBlueAttribute.SetBackground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(4)), false }); + vtRedOnBlueAttribute.SetForeground(TextColor{ TextColor::DARK_RED, false }); + vtRedOnBlueAttribute.SetBackground(TextColor{ TextColor::DARK_BLUE, false }); seq = L"\x1b[31;44m"; seqCb = 2 * seq.size(); @@ -6187,8 +6187,8 @@ void ScreenBufferTests::TestWriteConsoleVTQuirkMode() /* Write white on black, verify that it acts as expected for the quirk mode */ { TextAttribute vtWhiteOnBlackAttribute{}; - vtWhiteOnBlackAttribute.SetForeground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(7)), false }); - vtWhiteOnBlackAttribute.SetBackground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(0)), false }); + vtWhiteOnBlackAttribute.SetForeground(TextColor{ TextColor::DARK_WHITE, false }); + vtWhiteOnBlackAttribute.SetBackground(TextColor{ TextColor::DARK_BLACK, false }); const TextAttribute quirkExpectedAttribute{ useQuirk ? defaultAttribute : vtWhiteOnBlackAttribute }; @@ -6208,8 +6208,8 @@ void ScreenBufferTests::TestWriteConsoleVTQuirkMode() /* Write bright white on black, verify that it acts as expected for the quirk mode */ { TextAttribute vtBrightWhiteOnBlackAttribute{}; - vtBrightWhiteOnBlackAttribute.SetForeground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(7)), false }); - vtBrightWhiteOnBlackAttribute.SetBackground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(0)), false }); + vtBrightWhiteOnBlackAttribute.SetForeground(TextColor{ TextColor::DARK_WHITE, false }); + vtBrightWhiteOnBlackAttribute.SetBackground(TextColor{ TextColor::DARK_BLACK, false }); vtBrightWhiteOnBlackAttribute.SetBold(true); TextAttribute vtBrightWhiteOnDefaultAttribute{ vtBrightWhiteOnBlackAttribute }; // copy the above attribute @@ -6233,8 +6233,8 @@ void ScreenBufferTests::TestWriteConsoleVTQuirkMode() /* Write a 256-color white on a 256-color black, make sure the quirk does not suppress it */ { TextAttribute vtWhiteOnBlack256Attribute{}; - vtWhiteOnBlack256Attribute.SetForeground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(7)), true }); - vtWhiteOnBlack256Attribute.SetBackground(TextColor{ gsl::narrow_cast(XtermToWindowsIndex(0)), true }); + vtWhiteOnBlack256Attribute.SetForeground(TextColor{ TextColor::DARK_WHITE, true }); + vtWhiteOnBlack256Attribute.SetBackground(TextColor{ TextColor::DARK_BLACK, true }); // reset (disable bold from the last test) before setting both colors seq = L"\x1b[m\x1b[38;5;7;48;5;0m"; // the quirk should *not* suppress this (!) diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index c8dd06793..2872c9421 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "precomp.h" @@ -779,8 +779,8 @@ void TextBufferTests::TestMixedRgbAndLegacyBrightness() // '\E[m\E[32mX\E[1mX' // Make sure that the second X is a BRIGHT green, not white. Log::Comment(L"Case 4 ;\"\\E[m\\E[32mX\\E[1mX\""); - const auto dark_green = gci.GetColorTableEntry(2); - const auto bright_green = gci.GetColorTableEntry(10); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); + const auto bright_green = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); VERIFY_ARE_NOT_EQUAL(dark_green, bright_green); wchar_t* sequence = L"\x1b[m\x1b[32mX\x1b[1mX"; @@ -887,8 +887,8 @@ void TextBufferTests::TestUnBold() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const auto dark_green = gci.GetColorTableEntry(2); - const auto bright_green = gci.GetColorTableEntry(10); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); + const auto bright_green = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); Log::Comment(NoThrowString().Format( L"cursor={X:%d,Y:%d}", @@ -938,8 +938,8 @@ void TextBufferTests::TestUnBoldRgb() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const auto dark_green = gci.GetColorTableEntry(2); - const auto bright_green = gci.GetColorTableEntry(10); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); + const auto bright_green = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); Log::Comment(NoThrowString().Format( L"cursor={X:%d,Y:%d}", @@ -997,8 +997,8 @@ void TextBufferTests::TestComplexUnBold() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const auto dark_green = gci.GetColorTableEntry(2); - const auto bright_green = gci.GetColorTableEntry(10); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); + const auto bright_green = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); Log::Comment(NoThrowString().Format( L"cursor={X:%d,Y:%d}", @@ -1079,8 +1079,8 @@ void TextBufferTests::CopyAttrs() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const auto dark_blue = gci.GetColorTableEntry(1); - const auto dark_magenta = gci.GetColorTableEntry(5); + const auto dark_blue = gci.GetColorTableEntry(TextColor::DARK_BLUE); + const auto dark_magenta = gci.GetColorTableEntry(TextColor::DARK_MAGENTA); Log::Comment(NoThrowString().Format( L"cursor={X:%d,Y:%d}", @@ -1133,7 +1133,7 @@ void TextBufferTests::EmptySgrTest() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const COLORREF darkRed = gci.GetColorTableEntry(4); + const COLORREF darkRed = gci.GetColorTableEntry(TextColor::DARK_RED); Log::Comment(NoThrowString().Format( L"cursor={X:%d,Y:%d}", x, @@ -1192,7 +1192,7 @@ void TextBufferTests::TestReverseReset() const auto x = cursor.GetPosition().X; const auto y = cursor.GetPosition().Y; - const auto dark_green = gci.GetColorTableEntry(2); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); const COLORREF rgbColor = RGB(128, 5, 255); Log::Comment(NoThrowString().Format( @@ -1258,9 +1258,9 @@ void TextBufferTests::CopyLastAttr() const COLORREF defaultBrightYellow = RGB(249, 241, 165); const COLORREF defaultBrightCyan = RGB(97, 214, 214); - gci.SetColorTableEntry(8, defaultBrightBlack); - gci.SetColorTableEntry(14, defaultBrightYellow); - gci.SetColorTableEntry(11, defaultBrightCyan); + gci.SetColorTableEntry(TextColor::BRIGHT_BLACK, defaultBrightBlack); + gci.SetColorTableEntry(TextColor::BRIGHT_YELLOW, defaultBrightYellow); + gci.SetColorTableEntry(TextColor::BRIGHT_CYAN, defaultBrightCyan); // Write (solFg, solBG) X \n // (solFg, solBG) X (solCyan, solBG) X \n @@ -1405,8 +1405,8 @@ void TextBufferTests::TestResetClearsBoldness() si.SetAttributes(defaultAttribute); const auto [defaultFg, defaultBg] = gci.LookupAttributeColors(si.GetAttributes()); - const auto dark_green = gci.GetColorTableEntry(2); - const auto bright_green = gci.GetColorTableEntry(10); + const auto dark_green = gci.GetColorTableEntry(TextColor::DARK_GREEN); + const auto bright_green = gci.GetColorTableEntry(TextColor::BRIGHT_GREEN); wchar_t* sequence = L"\x1b[32mA\x1b[1mB\x1b[0mC\x1b[32mD"; Log::Comment(NoThrowString().Format(sequence)); diff --git a/src/host/ut_host/VtRendererTests.cpp b/src/host/ut_host/VtRendererTests.cpp index a12dd8673..3d9a5b8aa 100644 --- a/src/host/ut_host/VtRendererTests.cpp +++ b/src/host/ut_host/VtRendererTests.cpp @@ -470,7 +470,7 @@ void VtRendererTest::Xterm256TestColors() Log::Comment(NoThrowString().Format( L"----Change only the BG----")); - textAttributes.SetIndexedBackground(FOREGROUND_RED); + textAttributes.SetIndexedBackground(TextColor::DARK_RED); qExpectedInput.push_back("\x1b[41m"); // Background DARK_RED VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -479,7 +479,7 @@ void VtRendererTest::Xterm256TestColors() Log::Comment(NoThrowString().Format( L"----Change only the FG----")); - textAttributes.SetIndexedForeground(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + textAttributes.SetIndexedForeground(TextColor::DARK_WHITE); qExpectedInput.push_back("\x1b[37m"); // Foreground DARK_WHITE VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -515,7 +515,7 @@ void VtRendererTest::Xterm256TestColors() Log::Comment(NoThrowString().Format( L"----Change only the FG to a 256-color index----")); - textAttributes.SetIndexedForeground256(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + textAttributes.SetIndexedForeground256(TextColor::DARK_WHITE); qExpectedInput.push_back("\x1b[38;5;7m"); // Foreground DARK_WHITE (256-Color Index) VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -524,7 +524,7 @@ void VtRendererTest::Xterm256TestColors() Log::Comment(NoThrowString().Format( L"----Change only the BG to a 256-color index----")); - textAttributes.SetIndexedBackground256(FOREGROUND_RED); + textAttributes.SetIndexedBackground256(TextColor::DARK_RED); qExpectedInput.push_back("\x1b[48;5;1m"); // Background DARK_RED (256-Color Index) VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -859,7 +859,7 @@ void VtRendererTest::Xterm256TestAttributesAcrossReset() VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); Log::Comment(L"----Set Green Foreground----"); - textAttributes.SetIndexedForeground(FOREGROUND_GREEN); + textAttributes.SetIndexedForeground(TextColor::DARK_GREEN); qExpectedInput.push_back("\x1b[32m"); VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); @@ -870,7 +870,7 @@ void VtRendererTest::Xterm256TestAttributesAcrossReset() VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); Log::Comment(L"----Set Green Background----"); - textAttributes.SetIndexedBackground(FOREGROUND_GREEN); + textAttributes.SetIndexedBackground(TextColor::DARK_GREEN); qExpectedInput.push_back("\x1b[42m"); VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); @@ -1104,7 +1104,7 @@ void VtRendererTest::XtermTestColors() Log::Comment(NoThrowString().Format( L"----Change only the BG----")); - textAttributes.SetIndexedBackground(FOREGROUND_RED); + textAttributes.SetIndexedBackground(TextColor::DARK_RED); qExpectedInput.push_back("\x1b[41m"); // Background DARK_RED VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -1113,7 +1113,7 @@ void VtRendererTest::XtermTestColors() Log::Comment(NoThrowString().Format( L"----Change only the FG----")); - textAttributes.SetIndexedForeground(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + textAttributes.SetIndexedForeground(TextColor::DARK_WHITE); qExpectedInput.push_back("\x1b[37m"); // Foreground DARK_WHITE VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -1150,7 +1150,7 @@ void VtRendererTest::XtermTestColors() Log::Comment(NoThrowString().Format( L"----Change only the FG to a 256-color index----")); - textAttributes.SetIndexedForeground256(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + textAttributes.SetIndexedForeground256(TextColor::DARK_WHITE); qExpectedInput.push_back("\x1b[37m"); // Foreground DARK_WHITE VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -1159,7 +1159,7 @@ void VtRendererTest::XtermTestColors() Log::Comment(NoThrowString().Format( L"----Change only the BG to a 256-color index----")); - textAttributes.SetIndexedBackground256(FOREGROUND_RED); + textAttributes.SetIndexedBackground256(TextColor::DARK_RED); qExpectedInput.push_back("\x1b[41m"); // Background DARK_RED VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, @@ -1351,7 +1351,7 @@ void VtRendererTest::XtermTestAttributesAcrossReset() VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); Log::Comment(L"----Set Green Foreground----"); - textAttributes.SetIndexedForeground(FOREGROUND_GREEN); + textAttributes.SetIndexedForeground(TextColor::DARK_GREEN); qExpectedInput.push_back("\x1b[32m"); VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); @@ -1362,7 +1362,7 @@ void VtRendererTest::XtermTestAttributesAcrossReset() VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); Log::Comment(L"----Set Green Background----"); - textAttributes.SetIndexedBackground(FOREGROUND_GREEN); + textAttributes.SetIndexedBackground(TextColor::DARK_GREEN); qExpectedInput.push_back("\x1b[42m"); VERIFY_SUCCEEDED(engine->UpdateDrawingBrushes(textAttributes, &renderData, false, false)); diff --git a/src/inc/conattrs.hpp b/src/inc/conattrs.hpp index cb9110064..8589590d4 100644 --- a/src/inc/conattrs.hpp +++ b/src/inc/conattrs.hpp @@ -22,20 +22,6 @@ enum class ExtendedAttributes : BYTE }; DEFINE_ENUM_FLAG_OPERATORS(ExtendedAttributes); -WORD XtermToWindowsIndex(const size_t index) noexcept; -WORD Xterm256ToWindowsIndex(const size_t index) noexcept; -WORD XtermToLegacy(const size_t xtermForeground, const size_t xtermBackground); - -constexpr WORD WINDOWS_RED_ATTR = FOREGROUND_RED; -constexpr WORD WINDOWS_GREEN_ATTR = FOREGROUND_GREEN; -constexpr WORD WINDOWS_BLUE_ATTR = FOREGROUND_BLUE; -constexpr WORD WINDOWS_BRIGHT_ATTR = FOREGROUND_INTENSITY; - -constexpr WORD XTERM_RED_ATTR = 0x01; -constexpr WORD XTERM_GREEN_ATTR = 0x02; -constexpr WORD XTERM_BLUE_ATTR = 0x04; -constexpr WORD XTERM_BRIGHT_ATTR = 0x08; - enum class CursorType : unsigned int { Legacy = 0x0, // uses the cursor's height value to range from underscore-like to full box diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index b12161ccf..8e05e7827 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -208,7 +208,11 @@ void Clipboard::StoreSelectionToClipboard(bool const copyFormatting) const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); const auto& buffer = gci.GetActiveOutputBuffer().GetTextBuffer(); - const auto GetAttributeColors = std::bind(&CONSOLE_INFORMATION::LookupAttributeColors, &gci, std::placeholders::_1); + const auto defaultForeground = gci.GetDefaultForeground(); + const auto defaultBackground = gci.GetDefaultBackground(); + const auto GetAttributeColors = [=, &gci](const auto& attr) { + return gci.LookupAttributeColors(attr, defaultForeground, defaultBackground); + }; bool includeCRLF, trimTrailingWhitespace; if (WI_IsFlagSet(GetKeyState(VK_SHIFT), KEY_PRESSED)) diff --git a/src/interactivity/win32/menu.cpp b/src/interactivity/win32/menu.cpp index 3229f5663..9364e6364 100644 --- a/src/interactivity/win32/menu.cpp +++ b/src/interactivity/win32/menu.cpp @@ -337,7 +337,7 @@ void Menu::s_ShowPropertiesDialog(HWND const hwnd, BOOL const Defaults) for (size_t i = 0; i < std::size(pStateInfo->ColorTable); i++) { - pStateInfo->ColorTable[i] = gci.GetColorTableEntry(i); + pStateInfo->ColorTable[i] = gci.GetLegacyColorTableEntry(i); } // Create mutable copies of the titles so the propsheet can do something with them. @@ -566,7 +566,7 @@ void Menu::s_PropertiesUpdate(PCONSOLE_STATE_INFO pStateInfo) for (size_t i = 0; i < std::size(pStateInfo->ColorTable); i++) { - gci.SetColorTableEntry(i, pStateInfo->ColorTable[i]); + gci.SetLegacyColorTableEntry(i, pStateInfo->ColorTable[i]); } // Ensure that attributes only contain color specification. diff --git a/src/renderer/vt/VtSequences.cpp b/src/renderer/vt/VtSequences.cpp index efd16e2cb..c11e8cc4d 100644 --- a/src/renderer/vt/VtSequences.cpp +++ b/src/renderer/vt/VtSequences.cpp @@ -199,13 +199,14 @@ using namespace Microsoft::Console::Render; } // Method Description: -// - Formats and writes a sequence to change the current text attributes. +// - Formats and writes a sequence to change the current text attributes to an +// indexed color from the 16-color table. // Arguments: -// - wAttr: Windows color table index to emit as a VT sequence +// - index: color table index to emit as a VT sequence // - fIsForeground: true if we should emit the foreground sequence, false for background // Return Value: // - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. -[[nodiscard]] HRESULT VtEngine::_SetGraphicsRendition16Color(const WORD wAttr, +[[nodiscard]] HRESULT VtEngine::_SetGraphicsRendition16Color(const BYTE index, const bool fIsForeground) noexcept { // Always check using the foreground flags, because the bg flags constants @@ -220,28 +221,22 @@ using namespace Microsoft::Console::Render; // terminals display the bright color when displaying bolded text. // By specifying the boldness and brightness separately, we'll make sure the // terminal has an accurate representation of our buffer. - const int vtIndex = 30 + - (fIsForeground ? 0 : 10) + - ((WI_IsFlagSet(wAttr, FOREGROUND_INTENSITY)) ? 60 : 0) + - (WI_IsFlagSet(wAttr, FOREGROUND_RED) ? 1 : 0) + - (WI_IsFlagSet(wAttr, FOREGROUND_GREEN) ? 2 : 0) + - (WI_IsFlagSet(wAttr, FOREGROUND_BLUE) ? 4 : 0); - - return _WriteFormatted(FMT_COMPILE("\x1b[{}m"), vtIndex); + const auto prefix = WI_IsFlagSet(index, FOREGROUND_INTENSITY) ? (fIsForeground ? 90 : 100) : (fIsForeground ? 30 : 40); + return _WriteFormatted(FMT_COMPILE("\x1b[{}m"), prefix + (index & 7)); } // Method Description: // - Formats and writes a sequence to change the current text attributes to an // indexed color from the 256-color table. // Arguments: -// - wAttr: Windows color table index to emit as a VT sequence +// - index: color table index to emit as a VT sequence // - fIsForeground: true if we should emit the foreground sequence, false for background // Return Value: // - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write. -[[nodiscard]] HRESULT VtEngine::_SetGraphicsRendition256Color(const WORD index, +[[nodiscard]] HRESULT VtEngine::_SetGraphicsRendition256Color(const BYTE index, const bool fIsForeground) noexcept { - return _WriteFormatted(FMT_COMPILE("\x1b[{}8;5;{}m"), fIsForeground ? '3' : '4', ::Xterm256ToWindowsIndex(index)); + return _WriteFormatted(FMT_COMPILE("\x1b[{}8;5;{}m"), fIsForeground ? '3' : '4', index); } // Method Description: diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 1b9efb35e..adb0c90d1 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -287,9 +287,10 @@ using namespace Microsoft::Console::Types; } // We use the legacy color calculations to generate an approximation of the - // colors in the 16-color table. - auto fgIndex = fg.GetLegacyIndex(0); - auto bgIndex = bg.GetLegacyIndex(0); + // colors in the Windows 16-color table, but we need to transpose those + // values to obtain an index in an ANSI-compatible order. + auto fgIndex = TextColor::TransposeLegacyIndex(fg.GetLegacyIndex(0)); + auto bgIndex = TextColor::TransposeLegacyIndex(bg.GetLegacyIndex(0)); // If the bold attribute is set, and the foreground can be brightened, then do so. const bool brighten = textAttributes.IsBold() && fg.CanBeBrightened(); diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 1965034db..f78adf647 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -185,9 +185,9 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _ClearScreen() noexcept; [[nodiscard]] HRESULT _ClearScrollback() noexcept; [[nodiscard]] HRESULT _ChangeTitle(const std::string& title) noexcept; - [[nodiscard]] HRESULT _SetGraphicsRendition16Color(const WORD wAttr, + [[nodiscard]] HRESULT _SetGraphicsRendition16Color(const BYTE index, const bool fIsForeground) noexcept; - [[nodiscard]] HRESULT _SetGraphicsRendition256Color(const WORD index, + [[nodiscard]] HRESULT _SetGraphicsRendition256Color(const BYTE index, const bool fIsForeground) noexcept; [[nodiscard]] HRESULT _SetGraphicsRenditionRGBColor(const COLORREF color, const bool fIsForeground) noexcept; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 49a3f71a9..8ffe4c202 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2541,13 +2541,13 @@ void AdaptDispatch::_ReportSGRSetting() const const auto iterator = std::back_insert_iterator(response); if (color.IsIndex16()) { - const auto index = XtermToWindowsIndex(color.GetIndex()); + const auto index = color.GetIndex(); const auto colorParameter = base + (index >= 8 ? 60 : 0) + (index % 8); fmt::format_to(iterator, FMT_STRING(L";{}"), colorParameter); } else if (color.IsIndex256()) { - const auto index = Xterm256ToWindowsIndex(color.GetIndex()); + const auto index = color.GetIndex(); fmt::format_to(iterator, FMT_STRING(L";{};5;{}"), base + 8, index); } else if (color.IsRgb()) diff --git a/src/terminal/adapter/adaptDispatchGraphics.cpp b/src/terminal/adapter/adaptDispatchGraphics.cpp index c258c2c44..6d586cce6 100644 --- a/src/terminal/adapter/adaptDispatchGraphics.cpp +++ b/src/terminal/adapter/adaptDispatchGraphics.cpp @@ -13,29 +13,6 @@ using namespace Microsoft::Console::VirtualTerminal; using namespace Microsoft::Console::VirtualTerminal::DispatchTypes; -// clang-format off -constexpr BYTE BLUE_ATTR = 0x01; -constexpr BYTE GREEN_ATTR = 0x02; -constexpr BYTE RED_ATTR = 0x04; -constexpr BYTE BRIGHT_ATTR = 0x08; -constexpr BYTE DARK_BLACK = 0; -constexpr BYTE DARK_RED = RED_ATTR; -constexpr BYTE DARK_GREEN = GREEN_ATTR; -constexpr BYTE DARK_YELLOW = RED_ATTR | GREEN_ATTR; -constexpr BYTE DARK_BLUE = BLUE_ATTR; -constexpr BYTE DARK_MAGENTA = RED_ATTR | BLUE_ATTR; -constexpr BYTE DARK_CYAN = GREEN_ATTR | BLUE_ATTR; -constexpr BYTE DARK_WHITE = RED_ATTR | GREEN_ATTR | BLUE_ATTR; -constexpr BYTE BRIGHT_BLACK = BRIGHT_ATTR; -constexpr BYTE BRIGHT_RED = BRIGHT_ATTR | RED_ATTR; -constexpr BYTE BRIGHT_GREEN = BRIGHT_ATTR | GREEN_ATTR; -constexpr BYTE BRIGHT_YELLOW = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR; -constexpr BYTE BRIGHT_BLUE = BRIGHT_ATTR | BLUE_ATTR; -constexpr BYTE BRIGHT_MAGENTA = BRIGHT_ATTR | RED_ATTR | BLUE_ATTR; -constexpr BYTE BRIGHT_CYAN = BRIGHT_ATTR | GREEN_ATTR | BLUE_ATTR; -constexpr BYTE BRIGHT_WHITE = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR | BLUE_ATTR; -// clang-format on - // Routine Description: // - Helper to parse extended graphics options, which start with 38 (FG) or 48 (BG) // These options are followed by either a 2 (RGB) or 5 (xterm index) @@ -72,7 +49,7 @@ size_t AdaptDispatch::_SetRgbColorsHelper(const VTParameters options, const size_t tableIndex = options.at(1).value_or(0); if (tableIndex <= 255) { - const auto adjustedIndex = gsl::narrow_cast(::Xterm256ToWindowsIndex(tableIndex)); + const auto adjustedIndex = gsl::narrow_cast(tableIndex); if (isForeground) { attr.SetIndexedForeground256(adjustedIndex); @@ -178,100 +155,100 @@ bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) attr.SetOverlined(false); break; case ForegroundBlack: - attr.SetIndexedForeground(DARK_BLACK); + attr.SetIndexedForeground(TextColor::DARK_BLACK); break; case ForegroundBlue: - attr.SetIndexedForeground(DARK_BLUE); + attr.SetIndexedForeground(TextColor::DARK_BLUE); break; case ForegroundGreen: - attr.SetIndexedForeground(DARK_GREEN); + attr.SetIndexedForeground(TextColor::DARK_GREEN); break; case ForegroundCyan: - attr.SetIndexedForeground(DARK_CYAN); + attr.SetIndexedForeground(TextColor::DARK_CYAN); break; case ForegroundRed: - attr.SetIndexedForeground(DARK_RED); + attr.SetIndexedForeground(TextColor::DARK_RED); break; case ForegroundMagenta: - attr.SetIndexedForeground(DARK_MAGENTA); + attr.SetIndexedForeground(TextColor::DARK_MAGENTA); break; case ForegroundYellow: - attr.SetIndexedForeground(DARK_YELLOW); + attr.SetIndexedForeground(TextColor::DARK_YELLOW); break; case ForegroundWhite: - attr.SetIndexedForeground(DARK_WHITE); + attr.SetIndexedForeground(TextColor::DARK_WHITE); break; case BackgroundBlack: - attr.SetIndexedBackground(DARK_BLACK); + attr.SetIndexedBackground(TextColor::DARK_BLACK); break; case BackgroundBlue: - attr.SetIndexedBackground(DARK_BLUE); + attr.SetIndexedBackground(TextColor::DARK_BLUE); break; case BackgroundGreen: - attr.SetIndexedBackground(DARK_GREEN); + attr.SetIndexedBackground(TextColor::DARK_GREEN); break; case BackgroundCyan: - attr.SetIndexedBackground(DARK_CYAN); + attr.SetIndexedBackground(TextColor::DARK_CYAN); break; case BackgroundRed: - attr.SetIndexedBackground(DARK_RED); + attr.SetIndexedBackground(TextColor::DARK_RED); break; case BackgroundMagenta: - attr.SetIndexedBackground(DARK_MAGENTA); + attr.SetIndexedBackground(TextColor::DARK_MAGENTA); break; case BackgroundYellow: - attr.SetIndexedBackground(DARK_YELLOW); + attr.SetIndexedBackground(TextColor::DARK_YELLOW); break; case BackgroundWhite: - attr.SetIndexedBackground(DARK_WHITE); + attr.SetIndexedBackground(TextColor::DARK_WHITE); break; case BrightForegroundBlack: - attr.SetIndexedForeground(BRIGHT_BLACK); + attr.SetIndexedForeground(TextColor::BRIGHT_BLACK); break; case BrightForegroundBlue: - attr.SetIndexedForeground(BRIGHT_BLUE); + attr.SetIndexedForeground(TextColor::BRIGHT_BLUE); break; case BrightForegroundGreen: - attr.SetIndexedForeground(BRIGHT_GREEN); + attr.SetIndexedForeground(TextColor::BRIGHT_GREEN); break; case BrightForegroundCyan: - attr.SetIndexedForeground(BRIGHT_CYAN); + attr.SetIndexedForeground(TextColor::BRIGHT_CYAN); break; case BrightForegroundRed: - attr.SetIndexedForeground(BRIGHT_RED); + attr.SetIndexedForeground(TextColor::BRIGHT_RED); break; case BrightForegroundMagenta: - attr.SetIndexedForeground(BRIGHT_MAGENTA); + attr.SetIndexedForeground(TextColor::BRIGHT_MAGENTA); break; case BrightForegroundYellow: - attr.SetIndexedForeground(BRIGHT_YELLOW); + attr.SetIndexedForeground(TextColor::BRIGHT_YELLOW); break; case BrightForegroundWhite: - attr.SetIndexedForeground(BRIGHT_WHITE); + attr.SetIndexedForeground(TextColor::BRIGHT_WHITE); break; case BrightBackgroundBlack: - attr.SetIndexedBackground(BRIGHT_BLACK); + attr.SetIndexedBackground(TextColor::BRIGHT_BLACK); break; case BrightBackgroundBlue: - attr.SetIndexedBackground(BRIGHT_BLUE); + attr.SetIndexedBackground(TextColor::BRIGHT_BLUE); break; case BrightBackgroundGreen: - attr.SetIndexedBackground(BRIGHT_GREEN); + attr.SetIndexedBackground(TextColor::BRIGHT_GREEN); break; case BrightBackgroundCyan: - attr.SetIndexedBackground(BRIGHT_CYAN); + attr.SetIndexedBackground(TextColor::BRIGHT_CYAN); break; case BrightBackgroundRed: - attr.SetIndexedBackground(BRIGHT_RED); + attr.SetIndexedBackground(TextColor::BRIGHT_RED); break; case BrightBackgroundMagenta: - attr.SetIndexedBackground(BRIGHT_MAGENTA); + attr.SetIndexedBackground(TextColor::BRIGHT_MAGENTA); break; case BrightBackgroundYellow: - attr.SetIndexedBackground(BRIGHT_YELLOW); + attr.SetIndexedBackground(TextColor::BRIGHT_YELLOW); break; case BrightBackgroundWhite: - attr.SetIndexedBackground(BRIGHT_WHITE); + attr.SetIndexedBackground(TextColor::BRIGHT_WHITE); break; case ForegroundExtended: i += _SetRgbColorsHelper(options.subspan(i + 1), attr, true); diff --git a/src/terminal/adapter/terminalOutput.cpp b/src/terminal/adapter/terminalOutput.cpp index 944ff88b7..ac3c5e6d1 100644 --- a/src/terminal/adapter/terminalOutput.cpp +++ b/src/terminal/adapter/terminalOutput.cpp @@ -10,10 +10,16 @@ using namespace Microsoft::Console::VirtualTerminal; TerminalOutput::TerminalOutput() noexcept { + // By default we set all of the G-sets to ASCII, so if someone accidentally + // triggers a locking shift, they won't end up with Latin1 in the GL table, + // making their system unreadable. If ISO-2022 encoding is selected, though, + // we'll reset the G2 and G3 tables to Latin1, so that 8-bit apps will get a + // more meaningful character mapping by default. This is triggered by a DOCS + // sequence, which will call the EnableGrTranslation method below. _gsetTranslationTables.at(0) = Ascii; _gsetTranslationTables.at(1) = Ascii; - _gsetTranslationTables.at(2) = Latin1; - _gsetTranslationTables.at(3) = Latin1; + _gsetTranslationTables.at(2) = Ascii; + _gsetTranslationTables.at(3) = Ascii; } bool TerminalOutput::Designate94Charset(size_t gsetNumber, const VTID charset) @@ -91,7 +97,13 @@ bool TerminalOutput::NeedToTranslate() const noexcept void TerminalOutput::EnableGrTranslation(boolean enabled) { _grTranslationEnabled = enabled; - // We need to reapply the right locking shift to (de)activate the translation table. + // The default table for G2 and G3 is Latin1 when GR translation is enabled, + // and ASCII when disabled. The reason for this is explained in the constructor. + const auto defaultTranslationTable = enabled ? std::wstring_view{ Latin1 } : std::wstring_view{ Ascii }; + _gsetTranslationTables.at(2) = defaultTranslationTable; + _gsetTranslationTables.at(3) = defaultTranslationTable; + // We need to reapply the locking shifts in case the underlying G-sets have changed. + LockingShift(_glSetNumber); LockingShiftRight(_grSetNumber); } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index b775f4c7a..d494b2deb 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -1307,49 +1307,49 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Black'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(0); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLACK); break; case DispatchTypes::GraphicsOptions::ForegroundBlue: Log::Comment(L"Testing graphics 'Foreground Color Blue'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); break; case DispatchTypes::GraphicsOptions::ForegroundGreen: Log::Comment(L"Testing graphics 'Foreground Color Green'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); break; case DispatchTypes::GraphicsOptions::ForegroundCyan: Log::Comment(L"Testing graphics 'Foreground Color Cyan'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE | FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_CYAN); break; case DispatchTypes::GraphicsOptions::ForegroundRed: Log::Comment(L"Testing graphics 'Foreground Color Red'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); break; case DispatchTypes::GraphicsOptions::ForegroundMagenta: Log::Comment(L"Testing graphics 'Foreground Color Magenta'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_GREEN | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_MAGENTA); break; case DispatchTypes::GraphicsOptions::ForegroundYellow: Log::Comment(L"Testing graphics 'Foreground Color Yellow'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_YELLOW); break; case DispatchTypes::GraphicsOptions::ForegroundWhite: Log::Comment(L"Testing graphics 'Foreground Color White'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_WHITE); break; case DispatchTypes::GraphicsOptions::ForegroundDefault: Log::Comment(L"Testing graphics 'Foreground Color Default'"); @@ -1362,49 +1362,49 @@ public: Log::Comment(L"Testing graphics 'Background Color Black'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground(0); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLACK); break; case DispatchTypes::GraphicsOptions::BackgroundBlue: Log::Comment(L"Testing graphics 'Background Color Blue'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_BLUE >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLUE); break; case DispatchTypes::GraphicsOptions::BackgroundGreen: Log::Comment(L"Testing graphics 'Background Color Green'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_GREEN >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_GREEN); break; case DispatchTypes::GraphicsOptions::BackgroundCyan: Log::Comment(L"Testing graphics 'Background Color Cyan'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_BLUE | BACKGROUND_GREEN) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_CYAN); break; case DispatchTypes::GraphicsOptions::BackgroundRed: Log::Comment(L"Testing graphics 'Background Color Red'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_RED >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_RED); break; case DispatchTypes::GraphicsOptions::BackgroundMagenta: Log::Comment(L"Testing graphics 'Background Color Magenta'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_GREEN | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_BLUE | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_MAGENTA); break; case DispatchTypes::GraphicsOptions::BackgroundYellow: Log::Comment(L"Testing graphics 'Background Color Yellow'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_GREEN | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_YELLOW); break; case DispatchTypes::GraphicsOptions::BackgroundWhite: Log::Comment(L"Testing graphics 'Background Color White'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_INTENSITY }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_WHITE); break; case DispatchTypes::GraphicsOptions::BackgroundDefault: Log::Comment(L"Testing graphics 'Background Color Default'"); @@ -1417,97 +1417,97 @@ public: Log::Comment(L"Testing graphics 'Bright Foreground Color Black'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLACK); break; case DispatchTypes::GraphicsOptions::BrightForegroundBlue: Log::Comment(L"Testing graphics 'Bright Foreground Color Blue'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); break; case DispatchTypes::GraphicsOptions::BrightForegroundGreen: Log::Comment(L"Testing graphics 'Bright Foreground Color Green'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_GREEN); break; case DispatchTypes::GraphicsOptions::BrightForegroundCyan: Log::Comment(L"Testing graphics 'Bright Foreground Color Cyan'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_CYAN); break; case DispatchTypes::GraphicsOptions::BrightForegroundRed: Log::Comment(L"Testing graphics 'Bright Foreground Color Red'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_RED); break; case DispatchTypes::GraphicsOptions::BrightForegroundMagenta: Log::Comment(L"Testing graphics 'Bright Foreground Color Magenta'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_MAGENTA); break; case DispatchTypes::GraphicsOptions::BrightForegroundYellow: Log::Comment(L"Testing graphics 'Bright Foreground Color Yellow'"); _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_YELLOW); break; case DispatchTypes::GraphicsOptions::BrightForegroundWhite: Log::Comment(L"Testing graphics 'Bright Foreground Color White'"); _testGetSet->_attribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_WHITE); break; case DispatchTypes::GraphicsOptions::BrightBackgroundBlack: Log::Comment(L"Testing graphics 'Bright Background Color Black'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_INTENSITY >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_BLACK); break; case DispatchTypes::GraphicsOptions::BrightBackgroundBlue: Log::Comment(L"Testing graphics 'Bright Background Color Blue'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_BLUE) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_BLUE); break; case DispatchTypes::GraphicsOptions::BrightBackgroundGreen: Log::Comment(L"Testing graphics 'Bright Background Color Green'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_GREEN) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_GREEN); break; case DispatchTypes::GraphicsOptions::BrightBackgroundCyan: Log::Comment(L"Testing graphics 'Bright Background Color Cyan'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_BLUE | BACKGROUND_GREEN) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_CYAN); break; case DispatchTypes::GraphicsOptions::BrightBackgroundRed: Log::Comment(L"Testing graphics 'Bright Background Color Red'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_RED); break; case DispatchTypes::GraphicsOptions::BrightBackgroundMagenta: Log::Comment(L"Testing graphics 'Bright Background Color Magenta'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_GREEN }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_BLUE | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_MAGENTA); break; case DispatchTypes::GraphicsOptions::BrightBackgroundYellow: Log::Comment(L"Testing graphics 'Bright Background Color Yellow'"); _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_YELLOW); break; case DispatchTypes::GraphicsOptions::BrightBackgroundWhite: Log::Comment(L"Testing graphics 'Bright Background Color White'"); _testGetSet->_attribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = _testGetSet->_attribute; - _testGetSet->_expectedAttribute.SetIndexedBackground((BACKGROUND_INTENSITY | BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED) >> 4); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_WHITE); break; default: VERIFY_FAIL(L"Test not implemented yet!"); @@ -1545,7 +1545,7 @@ public: cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundCyan; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(3); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_CYAN); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); @@ -1561,7 +1561,7 @@ public: cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundRed; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); @@ -1572,14 +1572,14 @@ public: cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); // First pop: cOptions = 0; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->PopGraphicsRendition()); @@ -1593,22 +1593,22 @@ public: cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::BoldBright; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetBold(true); _testGetSet->_expectedAttribute.SetDefaultBackground(); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundBlue; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_BLUE >> 4); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLUE); _testGetSet->_expectedAttribute.SetBold(true); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); @@ -1624,8 +1624,8 @@ public: rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundGreen; rgOptions[1] = DispatchTypes::GraphicsOptions::DoublyUnderlined; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_GREEN >> 4); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetBold(true); _testGetSet->_expectedAttribute.SetDoublyUnderlined(true); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); @@ -1633,24 +1633,24 @@ public: cOptions = 1; rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundRed; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_GREEN >> 4); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetBold(true); _testGetSet->_expectedAttribute.SetDoublyUnderlined(true); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); rgOptions[0] = DispatchTypes::GraphicsOptions::NotBoldOrFaint; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_GREEN >> 4); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_GREEN); _testGetSet->_expectedAttribute.SetDoublyUnderlined(true); VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); // And then restore... cOptions = 0; _testGetSet->_expectedAttribute = {}; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_RED); - _testGetSet->_expectedAttribute.SetIndexedBackground(BACKGROUND_BLUE >> 4); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); + _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLUE); _testGetSet->_expectedAttribute.SetBold(true); VERIFY_IS_TRUE(_pDispatch->PopGraphicsRendition()); } @@ -1672,7 +1672,7 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Enabling brightness"); @@ -1683,7 +1683,7 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Green, with brightness'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_TRUE(WI_IsFlagSet(_testGetSet->_attribute.GetLegacyAttributes(), FOREGROUND_GREEN)); VERIFY_IS_TRUE(_testGetSet->_attribute.IsBold()); @@ -1698,13 +1698,13 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Bright Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::BrightForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE | FOREGROUND_INTENSITY); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_FALSE(_testGetSet->_attribute.IsBold()); Log::Comment(L"Testing graphics 'Foreground Color Blue', brightness of 9x series doesn't persist"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_FALSE(_testGetSet->_attribute.IsBold()); @@ -1717,7 +1717,7 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_FALSE(_testGetSet->_attribute.IsBold()); @@ -1729,19 +1729,19 @@ public: Log::Comment(L"Testing graphics 'Foreground Color Bright Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::BrightForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE | FOREGROUND_INTENSITY); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_TRUE(_testGetSet->_attribute.IsBold()); Log::Comment(L"Testing graphics 'Foreground Color Blue, with brightness', brightness of 9x series doesn't affect brightness"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_BLUE); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_TRUE(_testGetSet->_attribute.IsBold()); Log::Comment(L"Testing graphics 'Foreground Color Green, with brightness'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; - _testGetSet->_expectedAttribute.SetIndexedForeground(FOREGROUND_GREEN); + _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); VERIFY_IS_TRUE(_testGetSet->_attribute.IsBold()); } @@ -1971,16 +1971,16 @@ public: Log::Comment(L"Requesting SGR attributes (standard colors)."); _testGetSet->PrepData(); _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIndexedForeground((BYTE)::XtermToWindowsIndex(3)); - _testGetSet->_attribute.SetIndexedBackground((BYTE)::XtermToWindowsIndex(6)); + _testGetSet->_attribute.SetIndexedForeground(TextColor::DARK_YELLOW); + _testGetSet->_attribute.SetIndexedBackground(TextColor::DARK_CYAN); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;33;46m\033\\"); Log::Comment(L"Requesting SGR attributes (AIX colors)."); _testGetSet->PrepData(); _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIndexedForeground((BYTE)::XtermToWindowsIndex(14)); - _testGetSet->_attribute.SetIndexedBackground((BYTE)::XtermToWindowsIndex(11)); + _testGetSet->_attribute.SetIndexedForeground(TextColor::BRIGHT_CYAN); + _testGetSet->_attribute.SetIndexedBackground(TextColor::BRIGHT_YELLOW); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;96;103m\033\\"); @@ -2297,14 +2297,14 @@ public: rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)2; // Green - _testGetSet->_expectedAttribute.SetIndexedForeground256((BYTE)::XtermToWindowsIndex(2)); + _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::DARK_GREEN); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 2: Change Background"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)9; // Bright Red - _testGetSet->_expectedAttribute.SetIndexedBackground256((BYTE)::XtermToWindowsIndex(9)); + _testGetSet->_expectedAttribute.SetIndexedBackground256(TextColor::BRIGHT_RED); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 3: Change Foreground to RGB color"); @@ -2328,7 +2328,7 @@ public: rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)9; // Bright Red - _testGetSet->_expectedAttribute.SetIndexedForeground256((BYTE)::XtermToWindowsIndex(9)); + _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::BRIGHT_RED); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); } @@ -2346,14 +2346,14 @@ public: Log::Comment(L"Test 1: Change Indexed Foreground with missing index parameter"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; - _testGetSet->_expectedAttribute.SetIndexedForeground256(0); + _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::DARK_BLACK); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 2 })); Log::Comment(L"Test 2: Change Indexed Background with default index parameter"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = {}; - _testGetSet->_expectedAttribute.SetIndexedBackground256(0); + _testGetSet->_expectedAttribute.SetIndexedBackground256(TextColor::DARK_BLACK); VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 3 })); Log::Comment(L"Test 3: Change RGB Foreground with all RGB parameters missing"); diff --git a/src/types/colorTable.cpp b/src/types/colorTable.cpp index 630674549..0fa05ae3c 100644 --- a/src/types/colorTable.cpp +++ b/src/types/colorTable.cpp @@ -8,7 +8,7 @@ using namespace Microsoft::Console; using namespace std::string_view_literals; -static constexpr std::array campbellColorTable{ +static constexpr std::array standard256ColorTable{ til::color{ 0x0C, 0x0C, 0x0C }, til::color{ 0xC5, 0x0F, 0x1F }, til::color{ 0x13, 0xA1, 0x0E }, @@ -25,25 +25,6 @@ static constexpr std::array campbellColorTable{ til::color{ 0xB4, 0x00, 0x9E }, til::color{ 0x61, 0xD6, 0xD6 }, til::color{ 0xF2, 0xF2, 0xF2 }, -}; - -static constexpr std::array standardXterm256ColorTable{ - til::color{ 0x00, 0x00, 0x00 }, - til::color{ 0x80, 0x00, 0x00 }, - til::color{ 0x00, 0x80, 0x00 }, - til::color{ 0x80, 0x80, 0x00 }, - til::color{ 0x00, 0x00, 0x80 }, - til::color{ 0x80, 0x00, 0x80 }, - til::color{ 0x00, 0x80, 0x80 }, - til::color{ 0xC0, 0xC0, 0xC0 }, - til::color{ 0x80, 0x80, 0x80 }, - til::color{ 0xFF, 0x00, 0x00 }, - til::color{ 0x00, 0xFF, 0x00 }, - til::color{ 0xFF, 0xFF, 0x00 }, - til::color{ 0x00, 0x00, 0xFF }, - til::color{ 0xFF, 0x00, 0xFF }, - til::color{ 0x00, 0xFF, 0xFF }, - til::color{ 0xFF, 0xFF, 0xFF }, til::color{ 0x00, 0x00, 0x00 }, til::color{ 0x00, 0x00, 0x5F }, til::color{ 0x00, 0x00, 0x87 }, @@ -454,73 +435,21 @@ static constexpr til::presorted_static_map xorgAppColorTable{ std::pair{ "yellowgreen"sv, til::color{ 154, 205, 50 } } }; -// Function Description: -// - Fill the first 16 entries of a given color table with the Campbell color -// scheme, in the ANSI/VT RGB order. -// Arguments: -// - table: a color table with at least 16 entries -// Return Value: -// - , throws if the table has less that 16 entries -void Utils::InitializeCampbellColorTable(const gsl::span table) -{ - THROW_HR_IF(E_INVALIDARG, table.size() < 16); - - std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin()); -} - -void Utils::InitializeCampbellColorTable(const gsl::span table) -{ - THROW_HR_IF(E_INVALIDARG, table.size() < 16); - - std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin()); -} - gsl::span Utils::CampbellColorTable() { - return gsl::make_span(campbellColorTable); + return gsl::make_span(standard256ColorTable).first(16); } // Function Description: -// - Fill the first 16 entries of a given color table with the Campbell color -// scheme, in the Windows BGR order. +// - Fill up to 256 entries of a given color table with the default values // Arguments: -// - table: a color table with at least 16 entries +// - table: a color table to be filled // Return Value: -// - , throws if the table has less that 16 entries -void Utils::InitializeCampbellColorTableForConhost(const gsl::span table) +// - +void Utils::InitializeColorTable(const gsl::span table) { - THROW_HR_IF(E_INVALIDARG, table.size() < 16); - InitializeCampbellColorTable(table); - SwapANSIColorOrderForConhost(table); -} - -// Function Description: -// - modifies in-place the given color table from ANSI (RGB) order to Console order (BRG). -// Arguments: -// - table: a color table with at least 16 entries -// Return Value: -// - , throws if the table has less that 16 entries -void Utils::SwapANSIColorOrderForConhost(const gsl::span table) -{ - THROW_HR_IF(E_INVALIDARG, table.size() < 16); - std::swap(til::at(table, 1), til::at(table, 4)); - std::swap(til::at(table, 3), til::at(table, 6)); - std::swap(til::at(table, 9), til::at(table, 12)); - std::swap(til::at(table, 11), til::at(table, 14)); -} - -// Function Description: -// - Fill the first 255 entries of a given color table with the default values -// of a full 256-color table -// Arguments: -// - table: a color table with at least 256 entries -// Return Value: -// - , throws if the table has less that 256 entries -void Utils::Initialize256ColorTable(const gsl::span table) -{ - THROW_HR_IF(E_INVALIDARG, table.size() < 256); - - std::copy(standardXterm256ColorTable.begin(), standardXterm256ColorTable.end(), table.begin()); + const auto tableSize = std::min(table.size(), standard256ColorTable.size()); + std::copy_n(standard256ColorTable.begin(), tableSize, table.begin()); } #pragma warning(push) diff --git a/src/types/inc/colorTable.hpp b/src/types/inc/colorTable.hpp index 9754fc362..9999c3768 100644 --- a/src/types/inc/colorTable.hpp +++ b/src/types/inc/colorTable.hpp @@ -12,11 +12,7 @@ Abstract: namespace Microsoft::Console::Utils { - void InitializeCampbellColorTable(const gsl::span table); - void InitializeCampbellColorTable(const gsl::span table); - void InitializeCampbellColorTableForConhost(const gsl::span table); - void SwapANSIColorOrderForConhost(const gsl::span table); - void Initialize256ColorTable(const gsl::span table); + void InitializeColorTable(const gsl::span table); gsl::span CampbellColorTable(); std::optional ColorFromXOrgAppColorName(const std::wstring_view wstr) noexcept; diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 88b8926f6..ac8b5e974 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -20,7 +20,6 @@ class UtilsTests TEST_CLASS(UtilsTests); TEST_METHOD(TestClampToShortMax); - TEST_METHOD(TestSwapColorPalette); TEST_METHOD(TestGuidToString); TEST_METHOD(TestSplitString); TEST_METHOD(TestFilterStringForPaste); @@ -55,35 +54,6 @@ void UtilsTests::TestClampToShortMax() auto withinRangeActual = ClampToShortMax(withinRangeExpected, min); VERIFY_ARE_EQUAL(withinRangeExpected, withinRangeActual); } -void UtilsTests::TestSwapColorPalette() -{ - std::array terminalTable; - std::array consoleTable; - - gsl::span terminalTableView = { &terminalTable[0], terminalTable.size() }; - gsl::span consoleTableView = { &consoleTable[0], consoleTable.size() }; - - // First set up the colors - InitializeCampbellColorTable(terminalTableView); - InitializeCampbellColorTableForConhost(consoleTableView); - - VERIFY_ARE_EQUAL(terminalTable[0], consoleTable[0]); - VERIFY_ARE_EQUAL(terminalTable[1], consoleTable[4]); - VERIFY_ARE_EQUAL(terminalTable[2], consoleTable[2]); - VERIFY_ARE_EQUAL(terminalTable[3], consoleTable[6]); - VERIFY_ARE_EQUAL(terminalTable[4], consoleTable[1]); - VERIFY_ARE_EQUAL(terminalTable[5], consoleTable[5]); - VERIFY_ARE_EQUAL(terminalTable[6], consoleTable[3]); - VERIFY_ARE_EQUAL(terminalTable[7], consoleTable[7]); - VERIFY_ARE_EQUAL(terminalTable[8], consoleTable[8]); - VERIFY_ARE_EQUAL(terminalTable[9], consoleTable[12]); - VERIFY_ARE_EQUAL(terminalTable[10], consoleTable[10]); - VERIFY_ARE_EQUAL(terminalTable[11], consoleTable[14]); - VERIFY_ARE_EQUAL(terminalTable[12], consoleTable[9]); - VERIFY_ARE_EQUAL(terminalTable[13], consoleTable[13]); - VERIFY_ARE_EQUAL(terminalTable[14], consoleTable[11]); - VERIFY_ARE_EQUAL(terminalTable[15], consoleTable[15]); -} void UtilsTests::TestGuidToString() { diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 59919bba7..1dc18a68e 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -5,7 +5,7 @@ #include "inc/utils.hpp" #include "inc/colorTable.hpp" -#include +#include using namespace Microsoft::Console;