diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index 53bff1ce7f2..2d0efed81fc 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -217,6 +217,10 @@ Please either install the missing font or choose another one. Unable to find the following fonts: {0}. Please either install them or choose different fonts. {Locked="{0}"} This is a warning dialog shown when the user selects a font that isn't installed. + + Your version of MacType is incompatible with this application. Please update to version 2023.5.31 or later. + {Locked="2023.5.31","MacType"} + Renderer encountered an unexpected error: {0:#010x} {1} {Locked="{0:#010x}","{1}"} {0:#010x} is a placeholder for a Windows error code (e.g. 0x88985002). {1} is the corresponding message. diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 1e73832b02d..cc97cf6aa57 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1143,6 +1143,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation case DWRITE_E_NOFONT: message = winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"RendererErrorFontNotFound") }, parameter) }; break; + case ATLAS_ENGINE_ERROR_MAC_TYPE: + message = RS_(L"RendererErrorMacType"); + break; default: { wchar_t buf[512]; diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index d85d6e0365f..b266d3ba092 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -37,6 +37,7 @@ TIL_FAST_MATH_BEGIN #pragma warning(disable : 26459) // You called an STL function '...' with a raw pointer parameter at position '...' that may be unsafe [...]. #pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). #pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2). +#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1). // Initializing large arrays can be very costly compared to how cheap some of these functions are. #define ALLOW_UNINITIALIZED_BEGIN _Pragma("warning(push)") _Pragma("warning(disable : 26494)") @@ -797,6 +798,13 @@ void BackendD3D::_resetGlyphAtlas(const RenderingPayload& p) void BackendD3D::_resizeGlyphAtlas(const RenderingPayload& p, const u16 u, const u16 v) { +#if defined(_M_X64) || defined(_M_IX86) + static const auto faultyMacTypeVersion = _checkMacTypeVersion(p); +#else + // The affected versions of MacType are unavailable on ARM. + static constexpr auto faultyMacTypeVersion = false; +#endif + _d2dRenderTarget.reset(); _d2dRenderTarget4.reset(); _glyphAtlas.reset(); @@ -842,9 +850,13 @@ void BackendD3D::_resizeGlyphAtlas(const RenderingPayload& p, const u16 u, const _d2dRenderTarget4->GetDevice(device.addressof()); device->SetMaximumTextureMemory(0); - if (const auto device4 = device.try_query()) + + if (!faultyMacTypeVersion) { - device4->SetMaximumColorGlyphCacheMemory(0); + if (const auto device4 = device.try_query()) + { + device4->SetMaximumColorGlyphCacheMemory(0); + } } } @@ -859,6 +871,62 @@ void BackendD3D::_resizeGlyphAtlas(const RenderingPayload& p, const u16 u, const _rectPackerData = Buffer{ u }; } +// MacType is a popular 3rd party system to give the font rendering on Windows a softer look. +// It's particularly popular in China. Unfortunately, it hooks ID2D1Device4 incorrectly: +// https://github.com/snowie2000/mactype/pull/938 +// This results in crashes. Not a lot of them, but enough to constantly show up. +// The issue was fixed in the MacType v1.2023.5.31 release, the only one in 2023. +// +// Please feel free to remove this check in a few years. +bool BackendD3D::_checkMacTypeVersion(const RenderingPayload& p) +{ +#ifdef _WIN64 + static constexpr auto name = L"MacType64.Core.dll"; +#else + static constexpr auto name = L"MacType.Core.dll"; +#endif + + wil::unique_hmodule handle; + if (!GetModuleHandleExW(0, name, handle.addressof())) + { + return false; + } + + const auto resource = FindResourceW(handle.get(), MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (!resource) + { + return false; + } + + const auto dataHandle = LoadResource(handle.get(), resource); + if (!dataHandle) + { + return false; + } + + const auto data = LockResource(dataHandle); + if (!data) + { + return false; + } + + VS_FIXEDFILEINFO* info; + UINT varLen = 0; + if (!VerQueryValueW(data, L"\\", reinterpret_cast(&info), &varLen)) + { + return false; + } + + const auto faulty = info->dwFileVersionMS < (1 << 16 | 2023); + + if (faulty && p.warningCallback) + { + p.warningCallback(ATLAS_ENGINE_ERROR_MAC_TYPE, {}); + } + + return faulty; +} + BackendD3D::QuadInstance& BackendD3D::_getLastQuad() noexcept { assert(_instancesCount != 0); diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 218df1f0179..d2712bb453f 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -204,6 +204,7 @@ namespace Microsoft::Console::Render::Atlas void _d2dEndDrawing(); ATLAS_ATTR_COLD void _resetGlyphAtlas(const RenderingPayload& p); ATLAS_ATTR_COLD void _resizeGlyphAtlas(const RenderingPayload& p, u16 u, u16 v); + static bool _checkMacTypeVersion(const RenderingPayload& p); QuadInstance& _getLastQuad() noexcept; QuadInstance& _appendQuad(); ATLAS_ATTR_COLD void _bumpInstancesSize(); diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 53b093e8a49..9651efd45c8 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -53,6 +53,8 @@ namespace Microsoft::Console::Render::Atlas // My best effort of replicating __attribute__((cold)) from gcc/clang. #define ATLAS_ATTR_COLD __declspec(noinline) +#define ATLAS_ENGINE_ERROR_MAC_TYPE MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_ITF, 'MT') + template struct vec2 {