Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for customizing font fallback #16821

Merged
merged 11 commits into from
Mar 26, 2024
7 changes: 4 additions & 3 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1122,8 +1122,8 @@
msrc
MSVCRTD
MTSM
munges
Munged
munges
murmurhash
muxes
myapplet
Expand Down Expand Up @@ -1172,6 +1172,7 @@
NOCOPYBITS
NODUP
noexcepts
NOFONT
NOINTEGRALHEIGHT
NOINTERFACE
NOLINKINFO
Expand Down Expand Up @@ -1887,8 +1888,8 @@
ul
ulcch
uld
uldb
uldash
uldb
ulwave
Unadvise
unattend
Expand All @@ -1911,8 +1912,8 @@
UPDATEDISPLAY
UPDOWN
UPKEY
UPSS
upss
UPSS
Fixed Show fixed Hide fixed
uregex
URegular
usebackq
Expand Down
27 changes: 7 additions & 20 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
_renderer->AddRenderEngine(_renderEngine.get());

// Hook up the warnings callback as early as possible so that we catch everything.
_renderEngine->SetWarningCallback([this](HRESULT hr, wil::zwstring_view parameter) {
_rendererWarning(hr, parameter);
});

// Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
// and react accordingly.
Expand Down Expand Up @@ -372,12 +377,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation

_terminal->CreateFromSettings(*_settings, *_renderer);

// IMPORTANT! Set this callback up sooner than later. If we do it
// after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored!
_renderEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1));

// Tell the render engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid
// unnecessary callbacks (and locking problems)
Expand Down Expand Up @@ -1007,18 +1006,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
LOG_IF_FAILED(_renderEngine->UpdateFont(_desiredFont, _actualFont, featureMap, axesMap));
}

// If the actual font isn't what was requested...
if (_actualFont.GetFaceName() != _desiredFont.GetFaceName())
{
// Then warn the user that we picked something because we couldn't find their font.
// Format message with user's choice of font and the font that was chosen instead.
const winrt::hstring message{ fmt::format(std::wstring_view{ RS_(L"NoticeFontNotFound") },
_desiredFont.GetFaceName(),
_actualFont.GetFaceName()) };
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Warning, message);
_RaiseNoticeHandlers(*this, std::move(noticeArgs));
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This went into TermControl.cpp.


const auto actualNewSize = _actualFont.GetSize();
_FontSizeChangedHandlers(*this, winrt::make<FontSizeChangedArgs>(actualNewSize.width, actualNewSize.height));
}
Expand Down Expand Up @@ -1706,9 +1693,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}

void ControlCore::_rendererWarning(const HRESULT hr)
void ControlCore::_rendererWarning(const HRESULT hr, wil::zwstring_view parameter)
{
_RendererWarningHandlers(*this, winrt::make<RendererWarningArgs>(hr));
_RendererWarningHandlers(*this, winrt::make<RendererWarningArgs>(hr, winrt::hstring{ parameter }));
}

winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle)
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr };

#pragma region RendererCallbacks
void _rendererWarning(const HRESULT hr);
void _rendererWarning(const HRESULT hr, wil::zwstring_view parameter);
winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle);
void _rendererBackgroundColorChanged();
void _rendererTabColorChanged();
Expand Down
8 changes: 5 additions & 3 deletions src/cascadia/TerminalControl/EventArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
struct RendererWarningArgs : public RendererWarningArgsT<RendererWarningArgs>
{
public:
RendererWarningArgs(const uint64_t hr) :
_Result(hr)
RendererWarningArgs(const HRESULT hr, winrt::hstring parameter) :
_Result{ hr },
_Parameter{ std::move(parameter) }
{
}

WINRT_PROPERTY(uint64_t, Result);
WINRT_PROPERTY(HRESULT, Result);
WINRT_PROPERTY(winrt::hstring, Parameter);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow us to carry messages to the warning popup code in TermControl.

};

struct TransparencyChangedEventArgs : public TransparencyChangedEventArgsT<TransparencyChangedEventArgs>
Expand Down
5 changes: 3 additions & 2 deletions src/cascadia/TerminalControl/EventArgs.idl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ namespace Microsoft.Terminal.Control

runtimeclass RendererWarningArgs
{
UInt64 Result { get; };
HRESULT Result { get; };
String Parameter { get; };
}

runtimeclass TransparencyChangedEventArgs
Expand All @@ -90,7 +91,7 @@ namespace Microsoft.Terminal.Control
{
Boolean ShowOrHide { get; };
}

runtimeclass UpdateSelectionMarkersEventArgs
{
Boolean ClearMarkers { get; };
Expand Down
12 changes: 4 additions & 8 deletions src/cascadia/TerminalControl/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,8 @@
<comment>Whenever we encounter an invalid URI or URL we show this string as a warning.</comment>
</data>
<data name="NoticeFontNotFound" xml:space="preserve">
<value>Unable to find the selected font "{0}".

"{1}" has been selected instead.

Please either install the missing font or choose another one.</value>
<comment>0 and 1 are names of fonts provided by the user and system respectively.</comment>
<value>Unable to find the following fonts: {0}. Please either install the missing or choose different fonts.</value>
lhecker marked this conversation as resolved.
Show resolved Hide resolved
lhecker marked this conversation as resolved.
Show resolved Hide resolved
<comment>This is a warning dialog shown when the user selects a font that isn't installed.</comment>
</data>
<data name="PixelShaderNotFound" xml:space="preserve">
<value>Unable to find the provided shader "{0}".</value>
Expand All @@ -210,8 +206,8 @@ Please either install the missing font or choose another one.</value>
<value>Unable to compile the specified pixel shader.</value>
</data>
<data name="UnexpectedRendererError" xml:space="preserve">
lhecker marked this conversation as resolved.
Show resolved Hide resolved
<value>Renderer encountered an unexpected error: {0}</value>
<comment>{0} is an error code.</comment>
<value>Renderer encountered an unexpected error {0:#8x}: {1}</value>
<comment>{0:#8x} is a placeholder for a Windows error code (e.g. 0x88985002) and {1} is the corresponding message.</comment>
lhecker marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="TermControlReadOnly" xml:space="preserve">
<value>Read-only mode is enabled.</value>
Expand Down
55 changes: 32 additions & 23 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,36 +993,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - hr: an HRESULT describing the warning
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::_RendererWarning(IInspectable /*sender*/,
Control::RendererWarningArgs args)
winrt::fire_and_forget TermControl::_RendererWarning(IInspectable /*sender*/, Control::RendererWarningArgs args)
{
const auto hr = static_cast<HRESULT>(args.Result());

auto weakThis{ get_weak() };
co_await wil::resume_foreground(Dispatcher());

if (auto control{ weakThis.get() })
const auto control = weakThis.get();
if (!control)
{
winrt::hstring message;
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ||
HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") },
(_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()).PixelShaderPath()) };
}
else if (D2DERR_SHADER_COMPILE_FAILED == hr)
{
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderCompileFailed") }) };
}
else
{
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"UnexpectedRendererError") },
hr) };
}
co_return;
}

auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Warning, std::move(message));
control->_RaiseNoticeHandlers(*control, std::move(noticeArgs));
const auto hr = args.Result();
const auto parameter = args.Parameter();
winrt::hstring message;

switch (hr)
{
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") }, parameter) };
break;
case D2DERR_SHADER_COMPILE_FAILED:
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"PixelShaderCompileFailed") }) };
break;
case DWRITE_E_NOFONT:
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"NoticeFontNotFound") }, parameter) };
break;
default:
{
wchar_t buf[512];
const auto len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &buf[0], ARRAYSIZE(buf), nullptr);
const std::wstring_view msg{ &buf[0], len };
message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"UnexpectedRendererError") }, hr, msg) };
break;
}
}

auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Warning, std::move(message));
control->_RaiseNoticeHandlers(*control, std::move(noticeArgs));
}

void TermControl::_AttachDxgiSwapChainToXaml(HANDLE swapChainHandle)
Expand Down
Loading
Loading