From c2a0d19fb95c759c70d91cd31ea7b77b4410cac1 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Thu, 26 Sep 2019 17:03:25 -0700 Subject: [PATCH] TermControl: force all ambiguous glyphs to be narrow From Egmont Koblinger: > In terminal emulation, apps have to be able to print something and keep track of the cursor, whereas they by design have no idea of the font being used. In many terminals the font can also be changed runtime and it's absolutely not feasible to then rearrange the cells. In some other cases there is no font at all (e.g. the libvterm headless terminal emulation library, or a detached screen/tmux), or there are multiple fonts at once (a screen/tmux attached from multiple graphical emulators). > The only way to do that is via some external agreement on the number of cells, which is typically the Unicode EastAsianWidth, often accessed via wcwidth(). It's not perfect (changes through Unicode versions, has ambiguous characters, etc.) but is still the best we have. > glibc's wcwidth() reports 1 for ambiguous width characters, so the de facto standard is that in terminals they are narrow. > If the glyph is wider then the terminal has to figure out what to do. It could crop it (newer versions of Konsole, as far as I know), overflow to the right (VTE), shrink it (Kitty I believe does this), etc. See Also: https://bugzilla.gnome.org/show_bug.cgi?id=767529 https://gitlab.freedesktop.org/terminal-wg/specifications/issues/9 https://www.unicode.org/reports/tr11/tr11-34.html Salient point from proposed update to Unicode Standard Annex #11: > Note: The East_Asian_Width property is not intended for use by modern terminal emulators without appropriate tailoring on a case-by-case basis. Fixes #2066 Fixes #2375 --- src/cascadia/TerminalControl/TermControl.cpp | 25 ++++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 4a0146a2bdf..0dbc4b081dc 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -22,6 +22,25 @@ using namespace winrt::Microsoft::Terminal::Settings; namespace winrt::Microsoft::Terminal::TerminalControl::implementation { + // Helper static function to ensure that all ambiguous-width glyphs are reported as narrow. + // See microsoft/terminal#2066 for more info. + static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */) + { + return false; // glyph is not wide. + } + + static bool _EnsureStaticInitialization() + { + // use C++11 magic statics to make sure we only do this once. + static bool initialized = []() { + // *** THIS IS A SINGLETON *** + SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback); + + return true; + }(); + return initialized; + } + TermControl::TermControl() : TermControl(Settings::TerminalSettings{}, TerminalConnection::ITerminalConnection{ nullptr }) { @@ -47,6 +66,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _lastMouseClick{}, _lastMouseClickPos{} { + _EnsureStaticInitialization(); _Create(); } @@ -413,11 +433,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>(); _renderer->AddRenderEngine(dxEngine.get()); - // Set up the renderer to be used to calculate the width of a glyph, - // should we be unable to figure out its width another way. - auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1); - SetGlyphWidthFallback(pfn); - // 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.