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
{