Compare commits

...

7 commits

Author SHA1 Message Date
Dustin L. Howett fd06b0ce60
Work around an ARM64 compiler crash by splitting a coroutine up (#10306)
Built and tested in PackageES
2021-06-01 11:56:28 -05:00
Mike Griese c8ac63e78e
Don't throw in GetProposedDimensions (#10260)
I cannot for the life of me repro the original bug. I've got fonts with bad permissions SxS, I've tried installing a font twice, I've tried stopping the font cache service. No idea how to manually repro the original bug.

BUT theoretically, this function should never throw. So lets just switch this to a `LOG_IF_FAILED`, and hope that this goes away?

* [x] Fixes #10211?
* [x] built & ran manually.

Unclear if this can get cherry-picked trivially to 1.8. Code's pretty trivial though so if we need another PR for that, it can be arranged.

(cherry picked from commit 89ca2ae05f)
2021-05-28 17:27:17 -05:00
Michael Niksa 414adf16a9
Switch FAIL_FAST to LOG for starting inbound connection server on monarch startup (#10261)
Stop startup crash by logging when monarch fails to register inbound connections, but still crash when COM attempted to start us

## References
- See also #10243

## PR Checklist
* [x] Closes #10233
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA

## Detailed Description of the Pull Request / Additional comments
- This should stop the crash on launch until we can get the internal teams to resolve the catalog issue
- I left the COM -Embedding start fail fast though so it won't take forever to time out (as default timeout is 3-5 minutes). I will change that if it becomes necessary.

## Validation Steps Performed
- I basically have to guess at this one based on the crash dump and Watson logs because it happens sporadically when the platform messes up on us.

(cherry picked from commit d8647e01c1)
2021-05-28 17:27:17 -05:00
Dustin L. Howett 3533aa2661
Ignore closeOnExit when a conn. moves from Connecting to Failed (#10263)
ConptyConnection has two different failure modes:

1. We failed to initialize the pseudoconsole or create the process
2. The process exited with an error code.

Until this commit, they were treated the same way: closeOnExit=always
would force the pane/tab to be destroyed. This was very bad in case 1,
where we would display a (possibly useful) error message and then
immediately close the window.

This was made even worse by the change in #10045. We removed
startingDirectory validation and promoted it to an error message (so
that we could eventually let the connection handle startingDirectory in
its own way.) This of course revealed that a number of users had set
invalid starting directories… and those users included some who set
closeOnExit to always. Boom: instant "terminal opens and crashes"¹

In this commit, we introduce detection for a connection that fails
before it's been established. When that happens, we will ignore the
user's closeOnExit mode.

¹ It only looks like a crash; it's actually _technically_ functioning
properly.

Closes #10225.

(cherry picked from commit 31d78dceb5)
2021-05-28 17:27:16 -05:00
Michael Niksa fbc59b7a3b
Prevent crashes in Settings UI launch on OS versions before package management extensions (#10238)
Prevent crashes in Settings UI launch on OS versions before package management extensions

## PR Checklist
* [x] Closes #10106
* [x] I work here
* [x] Manual tests passed.

## Detailed Description of the Pull Request / Additional comments
- On older OS versions like 18363, some of the COM interfaces we use to look up information from the OS application package management catalog (to find default terminals) are unavailable. This returns `E_NOINTERFACE`. This then ends up returning an empty list of items and null as a selected item.
- I had intended for that to not return that particular error all the way up and just log it because the console and terminal lookup functions always return at least one element: the one representing the `conhost.exe` that is already on the machine.
- I have changed the "default packages" lookup to log instead of return failures like E_NOINTERFACE such that it can continue processing and make the "package" of the hardcoded `conhost.exe` default no matter what. (It will still return an error if there are somehow 0 packages because that code changed or some other catastrophic event happened...)
- I have also changed the Model to have a nulled DefaultTerminal model object (as all winrt objects are nullable) instead of using an optional. I did this because XAML is perfectly happy receiving a `nullptr` for a selected item and will just not select anything. By contrast, if it has an exception occur... it will just bubble that out and crash.

## Validation Steps Performed
- Simulated no items returned from list and nullptr returned to XAML on Current() method of Model. Validated XAML will happily select no item from list (and is fine with an empty list of items... that is it doesn't crash).
- Simulated downlevel OS returning package management errors in lookup catalog functions after the hardcoded default is added to the list. Ensured that this error is only logged, the remainder of the package identification functions make the hardcoded default package, and it is presented as your one and only option in the XAML.

(cherry picked from commit b2c2a4c159)
2021-05-28 17:27:15 -05:00
Michael Niksa 30f1c64e08
Summon this window when it receives an inbound connection (#10217)
Summon the listening window when it receives an inbound connection

## PR Checklist
* [x] Closes #9460
* [x] I work here.
* [x] Manual test.

## Detailed Description of the Pull Request / Additional comments
- We cannot just send our window to foreground by simply calling user32 on the window handle. But fortunately, the remoting behavior already has a summon window function with a workaround for the Quake functionality.
- This bubbles up an event from the TerminalApp's Page to the WindowsTerminal's Apphost so it can call the same window summoning behavior in IslandWindow as is triggered when the Monarch dictates this out of the Microsoft.Terminal.Remoting project.

## Validation Steps Performed
- Opened the Terminal with it registered as DefTerm. Activated some other windows to the foreground. Start > Run > Cmd. Tab connects and opens in existing Terminal and it is brought to foreground.
- With no running Terminal and registered as DefTerm, do Start > Run > Cmd. New Terminal is spawned and it is brought to foreground

(cherry picked from commit e694f36ad2)
2021-05-28 17:27:15 -05:00
Michael Niksa d9c93430a0
Correct Default Application Selector styles for high contrast and to change with OS theme dark/light toggle (#10185)
Correct Default Application Selector styles for high contrast and to change with OS theme dark/light toggle

## References
- https://docs.microsoft.com/windows/uwp/design/controls-and-patterns/xaml-theme-resources

## PR Checklist
* [x] Closes #10181
* [x] I work here
* [x] Manual tests passed

## Detailed Description of the Pull Request / Additional comments
1. If I'm going to override colors, I need to define styles in a resource dictionary with Light, Dark, and HighContrast variants so it can be appropriate for each of those.
2. For HighContrast, I need to not mess with text colors and let them follow the default settings.
3. For using System Brushes, I need to use a `ThemeResource` binding not a `StaticResource` binding. The former lets it change when you flip the OS toggle Light/Dark. The latter is stuck to whatever it was when the page loaded.

## Validation Steps Performed
- Loaded in light mode. Flipped to dark. Watched it change live. Checked both unselected and rollover/selected to ensure it was fine.
- Loaded in dark mode. Flipped to light. Watched it change live. Checked both unselected and rollover/selected to ensure it was fine.
- Flipped to HC. Watched it change live. Confirmed that unselected is black/white contrast and the roll over has the cyan/black. (No longer uses special second-line brush for HC, matches the controls I modeled this one on from OS Settings).

(cherry picked from commit 43d5713a02)
2021-05-28 17:27:06 -05:00
21 changed files with 178 additions and 66 deletions

View file

@ -1206,7 +1206,7 @@ namespace winrt::TerminalApp::implementation
// in and be routed to an event with no handlers or a non-ready Page.
if (_appArgs.IsHandoffListener())
{
SetInboundListener();
_root->SetInboundListener(true);
}
}
@ -1222,7 +1222,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void AppLogic::SetInboundListener()
{
_root->SetInboundListener();
_root->SetInboundListener(false);
}
// Method Description:

View file

@ -165,6 +165,7 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;

View file

@ -92,5 +92,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
}
}

View file

@ -329,6 +329,7 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
}
const auto newConnectionState = _control.ConnectionState();
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
if (newConnectionState < ConnectionState::Closed)
{
@ -336,6 +337,14 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
return;
}
if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed)
{
// A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit".
// This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting
// in Terminal flashing open and immediately closed.
return;
}
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
auto paneProfile = settings.FindProfile(_profile.value());
if (paneProfile)
@ -709,6 +718,7 @@ void Pane::_CloseChild(const bool closeFirst)
// take the control, profile and id of the pane that _wasn't_ closed.
_control = remainingChild->_control;
_connectionState = remainingChild->_connectionState;
_profile = remainingChild->_profile;
_id = remainingChild->Id();
@ -1476,6 +1486,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
// Move our control, guid into the first one.
// Move the new guid, control into the second.
_firstChild = std::make_shared<Pane>(_profile.value(), _control);
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
_profile = std::nullopt;
_control = { nullptr };
_secondChild = std::make_shared<Pane>(profile, control);

View file

@ -95,6 +95,7 @@ private:
winrt::Windows::UI::Xaml::Controls::Grid _root{};
winrt::Windows::UI::Xaml::Controls::Border _border{};
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;

View file

@ -356,7 +356,27 @@ namespace winrt::TerminalApp::implementation
// we would be left with an empty terminal frame with no tabs.
// Instead, crash out so COM sees the server die and things unwind
// without a weird empty frame window.
CATCH_FAIL_FAST()
catch (...)
{
// However, we cannot always fail fast because of MSFT:33501832. Sometimes the COM catalog
// tears the state between old and new versions and fails here for that reason.
// As we're always becoming an inbound server in the monarch, even when COM didn't strictly
// ask us yet...we might just crash always.
// Instead... we're going to differentiate. If COM started us... we will fail fast
// so it sees the process die and falls back.
// If we were just starting normally as a Monarch and opportunistically listening for
// inbound connections... then we'll just log the failure and move on assuming
// the version state is torn and will fix itself whenever the packaging upgrade
// tasks decide to clean up.
if (_isEmbeddingInboundListener)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
else
{
LOG_CAUGHT_EXCEPTION();
}
}
}
}
@ -1981,12 +2001,13 @@ namespace winrt::TerminalApp::implementation
// listener for command-line tools attempting to join this Terminal
// through the default application channel.
// Arguments:
// - <none> - Implicitly sets to true. Default page state is false.
// - isEmbedding - True if COM started us to be a server. False if we're doing it of our own accord.
// Return Value:
// - <none>
void TerminalPage::SetInboundListener()
void TerminalPage::SetInboundListener(bool isEmbedding)
{
_shouldStartInboundListener = true;
_isEmbeddingInboundListener = isEmbedding;
// If the page has already passed the NotInitialized state,
// then it is ready-enough for us to just start this immediately.
@ -2319,6 +2340,9 @@ namespace winrt::TerminalApp::implementation
{
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
_OpenNewTab(nullptr, connection);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
}
// Method Description:

View file

@ -79,7 +79,7 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void SetInboundListener();
void SetInboundListener(bool isEmbedding);
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
@ -124,6 +124,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable);
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
@ -174,6 +175,7 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
bool _shouldStartInboundListener{ false };
bool _isEmbeddingInboundListener{ false };
std::shared_ptr<Toast> _windowIdToast{ nullptr };
std::shared_ptr<Toast> _windowRenameFailedToast{ nullptr };

View file

@ -56,5 +56,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
}
}

View file

@ -251,6 +251,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void ConptyConnection::Start()
try
{
_transitionToState(ConnectionState::Connecting);
if (!_inPipe)
{
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };

View file

@ -1845,9 +1845,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// will take up.
// TODO: MSFT:21254947 - use a static function to do this instead of
// instantiating a DxEngine
// GH#10211 - UNDER NO CIRCUMSTANCE should this fail. If it does, the
// whole app will crash instantaneously on launch, which is no good.
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
THROW_IF_FAILED(dxEngine->UpdateDpi(dpi));
THROW_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
LOG_IF_FAILED(dxEngine->UpdateDpi(dpi));
LOG_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
const auto scale = dxEngine->GetScaling();
const auto fontSize = actualFont.GetSize();

View file

@ -11,6 +11,26 @@
<ResourceDictionary Source="SettingContainerStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Style x:Key="SecondaryTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemBaseMediumColor}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Style x:Key="SecondaryTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemBaseMediumColor}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Style x:Key="SecondaryTextBlockStyle"
TargetType="TextBlock" />
<!-- Do not mess with the foreground color for High Contrast. Let it ride as is. -->
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<x:Double x:Key="StandardIconSize">14.0</x:Double>
<Thickness x:Key="StandardIndentMargin">13,0,0,0</Thickness>
<Thickness x:Key="StandardControlMargin">0,24,0,0</Thickness>

View file

@ -114,12 +114,12 @@
<TextBlock Grid.Row="1"
Grid.Column="1"
Foreground="{StaticResource SystemBaseMediumColor}"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind Author}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Foreground="{StaticResource SystemBaseMediumColor}"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind Version}" />
</Grid>

View file

@ -11,7 +11,7 @@ using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
winrt::Windows::Foundation::Collections::IVector<Model::DefaultTerminal> DefaultTerminal::_available = winrt::single_threaded_vector<Model::DefaultTerminal>();
std::optional<Model::DefaultTerminal> DefaultTerminal::_current;
Model::DefaultTerminal DefaultTerminal::_current = nullptr;
DefaultTerminal::DefaultTerminal(DelegationConfig::DelegationPackage pkg) :
_pkg(pkg)
@ -81,7 +81,10 @@ Model::DefaultTerminal DefaultTerminal::Current()
{
Refresh();
}
return _current.value();
// The potential of returning nullptr feels weird, but XAML can handle that appropriately
// and will select nothing as current in the dropdown.
return _current;
}
void DefaultTerminal::Current(const Model::DefaultTerminal& term)

View file

@ -42,7 +42,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
private:
DelegationConfig::DelegationPackage _pkg;
static Windows::Foundation::Collections::IVector<Model::DefaultTerminal> _available;
static std::optional<Model::DefaultTerminal> _current;
static Model::DefaultTerminal _current;
};
}

View file

@ -266,6 +266,7 @@ void AppHost::Initialize()
_logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested });
_logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged });
_logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged });
_logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested });
_window->UpdateTitle(_logic.Title());
@ -586,7 +587,7 @@ bool AppHost::HasWindow()
// - args: the bundle of a commandline and working directory to use for this invocation.
// Return Value:
// - <none>
void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/,
void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender,
Remoting::CommandlineArgs args)
{
const Remoting::SummonWindowBehavior summonArgs{};
@ -595,7 +596,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se
summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace);
// Summon the window whenever we dispatch a commandline to it. This will
// make it obvious when a new tab/pane is created in a window.
_window->SummonWindow(summonArgs);
_HandleSummon(sender, summonArgs);
_logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory());
}
@ -925,3 +926,14 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab
{
_window->IsQuakeWindow(_logic.IsQuakeWindow());
}
void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable&)
{
const Remoting::SummonWindowBehavior summonArgs{};
summonArgs.MoveToCurrentDesktop(false);
summonArgs.DropdownDuration(0);
summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace);
_HandleSummon(sender, summonArgs);
}

View file

@ -82,4 +82,7 @@ private:
void _IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
};

View file

@ -1023,7 +1023,15 @@ winrt::fire_and_forget IslandWindow::SummonWindow(Remoting::SummonWindowBehavior
{
// On the foreground thread:
co_await winrt::resume_foreground(_rootGrid.Dispatcher());
_summonWindowRoutineBody(args);
}
// Method Description:
// - As above.
// BODGY: ARM64 BUILD FAILED WITH fatal error C1001: Internal compiler error
// when this was part of the coroutine body.
void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args)
{
uint32_t actualDropdownDuration = args.DropdownDuration();
// If the user requested an animation, let's check if animations are enabled in the OS.
if (args.DropdownDuration() > 0)

View file

@ -111,6 +111,8 @@ protected:
bool _isQuakeWindow{ false };
void _enterQuakeMode();
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);
private:
// This minimum width allows for width the tabs fit
static constexpr long minimumWidth = 460L;

View file

@ -185,11 +185,14 @@ try
{
packages.clear();
// Get consoles and terminals.
// If we fail to look up any, we should still have ONE come back to us as the hardcoded default console host.
// The errors aren't really useful except for debugging, so log only.
std::vector<DelegationConsole> consoles;
RETURN_IF_FAILED(s_GetAvailableConsoles(consoles));
LOG_IF_FAILED(s_GetAvailableConsoles(consoles));
std::vector<DelegationTerminal> terminals;
RETURN_IF_FAILED(s_GetAvailableTerminals(terminals));
LOG_IF_FAILED(s_GetAvailableTerminals(terminals));
// TODO: I hate this algorithm (it's bad performance), but I couldn't
// find an AppModel interface that would let me look up all the extensions

View file

@ -644,63 +644,77 @@ CATCH_RETURN()
{
// First attempt to find exactly what the user asked for.
didFallback = false;
auto face = _FindFontFace(familyName, weight, stretch, style, localeName);
Microsoft::WRL::ComPtr<IDWriteFontFace1> face{ nullptr };
// GH#10211 - wrap this all up in a try/catch. If the nearby fonts are
// corrupted, then we don't want to throw out of this top half of this
// method. We still want to fall back to a font that's reasonable, below.
try
{
face = _FindFontFace(familyName, weight, stretch, style, localeName, true);
if (!face)
{
// If we missed, try looking a little more by trimming the last word off the requested family name a few times.
// Quite often, folks are specifying weights or something in the familyName and it causes failed resolution and
// an unexpected error dialog. We theoretically could detect the weight words and convert them, but this
// is the quick fix for the majority scenario.
// The long/full fix is backlogged to GH#9744
// Also this doesn't count as a fallback because we don't want to annoy folks with the warning dialog over
// this resolution.
while (!face && !familyName.empty())
{
const auto lastSpace = familyName.find_last_of(UNICODE_SPACE);
// value is unsigned and npos will be greater than size.
// if we didn't find anything to trim, leave.
if (lastSpace >= familyName.size())
{
break;
}
// trim string down to just before the found space
// (space found at 6... trim from 0 for 6 length will give us 0-5 as the new string)
familyName = familyName.substr(0, lastSpace);
// Try to find it with the shortened family name
face = _FindFontFace(familyName, weight, stretch, style, localeName, true);
}
}
}
CATCH_LOG();
// Alright, if our quick shot at trimming didn't work either...
// move onto looking up a font from our hardcoded list of fonts
// that should really always be available.
if (!face)
{
// If we missed, try looking a little more by trimming the last word off the requested family name a few times.
// Quite often, folks are specifying weights or something in the familyName and it causes failed resolution and
// an unexpected error dialog. We theoretically could detect the weight words and convert them, but this
// is the quick fix for the majority scenario.
// The long/full fix is backlogged to GH#9744
// Also this doesn't count as a fallback because we don't want to annoy folks with the warning dialog over
// this resolution.
while (!face && !familyName.empty())
for (const auto fallbackFace : FALLBACK_FONT_FACES)
{
const auto lastSpace = familyName.find_last_of(UNICODE_SPACE);
familyName = fallbackFace;
// With these fonts, don't attempt the nearby lookup. We're looking
// for system fonts only. If one of the nearby fonts is causing us
// problems (like in GH#10211), then we don't want to go anywhere
// value is unsigned and npos will be greater than size.
// if we didn't find anything to trim, leave.
if (lastSpace >= familyName.size())
// near it in this part.
face = _FindFontFace(familyName, weight, stretch, style, localeName, false);
if (face)
{
didFallback = true;
break;
}
// trim string down to just before the found space
// (space found at 6... trim from 0 for 6 length will give us 0-5 as the new string)
familyName = familyName.substr(0, lastSpace);
familyName = fallbackFace;
weight = DWRITE_FONT_WEIGHT_NORMAL;
stretch = DWRITE_FONT_STRETCH_NORMAL;
style = DWRITE_FONT_STYLE_NORMAL;
face = _FindFontFace(familyName, weight, stretch, style, localeName, false);
// Try to find it with the shortened family name
face = _FindFontFace(familyName, weight, stretch, style, localeName);
}
// Alright, if our quick shot at trimming didn't work either...
// move onto looking up a font from our hardcoded list of fonts
// that should really always be available.
if (!face)
{
for (const auto fallbackFace : FALLBACK_FONT_FACES)
if (face)
{
familyName = fallbackFace;
face = _FindFontFace(familyName, weight, stretch, style, localeName);
if (face)
{
didFallback = true;
break;
}
familyName = fallbackFace;
weight = DWRITE_FONT_WEIGHT_NORMAL;
stretch = DWRITE_FONT_STRETCH_NORMAL;
style = DWRITE_FONT_STYLE_NORMAL;
face = _FindFontFace(familyName, weight, stretch, style, localeName);
if (face)
{
didFallback = true;
break;
}
didFallback = true;
break;
}
}
}
@ -723,7 +737,8 @@ CATCH_RETURN()
DWRITE_FONT_WEIGHT& weight,
DWRITE_FONT_STRETCH& stretch,
DWRITE_FONT_STYLE& style,
std::wstring& localeName) const
std::wstring& localeName,
const bool withNearbyLookup) const
{
Microsoft::WRL::ComPtr<IDWriteFontFace1> fontFace;
@ -735,7 +750,7 @@ CATCH_RETURN()
THROW_IF_FAILED(fontCollection->FindFamilyName(familyName.data(), &familyIndex, &familyExists));
// If the system collection missed, try the files sitting next to our binary.
if (!familyExists)
if (withNearbyLookup && !familyExists)
{
auto&& nearbyCollection = NearbyCollection();

View file

@ -71,7 +71,8 @@ namespace Microsoft::Console::Render
DWRITE_FONT_WEIGHT& weight,
DWRITE_FONT_STRETCH& stretch,
DWRITE_FONT_STYLE& style,
std::wstring& localeName) const;
std::wstring& localeName,
const bool withNearbyLookup) const;
[[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null<IDWriteFontFamily*> const fontFamily,
std::wstring& localeName) const;