"Intense text as bold" option for conhost/openconsole #23260

Open
opened 2026-01-31 08:37:04 +00:00 by claunia · 6 comments
Owner

Originally created by @johnd0e on GitHub (May 16, 2025).

Description of the new feature

There is such option in WT UI, so it would be nice to have the corresponding setting for openconsole/conhost

Proposed technical implementation details

No response

Originally created by @johnd0e on GitHub (May 16, 2025). ### Description of the new feature There is such option in WT UI, so it would be nice to have the corresponding setting for openconsole/conhost ### Proposed technical implementation details _No response_
claunia added the Product-ConhostArea-RenderingIssue-TaskPriority-3 labels 2026-01-31 08:37:05 +00:00
Author
Owner

@DHowett commented on GitHub (May 28, 2025):

Yup, we should probably do this.

The GDI engine doesn't support it, so it'll have to get in line behind "conhost uses Atlas Engine" and perhaps "we make the settings dialog better" :)

@DHowett commented on GitHub (May 28, 2025): Yup, we should probably do this. The GDI engine doesn't support it, so it'll have to get in line behind "conhost uses Atlas Engine" and perhaps "we make the settings dialog better" :)
Author
Owner

@johnd0e commented on GitHub (May 29, 2025):

and perhaps "we make the settings dialog better" :)

It would be good to have it even without dialog at first, with some registry setting.

@johnd0e commented on GitHub (May 29, 2025): > and perhaps "we make the settings dialog better" :) It would be good to have it even without dialog at first, with some registry setting.
Author
Owner

@alabuzhev commented on GitHub (Sep 27, 2025):

It is worth mentioning that bold already works in conhost/openconsole, but:

  • Only with Atlas Engine (UseDX=1).
  • Only with extended palettes (256 and RGB), where "intense" doesn't make much sense.
@alabuzhev commented on GitHub (Sep 27, 2025): It is worth mentioning that bold already works in conhost/openconsole, but: - Only with Atlas Engine (UseDX=1). - Only with extended palettes (256 and RGB), where "intense" doesn't make much sense.
Author
Owner

@alabuzhev commented on GitHub (Oct 2, 2025):

The GDI engine doesn't support it

Looks like adding the support is a no-brainer:

Spoiler
diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp
index d8bac2213..cbf93c6f6 100644
--- a/src/renderer/gdi/gdirenderer.hpp
+++ b/src/renderer/gdi/gdirenderer.hpp
@@ -103,7 +103,9 @@ namespace Microsoft::Console::Render
         bool _isTrueTypeFont;
         UINT _fontCodepage;
         HFONT _hfont;
+        HFONT _hfontBold;
         HFONT _hfontItalic;
+        HFONT _hfontBoldItalic;
         TEXTMETRICW _tmFontMetrics;
         FontResource _softFont;
 
@@ -151,7 +153,9 @@ namespace Microsoft::Console::Render
         {
             Undefined,
             Default,
+            Bold,
             Italic,
+            BoldItalic,
             Soft
         };
         FontType _lastFontType;
@@ -195,7 +199,9 @@ namespace Microsoft::Console::Render
                                                _Out_ FontInfo& Font,
                                                const int iDpi,
                                                _Inout_ wil::unique_hfont& hFont,
-                                               _Inout_ wil::unique_hfont& hFontItalic) noexcept;
+                                               _Inout_ wil::unique_hfont& hFontBold,
+                                               _Inout_ wil::unique_hfont& hFontItalic,
+                                               _Inout_ wil::unique_hfont& hFontBoldItalic) noexcept;
 
         til::size _GetFontSize() const;
         bool _IsMinimized() const;
diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp
index 8e498fc7d..6e7bceb6a 100644
--- a/src/renderer/gdi/state.cpp
+++ b/src/renderer/gdi/state.cpp
@@ -35,7 +35,9 @@ GdiEngine::GdiEngine() :
     _fPaintStarted(false),
     _invalidCharacters{},
     _hfont(nullptr),
+    _hfontBold(nullptr),
     _hfontItalic(nullptr),
+    _hfontBoldItalic(nullptr),
     _pool{ til::pmr::get_default_resource() }, // It's important the pool is first so it can be given to the others on construction.
     _polyStrings{ &_pool },
     _polyWidths{ &_pool }
@@ -96,12 +98,24 @@ GdiEngine::~GdiEngine()
         _hfont = nullptr;
     }
 
+    if (_hfontBold != nullptr)
+    {
+        LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBold)));
+        _hfontBold = nullptr;
+    }
+
     if (_hfontItalic != nullptr)
     {
         LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic)));
         _hfontItalic = nullptr;
     }
 
+    if (_hfontBoldItalic != nullptr)
+    {
+        LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBoldItalic)));
+        _hfontBoldItalic = nullptr;
+    }
+
     if (_hdcMemoryContext != nullptr)
     {
         LOG_HR_IF(E_FAIL, !(DeleteObject(_hdcMemoryContext)));
@@ -300,10 +314,13 @@ GdiEngine::~GdiEngine()
     }
 
     // If the font type has changed, select an appropriate font variant or soft font.
+    const auto usingBoldFont = textAttributes.IsBold(false);
     const auto usingItalicFont = textAttributes.IsItalic();
-    const auto fontType = usingSoftFont   ? FontType::Soft :
-                          usingItalicFont ? FontType::Italic :
-                                            FontType::Default;
+    const auto fontType = usingSoftFont                     ? FontType::Soft :
+                          usingBoldFont && !usingItalicFont ? FontType::Bold :
+                          !usingBoldFont && usingItalicFont ? FontType::Italic :
+                          usingBoldFont && usingItalicFont  ? FontType::BoldItalic :
+                                                              FontType::Default;
     if (fontType != _lastFontType)
     {
         switch (fontType)
@@ -311,9 +328,15 @@ GdiEngine::~GdiEngine()
         case FontType::Soft:
             SelectFont(_hdcMemoryContext, _softFont);
             break;
+        case FontType::Bold:
+            SelectFont(_hdcMemoryContext, _hfontBold);
+            break;
         case FontType::Italic:
             SelectFont(_hdcMemoryContext, _hfontItalic);
             break;
+        case FontType::BoldItalic:
+            SelectFont(_hdcMemoryContext, _hfontBoldItalic);
+            break;
         case FontType::Default:
         default:
             SelectFont(_hdcMemoryContext, _hfont);
@@ -336,8 +359,8 @@ GdiEngine::~GdiEngine()
 // - S_OK if set successfully or relevant GDI error via HRESULT.
 [[nodiscard]] HRESULT GdiEngine::UpdateFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font) noexcept
 {
-    wil::unique_hfont hFont, hFontItalic;
-    RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontItalic));
+    wil::unique_hfont hFont, hFontBold, hFontItalic, hFontBoldItalic;
+    RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontBold, hFontItalic, hFontBoldItalic));
 
     // Select into DC
     RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get()));
@@ -464,6 +487,13 @@ GdiEngine::~GdiEngine()
     // Save the font.
     _hfont = hFont.release();
 
+    // Persist bold font for cleanup (and free existing if necessary)
+    if (_hfontBold != nullptr)
+    {
+        LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBold)));
+        _hfontBold = nullptr;
+    }
+
     // Persist italic font for cleanup (and free existing if necessary)
     if (_hfontItalic != nullptr)
     {
@@ -471,9 +501,22 @@ GdiEngine::~GdiEngine()
         _hfontItalic = nullptr;
     }
 
+    // Persist bold italic font for cleanup (and free existing if necessary)
+    if (_hfontBoldItalic != nullptr)
+    {
+        LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBoldItalic)));
+        _hfontBoldItalic = nullptr;
+    }
+
+    // Save the bold font.
+    _hfontBold = hFontBold.release();
+
     // Save the italic font.
     _hfontItalic = hFontItalic.release();
 
+    // Save the bold italic font.
+    _hfontBoldItalic = hFontBoldItalic.release();
+
     // Save raster vs. TrueType and codepage data in case we need to convert.
     _isTrueTypeFont = Font.IsTrueTypeFont();
     _fontCodepage = Font.GetCodePage();
@@ -550,8 +593,8 @@ GdiEngine::~GdiEngine()
 // - S_OK if set successfully or relevant GDI error via HRESULT.
 [[nodiscard]] HRESULT GdiEngine::GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, const int iDpi) noexcept
 {
-    wil::unique_hfont hFont, hFontItalic;
-    return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontItalic);
+    wil::unique_hfont hFont, hFontBold, hFontItalic, hFontBoldItalic;
+    return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontBold, hFontItalic, hFontBoldItalic);
 }
 
 // Method Description:
@@ -577,14 +620,18 @@ GdiEngine::~GdiEngine()
 // - Font - the actual font
 // - iDpi - The DPI we will have when rendering
 // - hFont - A smart pointer to receive a handle to a ready-to-use GDI font.
+// - hFontBold - A smart pointer to receive a handle to a bold variant of the font.
 // - hFontItalic - A smart pointer to receive a handle to an italic variant of the font.
+// - hFontBoldItalic - A smart pointer to receive a handle to a bold italic variant of the font.
 // Return Value:
 // - S_OK if set successfully or relevant GDI error via HRESULT.
 [[nodiscard]] HRESULT GdiEngine::_GetProposedFont(const FontInfoDesired& FontDesired,
                                                   _Out_ FontInfo& Font,
                                                   const int iDpi,
                                                   _Inout_ wil::unique_hfont& hFont,
-                                                  _Inout_ wil::unique_hfont& hFontItalic) noexcept
+                                                  _Inout_ wil::unique_hfont& hFontBold,
+                                                  _Inout_ wil::unique_hfont& hFontItalic,
+                                                  _Inout_ wil::unique_hfont& hFontBoldItalic) noexcept
 {
     wil::unique_hdc hdcTemp(CreateCompatibleDC(_hdcMemoryContext));
     RETURN_HR_IF_NULL(E_FAIL, hdcTemp.get());
@@ -601,7 +648,9 @@ GdiEngine::~GdiEngine()
         // it may very well decide to choose Courier New instead of the Terminal raster.
 #pragma prefast(suppress : 38037, "raster fonts get special handling, we need to get it this way")
         hFont.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
+        hFontBold.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
         hFontItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
+        hFontBoldItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
     }
     else
     {
@@ -662,10 +711,22 @@ GdiEngine::~GdiEngine()
         hFont.reset(CreateFontIndirectW(&lf));
         RETURN_HR_IF_NULL(E_FAIL, hFont.get());
 
+        // Create bold variant of the font.
+        lf.lfWeight = FW_BOLD;
+        hFontBold.reset(CreateFontIndirectW(&lf));
+        RETURN_HR_IF_NULL(E_FAIL, hFontBold.get());
+
         // Create italic variant of the font.
+        lf.lfWeight = FontDesired.GetWeight();
         lf.lfItalic = TRUE;
         hFontItalic.reset(CreateFontIndirectW(&lf));
         RETURN_HR_IF_NULL(E_FAIL, hFontItalic.get());
+
+        // Create bold italic variant of the font.
+        lf.lfWeight = FW_BOLD;
+        lf.lfItalic = TRUE;
+        hFontBoldItalic.reset(CreateFontIndirectW(&lf));
+        RETURN_HR_IF_NULL(E_FAIL, hFontBoldItalic.get());
     }
 
     // Select into DC

Image

@DHowett, would you be interested in a PR?
Having it working at least in 256 & RGB should be better than nothing and bring it closer to Atlas.
We can think about the bold/intense switch for the base palette later.

@alabuzhev commented on GitHub (Oct 2, 2025): > The GDI engine doesn't support it Looks like adding the support is a no-brainer: <details> <summary> Spoiler </summary> ```diff diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index d8bac2213..cbf93c6f6 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -103,7 +103,9 @@ namespace Microsoft::Console::Render bool _isTrueTypeFont; UINT _fontCodepage; HFONT _hfont; + HFONT _hfontBold; HFONT _hfontItalic; + HFONT _hfontBoldItalic; TEXTMETRICW _tmFontMetrics; FontResource _softFont; @@ -151,7 +153,9 @@ namespace Microsoft::Console::Render { Undefined, Default, + Bold, Italic, + BoldItalic, Soft }; FontType _lastFontType; @@ -195,7 +199,9 @@ namespace Microsoft::Console::Render _Out_ FontInfo& Font, const int iDpi, _Inout_ wil::unique_hfont& hFont, - _Inout_ wil::unique_hfont& hFontItalic) noexcept; + _Inout_ wil::unique_hfont& hFontBold, + _Inout_ wil::unique_hfont& hFontItalic, + _Inout_ wil::unique_hfont& hFontBoldItalic) noexcept; til::size _GetFontSize() const; bool _IsMinimized() const; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 8e498fc7d..6e7bceb6a 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -35,7 +35,9 @@ GdiEngine::GdiEngine() : _fPaintStarted(false), _invalidCharacters{}, _hfont(nullptr), + _hfontBold(nullptr), _hfontItalic(nullptr), + _hfontBoldItalic(nullptr), _pool{ til::pmr::get_default_resource() }, // It's important the pool is first so it can be given to the others on construction. _polyStrings{ &_pool }, _polyWidths{ &_pool } @@ -96,12 +98,24 @@ GdiEngine::~GdiEngine() _hfont = nullptr; } + if (_hfontBold != nullptr) + { + LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBold))); + _hfontBold = nullptr; + } + if (_hfontItalic != nullptr) { LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic))); _hfontItalic = nullptr; } + if (_hfontBoldItalic != nullptr) + { + LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBoldItalic))); + _hfontBoldItalic = nullptr; + } + if (_hdcMemoryContext != nullptr) { LOG_HR_IF(E_FAIL, !(DeleteObject(_hdcMemoryContext))); @@ -300,10 +314,13 @@ GdiEngine::~GdiEngine() } // If the font type has changed, select an appropriate font variant or soft font. + const auto usingBoldFont = textAttributes.IsBold(false); const auto usingItalicFont = textAttributes.IsItalic(); - const auto fontType = usingSoftFont ? FontType::Soft : - usingItalicFont ? FontType::Italic : - FontType::Default; + const auto fontType = usingSoftFont ? FontType::Soft : + usingBoldFont && !usingItalicFont ? FontType::Bold : + !usingBoldFont && usingItalicFont ? FontType::Italic : + usingBoldFont && usingItalicFont ? FontType::BoldItalic : + FontType::Default; if (fontType != _lastFontType) { switch (fontType) @@ -311,9 +328,15 @@ GdiEngine::~GdiEngine() case FontType::Soft: SelectFont(_hdcMemoryContext, _softFont); break; + case FontType::Bold: + SelectFont(_hdcMemoryContext, _hfontBold); + break; case FontType::Italic: SelectFont(_hdcMemoryContext, _hfontItalic); break; + case FontType::BoldItalic: + SelectFont(_hdcMemoryContext, _hfontBoldItalic); + break; case FontType::Default: default: SelectFont(_hdcMemoryContext, _hfont); @@ -336,8 +359,8 @@ GdiEngine::~GdiEngine() // - S_OK if set successfully or relevant GDI error via HRESULT. [[nodiscard]] HRESULT GdiEngine::UpdateFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font) noexcept { - wil::unique_hfont hFont, hFontItalic; - RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontItalic)); + wil::unique_hfont hFont, hFontBold, hFontItalic, hFontBoldItalic; + RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontBold, hFontItalic, hFontBoldItalic)); // Select into DC RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get())); @@ -464,6 +487,13 @@ GdiEngine::~GdiEngine() // Save the font. _hfont = hFont.release(); + // Persist bold font for cleanup (and free existing if necessary) + if (_hfontBold != nullptr) + { + LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBold))); + _hfontBold = nullptr; + } + // Persist italic font for cleanup (and free existing if necessary) if (_hfontItalic != nullptr) { @@ -471,9 +501,22 @@ GdiEngine::~GdiEngine() _hfontItalic = nullptr; } + // Persist bold italic font for cleanup (and free existing if necessary) + if (_hfontBoldItalic != nullptr) + { + LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontBoldItalic))); + _hfontBoldItalic = nullptr; + } + + // Save the bold font. + _hfontBold = hFontBold.release(); + // Save the italic font. _hfontItalic = hFontItalic.release(); + // Save the bold italic font. + _hfontBoldItalic = hFontBoldItalic.release(); + // Save raster vs. TrueType and codepage data in case we need to convert. _isTrueTypeFont = Font.IsTrueTypeFont(); _fontCodepage = Font.GetCodePage(); @@ -550,8 +593,8 @@ GdiEngine::~GdiEngine() // - S_OK if set successfully or relevant GDI error via HRESULT. [[nodiscard]] HRESULT GdiEngine::GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, const int iDpi) noexcept { - wil::unique_hfont hFont, hFontItalic; - return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontItalic); + wil::unique_hfont hFont, hFontBold, hFontItalic, hFontBoldItalic; + return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontBold, hFontItalic, hFontBoldItalic); } // Method Description: @@ -577,14 +620,18 @@ GdiEngine::~GdiEngine() // - Font - the actual font // - iDpi - The DPI we will have when rendering // - hFont - A smart pointer to receive a handle to a ready-to-use GDI font. +// - hFontBold - A smart pointer to receive a handle to a bold variant of the font. // - hFontItalic - A smart pointer to receive a handle to an italic variant of the font. +// - hFontBoldItalic - A smart pointer to receive a handle to a bold italic variant of the font. // Return Value: // - S_OK if set successfully or relevant GDI error via HRESULT. [[nodiscard]] HRESULT GdiEngine::_GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, const int iDpi, _Inout_ wil::unique_hfont& hFont, - _Inout_ wil::unique_hfont& hFontItalic) noexcept + _Inout_ wil::unique_hfont& hFontBold, + _Inout_ wil::unique_hfont& hFontItalic, + _Inout_ wil::unique_hfont& hFontBoldItalic) noexcept { wil::unique_hdc hdcTemp(CreateCompatibleDC(_hdcMemoryContext)); RETURN_HR_IF_NULL(E_FAIL, hdcTemp.get()); @@ -601,7 +648,9 @@ GdiEngine::~GdiEngine() // it may very well decide to choose Courier New instead of the Terminal raster. #pragma prefast(suppress : 38037, "raster fonts get special handling, we need to get it this way") hFont.reset((HFONT)GetStockObject(OEM_FIXED_FONT)); + hFontBold.reset((HFONT)GetStockObject(OEM_FIXED_FONT)); hFontItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT)); + hFontBoldItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT)); } else { @@ -662,10 +711,22 @@ GdiEngine::~GdiEngine() hFont.reset(CreateFontIndirectW(&lf)); RETURN_HR_IF_NULL(E_FAIL, hFont.get()); + // Create bold variant of the font. + lf.lfWeight = FW_BOLD; + hFontBold.reset(CreateFontIndirectW(&lf)); + RETURN_HR_IF_NULL(E_FAIL, hFontBold.get()); + // Create italic variant of the font. + lf.lfWeight = FontDesired.GetWeight(); lf.lfItalic = TRUE; hFontItalic.reset(CreateFontIndirectW(&lf)); RETURN_HR_IF_NULL(E_FAIL, hFontItalic.get()); + + // Create bold italic variant of the font. + lf.lfWeight = FW_BOLD; + lf.lfItalic = TRUE; + hFontBoldItalic.reset(CreateFontIndirectW(&lf)); + RETURN_HR_IF_NULL(E_FAIL, hFontBoldItalic.get()); } // Select into DC ``` </details> <img width="460" height="220" alt="Image" src="https://github.com/user-attachments/assets/6ef3586f-9b7d-41ac-9981-5276d49f1557" /> @DHowett, would you be interested in a PR? Having it working at least in 256 & RGB should be better than nothing and bring it closer to Atlas. We can think about the bold/intense switch for the base palette later.
Author
Owner

@lhecker commented on GitHub (Oct 13, 2025):

@alabuzhev I'd gladly review your PR! 🙂

@lhecker commented on GitHub (Oct 13, 2025): @alabuzhev I'd gladly review your PR! 🙂
Author
Owner

@alabuzhev commented on GitHub (Oct 13, 2025):

#19441

@alabuzhev commented on GitHub (Oct 13, 2025): #19441
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23260