https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5d3915d0fcc465f65a614…
commit 5d3915d0fcc465f65a6146b3d6bd9c050638dc18
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sat Jan 29 20:04:16 2022 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Tue Feb 8 15:59:07 2022 +0100
[CONCFG:FONT][CONSRV] Use a suitable font when changing console output CP; fail if none found. (#4337)
CORE-12451, CORE-17601, CORE-17803
Replaces PR #4281.
When changing the console output code page, check whether the current
font can support it. If not, try to find a suitable font for the new
code page. If none can be found:
- if we are creating a new console, forcefully switch to codepage 437
(OEM USA) and retry finding a font, falling back to "Terminal" if
none could be found;
- if we were just changing the current CP, just fail and keep the old
code page and font.
Rework the console font selection/creation functions for this new job
(see CreateConsoleFontEx() and friends). Elements of implementation
based from https://github.com/microsoft/terminal ; see code for more
information.
Silence the noisy IsValidConsoleFont2() diagnostic messages.
Add Doxygen documentation.
[CONSOLE.CPL] Only add "Terminal" to the enumerated list of faces
+ add a TODO implementation comment.
---
dll/cpl/console/font.c | 6 +-
win32ss/user/winsrv/concfg/font.c | 703 +++++++++++++++++++--
win32ss/user/winsrv/concfg/font.h | 33 +-
win32ss/user/winsrv/consrv/frontends/gui/conwnd.c | 113 +++-
.../user/winsrv/consrv/frontends/gui/guisettings.c | 21 +-
.../user/winsrv/consrv/frontends/gui/guisettings.h | 2 +-
win32ss/user/winsrv/consrv/frontends/gui/guiterm.c | 27 +-
win32ss/user/winsrv/consrv/frontends/gui/guiterm.h | 17 +-
8 files changed, 794 insertions(+), 128 deletions(-)
diff --git a/dll/cpl/console/font.c b/dll/cpl/console/font.c
index 4cb5f4c61d3..888375924bb 100644
--- a/dll/cpl/console/font.c
+++ b/dll/cpl/console/font.c
@@ -429,9 +429,11 @@ FaceNameList_Initialize(
DPRINT1("The ideal console fonts were not found; manually add default ones.\n");
AddFontToList(hWndList, L"Terminal", RASTER_FONTTYPE);
+#if 0
+ // TODO: insert only the *single* default TT font, that should
+ // be found in the TT font cache with the codepage number 0.
AddFontToList(hWndList, L"Lucida Console", TRUETYPE_FONTTYPE);
- if (CodePageToCharSet(CodePage) != DEFAULT_CHARSET)
- AddFontToList(hWndList, L"Droid Sans Fallback", TRUETYPE_FONTTYPE);
+#endif
}
static VOID
diff --git a/win32ss/user/winsrv/concfg/font.c b/win32ss/user/winsrv/concfg/font.c
index 2889d9dd5ef..59b12e3ae90 100644
--- a/win32ss/user/winsrv/concfg/font.c
+++ b/win32ss/user/winsrv/concfg/font.c
@@ -1,10 +1,9 @@
/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS Console Server DLL
- * FILE: win32ss/user/winsrv/concfg/font.c
- * PURPOSE: Console Fonts Management
- * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
- * Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
+ * PROJECT: ReactOS Console Server DLL
+ * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Console GDI Fonts Management.
+ * COPYRIGHT: Copyright 2017-2022 Hermès Bélusca-Maïto
+ * Copyright 2017 Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
*/
/* INCLUDES *******************************************************************/
@@ -19,85 +18,594 @@
#define NDEBUG
#include <debug.h>
+#define DBGFNT DPRINT
+#define DBGFNT1 DPRINT1
+
/* GLOBALS ********************************************************************/
+#define TERMINAL_FACENAME L"Terminal"
+
// RTL_STATIC_LIST_HEAD(TTFontCache);
LIST_ENTRY TTFontCache = {&TTFontCache, &TTFontCache};
+// NOTE: Used to tag code that makes sense only with a font cache.
+// #define FONT_CACHE_PRESENT
+
+
/* FUNCTIONS ******************************************************************/
-/* Retrieves the character set associated with a given code page */
+/**
+ * @brief
+ * Retrieves the character set associated with a given code page.
+ *
+ * @param[in] CodePage
+ * The code page to convert.
+ *
+ * @return
+ * The character set corresponding to the code page, or @b DEFAULT_CHARSET.
+ **/
BYTE
CodePageToCharSet(
_In_ UINT CodePage)
{
CHARSETINFO CharInfo;
if (TranslateCharsetInfo(UlongToPtr(CodePage), &CharInfo, TCI_SRCCODEPAGE))
- return CharInfo.ciCharset;
+ return (BYTE)CharInfo.ciCharset;
else
return DEFAULT_CHARSET;
}
-HFONT
-CreateConsoleFontEx(
- _In_ LONG Height,
- _In_opt_ LONG Width,
- _Inout_updates_z_(LF_FACESIZE)
- PWSTR FaceName,
- _In_ ULONG FontFamily,
- _In_ ULONG FontWeight,
- _In_ UINT CodePage)
+/*****************************************************************************/
+
+typedef struct _FIND_SUITABLE_FONT_PROC_PARAM
+{
+ /* Search criteria */
+ _In_reads_or_z_(LF_FACESIZE) PCWSTR AltFaceName;
+ FONT_DATA SearchFont;
+ UINT CodePage;
+ BOOL StrictSearch; // TRUE to do strict search; FALSE for relaxed criteria.
+
+ /* Candidate font data */
+ BOOL FontFound; // TRUE/FALSE if we have/haven't found a suitable font.
+ FONT_DATA CandidateFont;
+ WCHAR CandidateFaceName[LF_FACESIZE];
+} FIND_SUITABLE_FONT_PROC_PARAM, *PFIND_SUITABLE_FONT_PROC_PARAM;
+
+#define TM_IS_TT_FONT(x) (((x) & TMPF_TRUETYPE) == TMPF_TRUETYPE)
+#define SIZE_EQUAL(s1, s2) (((s1).X == (s2).X) && ((s1).Y == (s2).Y))
+
+/**
+ * @brief EnumFontFamiliesEx() callback helper for FindSuitableFont().
+ *
+ * @remark
+ * It implements a nearly-identical console-suitable font search
+ * algorithm based on the one from FindCreateFont()
+ * https://github.com/microsoft/terminal/blob/main/src/propsheet/fontdlg.cpp#L…
+ * excepting that for now, it does not support an internal font cache.
+ **/
+static BOOL CALLBACK
+FindSuitableFontProc(
+ _In_ PLOGFONTW lplf,
+ _In_ PNEWTEXTMETRICW lpntm,
+ _In_ DWORD FontType,
+ _In_ LPARAM lParam)
+{
+ PFIND_SUITABLE_FONT_PROC_PARAM Param = (PFIND_SUITABLE_FONT_PROC_PARAM)lParam;
+ PFONT_DATA SearchFont = &Param->SearchFont;
+
+ if (!IsValidConsoleFont2(lplf, lpntm, FontType, Param->CodePage))
+ {
+ /* This font does not suit us; continue enumeration */
+ return TRUE;
+ }
+
+#ifndef FONT_CACHE_PRESENT
+ /*
+ * Since we don't cache all the possible font sizes for TrueType fonts,
+ * we cannot check our requested size (and weight) against the enumerated
+ * one; therefore reset the enumerated values to the requested ones.
+ * On the contrary, Raster fonts get their specific font sizes (and weights)
+ * enumerated separately, so for them we can keep the enumerated values.
+ */
+ if (FontType == TRUETYPE_FONTTYPE)
+ {
+ lplf->lfHeight = SearchFont->Size.Y;
+ lplf->lfWidth = 0; // SearchFont->Size.X;
+ lplf->lfWeight = FW_NORMAL;
+ }
+#endif
+
+ if (Param->StrictSearch)
+ {
+ /*
+ * Find whether this is an exact match.
+ */
+
+ /* If looking for a particular family, skip non-matches */
+ if ((SearchFont->Family != 0) &&
+ ((BYTE)SearchFont->Family != (lplf->lfPitchAndFamily & 0xF0)))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+
+ /* Skip non-matching sizes */
+#if 0
+ if ((FontInfo[i].SizeWant.Y != Size.Y) &&
+ !SIZE_EQUAL(FontInfo[i].Size, Size))
+#endif
+ if ((lplf->lfHeight != SearchFont->Size.Y) &&
+ !(lplf->lfWidth == SearchFont->Size.X &&
+ lplf->lfHeight == SearchFont->Size.Y))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+
+ /* Skip non-matching weights */
+ if ((SearchFont->Weight != 0) &&
+ (SearchFont->Weight != lplf->lfWeight))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+
+ /* NOTE: We are making the font enumeration at fixed CharSet,
+ * with the one specified in the parameter block. */
+ ASSERT(lplf->lfCharSet == SearchFont->CharSet);
+
+ if ((FontType != TRUETYPE_FONTTYPE) && // !TM_IS_TT_FONT(lpntm->tmPitchAndFamily)
+ (lplf->lfCharSet != SearchFont->CharSet) &&
+ !(lplf->lfCharSet == OEM_CHARSET && IsCJKCodePage(Param->CodePage))) // g_fEastAsianSystem
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+
+ /*
+ * Size (and maybe family) match. If we don't care about the name or
+ * if it matches, use this font. Otherwise, if name doesn't match and
+ * it is a raster font, consider it.
+ *
+ * NOTE: The font face names are case-sensitive.
+ */
+ if (!SearchFont->FaceName || !*(SearchFont->FaceName) ||
+ (wcscmp(lplf->lfFaceName, SearchFont->FaceName) == 0) ||
+ (wcscmp(lplf->lfFaceName, Param->AltFaceName) == 0))
+ {
+ // FontIndex = i;
+
+ PFONT_DATA CandidateFont = &Param->CandidateFont;
+
+ CandidateFont->FaceName = Param->CandidateFaceName;
+ StringCchCopyNW(Param->CandidateFaceName,
+ ARRAYSIZE(Param->CandidateFaceName),
+ lplf->lfFaceName, ARRAYSIZE(lplf->lfFaceName));
+
+ CandidateFont->Weight = lplf->lfWeight;
+ CandidateFont->Family = (lplf->lfPitchAndFamily & 0xF0);
+
+ CandidateFont->Size.X = lplf->lfWidth;
+ CandidateFont->Size.Y = lplf->lfHeight;
+
+ CandidateFont->CharSet = lplf->lfCharSet;
+
+ /* The font is found, stop enumeration */
+ Param->FontFound = TRUE;
+ return FALSE;
+ }
+ else if (FontType != TRUETYPE_FONTTYPE) // !TM_IS_TT_FONT(lpntm->tmPitchAndFamily)
+ {
+ // FontIndex = i;
+
+ PFONT_DATA CandidateFont = &Param->CandidateFont;
+
+ CandidateFont->FaceName = Param->CandidateFaceName;
+ StringCchCopyNW(Param->CandidateFaceName,
+ ARRAYSIZE(Param->CandidateFaceName),
+ lplf->lfFaceName, ARRAYSIZE(lplf->lfFaceName));
+
+ CandidateFont->Weight = lplf->lfWeight;
+ CandidateFont->Family = (lplf->lfPitchAndFamily & 0xF0);
+
+ CandidateFont->Size.X = lplf->lfWidth;
+ CandidateFont->Size.Y = lplf->lfHeight;
+
+ CandidateFont->CharSet = lplf->lfCharSet;
+
+ /* A close Raster Font fit was found; only the name doesn't match.
+ * Continue enumeration to see whether we can find better. */
+ Param->FontFound = TRUE;
+ }
+ }
+ else // !Param->StrictSearch
+ {
+ /*
+ * Failed to find exact match, even after enumeration, so now
+ * try to find a font of same family and same size or bigger.
+ */
+
+ if (IsCJKCodePage(Param->CodePage)) // g_fEastAsianSystem
+ {
+ if ((SearchFont->Family != 0) &&
+ ((BYTE)SearchFont->Family != (lplf->lfPitchAndFamily & 0xF0)))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+
+ if ((FontType != TRUETYPE_FONTTYPE) && // !TM_IS_TT_FONT(lpntm->tmPitchAndFamily)
+ (lplf->lfCharSet != SearchFont->CharSet))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (// (SearchFont->Family != 0) &&
+ ((BYTE)SearchFont->Family != (lplf->lfPitchAndFamily & 0xF0)))
+ {
+ /* Continue enumeration */
+ return TRUE;
+ }
+ }
+
+ if ((lplf->lfHeight >= SearchFont->Size.Y) &&
+ (lplf->lfWidth >= SearchFont->Size.X))
+ {
+ /* Same family, size >= desired */
+ // FontIndex = i;
+
+ PFONT_DATA CandidateFont = &Param->CandidateFont;
+
+ CandidateFont->FaceName = Param->CandidateFaceName;
+ StringCchCopyNW(Param->CandidateFaceName,
+ ARRAYSIZE(Param->CandidateFaceName),
+ lplf->lfFaceName, ARRAYSIZE(lplf->lfFaceName));
+
+ CandidateFont->Weight = lplf->lfWeight;
+ CandidateFont->Family = (lplf->lfPitchAndFamily & 0xF0);
+
+ CandidateFont->Size.X = lplf->lfWidth;
+ CandidateFont->Size.Y = lplf->lfHeight;
+
+ CandidateFont->CharSet = lplf->lfCharSet;
+
+ /* The font is found, stop enumeration */
+ Param->FontFound = TRUE;
+ return FALSE;
+ }
+ }
+
+ /* Continue enumeration */
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Finds a font suitable for the given code page, based on the current font
+ * and its characteristics provided in input.
+ *
+ * @param[in,out] FontData
+ * In input: The face name and characteristics of the font to search for,
+ * possibly getting a best match.
+ * In output: The face name and characteristics of the suitable font,
+ * in case of success.
+ *
+ * @param[in] CodePage
+ * The code page the font has to support.
+ *
+ * @return
+ * @b TRUE in case a suitable font has been found. Its name and characteristics
+ * are returned in @b FontData. @b FALSE if no suitable font has been found.
+ **/
+static BOOL
+FindSuitableFont(
+ _Inout_ PFONT_DATA FontData,
+ _In_ UINT CodePage)
{
+ FIND_SUITABLE_FONT_PROC_PARAM Param;
+ _Inout_updates_z_(LF_FACESIZE) PWSTR FaceName;
+ HDC hDC;
LOGFONTW lf;
+ PTT_FONT_ENTRY FontEntry;
+
+ /* Save the original FaceName pointer */
+ FaceName = FontData->FaceName;
+
+ /* Save our current search criteria */
+ RtlZeroMemory(&Param, sizeof(Param));
+ Param.SearchFont = *FontData;
+
+ Param.SearchFont.CharSet = CodePageToCharSet(CodePage);
+ Param.CodePage = CodePage;
+
+ /* Search for a TrueType alternative face name */
+ FontEntry = FindCachedTTFont(FaceName, CodePage);
+ if (FontEntry)
+ {
+ /* NOTE: The font face names are case-sensitive */
+ if (wcscmp(FontEntry->FaceName, FaceName) == 0)
+ Param.AltFaceName = FontEntry->FaceNameAlt;
+ else if (wcscmp(FontEntry->FaceNameAlt, FaceName) == 0)
+ Param.AltFaceName = FontEntry->FaceName;
+ }
+ else
+ {
+ Param.AltFaceName = FaceName;
+ }
+
+ /* Initialize the search: start with a strict search, then a relaxed one */
+ Param.FontFound = FALSE;
+ Param.StrictSearch = TRUE;
+SearchAgain:
+ /*
+ * Enumerate all fonts with the given character set.
+ * We will match them with the search criteria.
+ */
RtlZeroMemory(&lf, sizeof(lf));
+ lf.lfCharSet = Param.SearchFont.CharSet;
+ // lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
- lf.lfHeight = Height;
- lf.lfWidth = Width;
+ hDC = GetDC(NULL);
+ EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)FindSuitableFontProc, (LPARAM)&Param, 0);
+ ReleaseDC(NULL, hDC);
+
+ /* If we failed to find any font, search again with relaxed criteria */
+ if (Param.StrictSearch && !Param.FontFound)
+ {
+ Param.StrictSearch = FALSE;
+ goto SearchAgain;
+ }
+
+ /* If no font was found again, return failure */
+ if (!Param.FontFound)
+ return FALSE;
+
+ /* Return the font details */
+ *FontData = Param.CandidateFont;
+ FontData->FaceName = FaceName; // Restore the original FaceName pointer.
+ StringCchCopyNW(FaceName, LF_FACESIZE,
+ Param.CandidateFaceName,
+ ARRAYSIZE(Param.CandidateFaceName));
+
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Validates and creates a suitable console font based on the font
+ * characteristics given in input.
+ *
+ * @param[in] FontData
+ * The face name and characteristics of the font to create.
+ *
+ * @param[in] CodePage
+ * The code page the font has to support.
+ *
+ * @return
+ * A GDI handle to the created font, or @b NULL in case of failure.
+ **/
+static HFONT
+CreateConsoleFontWorker(
+ _In_ PFONT_DATA FontData,
+ _In_ UINT CodePage)
+{
+ LOGFONTW lf;
+
+ RtlZeroMemory(&lf, sizeof(lf));
+
+ lf.lfHeight = (LONG)(ULONG)FontData->Size.Y;
+ lf.lfWidth = (LONG)(ULONG)FontData->Size.X;
lf.lfEscapement = 0;
lf.lfOrientation = 0; // TA_BASELINE; // TA_RTLREADING; when the console supports RTL?
// lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE;
- lf.lfWeight = FontWeight;
+ lf.lfWeight = FontData->Weight;
lf.lfCharSet = CodePageToCharSet(CodePage);
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = DEFAULT_QUALITY;
/* Set the mandatory flags and remove those that we do not support */
- lf.lfPitchAndFamily = (BYTE)( (FIXED_PITCH | FF_MODERN | FontFamily) &
+ lf.lfPitchAndFamily = (BYTE)( (FIXED_PITCH | FF_MODERN | FontData->Family) &
~(VARIABLE_PITCH | FF_DECORATIVE | FF_ROMAN | FF_SCRIPT | FF_SWISS));
- if (!IsValidConsoleFont(FaceName, CodePage))
+ if (!IsValidConsoleFont(FontData->FaceName, CodePage))
+ return NULL;
+
+ StringCchCopyNW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName),
+ FontData->FaceName, LF_FACESIZE);
+
+ return CreateFontIndirectW(&lf);
+}
+
+/*****************************************************************************/
+
+/**
+ * @brief
+ * Validates and creates a suitable console font based on the font
+ * characteristics given in input.
+ *
+ * @param[in] Height
+ * The font height in cell units (pixels).
+ *
+ * @param[in,opt] Width
+ * The font width in cell units (pixels).
+ *
+ * @param[in,out] FaceName
+ * A pointer to a maximally @b LF_FACESIZE-sized buffer.
+ * In input: The buffer contains the face name of the font to try to create.
+ * In output: The buffer receives the face name of the font that has been
+ * created, in case of success. It may, or may not be, identical to the face
+ * name provided in input, in case a substitute font has been chosen.
+ *
+ * @param[in] FontWeight
+ * The font weight.
+ *
+ * @param[in] FontFamily
+ * The font family.
+ *
+ * @param[in] CodePage
+ * The code page the font has to support.
+ *
+ * @param[in] UseDefaultFallback
+ * Whether (@b TRUE) or not (@b FALSE) to use a default fallback font in case
+ * neither the specified font nor any substitute font could be found and
+ * created for the specified code page.
+ *
+ * @param[out] FontData
+ * The face name and characteristics of the created font.
+ *
+ * @return
+ * A GDI handle to the created font, or @b NULL in case of failure.
+ *
+ * @remark
+ * Similar to FindCreateFont()
+ * https://github.com/microsoft/terminal/blob/main/src/propsheet/fontdlg.cpp#L…
+ * but:
+ * - does not support an internal font cache for now;
+ * - returns a font handle (and not a font index to the cache).
+ **/
+HFONT
+CreateConsoleFontEx(
+ _In_ LONG Height,
+ _In_opt_ LONG Width,
+ _Inout_updates_z_(LF_FACESIZE)
+ PWSTR FaceName,
+ _In_ ULONG FontWeight,
+ _In_ ULONG FontFamily,
+ _In_ UINT CodePage,
+ _In_ BOOL UseDefaultFallback,
+ _Out_ PFONT_DATA FontData)
+{
+ HFONT hFont;
+
+ FontData->FaceName = FaceName;
+ FontData->Weight = FontWeight;
+ FontData->Family = FontFamily;
+ /* NOTE: FontSize is always in cell height/width units (pixels) */
+ FontData->Size.X = Width;
+ FontData->Size.Y = Height;
+ FontData->CharSet = 0; // CodePageToCharSet(CodePage);
+
+ hFont = CreateConsoleFontWorker(FontData, CodePage);
+ if (hFont)
+ return hFont;
+
+ DBGFNT1("CreateConsoleFont('%S') failed - Try to find a suitable font...\n",
+ FaceName);
+
+ /*
+ * We could not create a font with the default settings.
+ * Try to find a suitable font and retry.
+ */
+ if (!FindSuitableFont(FontData, CodePage))
{
- StringCchCopyW(FaceName, LF_FACESIZE, L"Terminal");
+ /* We could not find any suitable font, fall back
+ * to some default one if required to do so. */
+ DBGFNT1("FindSuitableFont could not find anything - %s\n",
+ UseDefaultFallback ? "Falling back to 'Terminal'"
+ : "Bailing out");
+
+ /* No fallback: no font! */
+ if (!UseDefaultFallback)
+ return NULL;
+
+ /* Use "Terminal" as the fallback */
+ StringCchCopyW(FaceName, LF_FACESIZE, TERMINAL_FACENAME);
+#if 0
+ // FIXME: CJK font choose workaround: Don't choose Asian
+ // charset font if there is no preferred font for CJK.
if (IsCJKCodePage(CodePage))
- {
- lf.lfCharSet = ANSI_CHARSET;
- }
+ FontData->CharSet = ANSI_CHARSET;
+#endif
+ }
+ else
+ {
+ DBGFNT1("FindSuitableFont found: '%S', size (%d x %d)\n",
+ FaceName, FontData->Size.X, FontData->Size.Y);
}
- StringCchCopyNW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName),
- FaceName, LF_FACESIZE);
+ /* Retry creating the font */
+ hFont = CreateConsoleFontWorker(FontData, CodePage);
+ if (!hFont)
+ DBGFNT1("CreateConsoleFont('%S') failed\n", FaceName);
- return CreateFontIndirectW(&lf);
+ return hFont;
}
+/**
+ * @brief
+ * A wrapper for CreateConsoleFontEx().
+ *
+ * @param[in] Height
+ * The font height in cell units (pixels).
+ *
+ * @param[in,opt] Width
+ * The font width in cell units (pixels).
+ *
+ * @param[in,out] ConsoleInfo
+ * A pointer to console settings information, containing in particular
+ * (in input) the face name and characteristics of the font to create
+ * with the current console code page.
+ * In output, the font information gets updated.
+ * Note that a default fallback font is always being used in case neither
+ * the specified font nor any substitute font could be found and created
+ * for the specified code page.
+ *
+ * @return
+ * A GDI handle to the created font, or @b NULL in case of failure.
+ *
+ * @see CreateConsoleFontEx(), CreateConsoleFont()
+ **/
HFONT
CreateConsoleFont2(
_In_ LONG Height,
_In_opt_ LONG Width,
_Inout_ PCONSOLE_STATE_INFO ConsoleInfo)
{
- return CreateConsoleFontEx(Height,
- Width,
- ConsoleInfo->FaceName,
- ConsoleInfo->FontFamily,
- ConsoleInfo->FontWeight,
- ConsoleInfo->CodePage);
+ FONT_DATA FontData;
+ HFONT hFont;
+
+ hFont = CreateConsoleFontEx(Height,
+ Width,
+ ConsoleInfo->FaceName,
+ ConsoleInfo->FontWeight,
+ ConsoleInfo->FontFamily,
+ ConsoleInfo->CodePage,
+ TRUE, // UseDefaultFallback
+ &FontData);
+ if (hFont)
+ {
+ ConsoleInfo->FontWeight = FontData.Weight;
+ ConsoleInfo->FontFamily = FontData.Family;
+ }
+
+ return hFont;
}
+/**
+ * @brief
+ * A wrapper for CreateConsoleFontEx().
+ *
+ * @param[in,out] ConsoleInfo
+ * A pointer to console settings information, containing in particular
+ * (in input) the face name and characteristics of the font to create
+ * with the current console code page.
+ * In output, the font information gets updated.
+ * Note that a default fallback font is always being used in case neither
+ * the specified font nor any substitute font could be found and created
+ * for the specified code page.
+ *
+ * @return
+ * A GDI handle to the created font, or @b NULL in case of failure.
+ *
+ * @see CreateConsoleFontEx(), CreateConsoleFont2()
+ **/
HFONT
CreateConsoleFont(
_Inout_ PCONSOLE_STATE_INFO ConsoleInfo)
@@ -108,14 +616,30 @@ CreateConsoleFont(
* Height = FontSize.Y = HIWORD(FontSize);
*/
/* NOTE: FontSize is always in cell height/width units (pixels) */
- return CreateConsoleFontEx((LONG)(ULONG)ConsoleInfo->FontSize.Y,
- (LONG)(ULONG)ConsoleInfo->FontSize.X,
- ConsoleInfo->FaceName,
- ConsoleInfo->FontFamily,
- ConsoleInfo->FontWeight,
- ConsoleInfo->CodePage);
+ return CreateConsoleFont2((LONG)(ULONG)ConsoleInfo->FontSize.Y,
+ (LONG)(ULONG)ConsoleInfo->FontSize.X,
+ ConsoleInfo);
}
+/**
+ * @brief
+ * Retrieves the cell size for a console font.
+ *
+ * @param[in,opt] hDC
+ * An optional GDI device context handle.
+ *
+ * @param[in] hFont
+ * The GDI handle to the font.
+ *
+ * @param[out] Height
+ * In case of success, receives the cell height size (in pixels).
+ *
+ * @param[out] Width
+ * In case of success, receives the cell height size (in pixels).
+ *
+ * @return
+ * @b TRUE if success, @b FALSE in case of failure.
+ **/
_Success_(return)
BOOL
GetFontCellSize(
@@ -138,7 +662,7 @@ GetFontCellSize(
hOldFont = SelectObject(hDC, hFont);
if (hOldFont == NULL)
{
- DPRINT1("GetFontCellSize: SelectObject failed\n");
+ DBGFNT1("GetFontCellSize: SelectObject failed\n");
goto Quit;
}
@@ -152,7 +676,7 @@ GetFontCellSize(
*/
if (!GetTextMetricsW(hDC, &tm))
{
- DPRINT1("GetFontCellSize: GetTextMetrics failed\n");
+ DBGFNT1("GetFontCellSize: GetTextMetrics failed\n");
goto Cleanup;
}
@@ -186,6 +710,31 @@ Quit:
return Success;
}
+/**
+ * @brief
+ * Validates whether a given font can be supported in the console,
+ * under the specified code page.
+ *
+ * @param[in] lplf
+ * @param[in] lpntm
+ * @param[in] FontType
+ * The GDI font characteristics of the font to validate.
+ *
+ * @param[in] CodePage
+ * The code page the font has to support.
+ *
+ * @return
+ * @b TRUE if the font is valid and supported in the console,
+ * @b FALSE if not.
+ *
+ * @remark
+ * Equivalent of the font validation tests in FontEnumForV2Console()
+ * (or the more restrictive ones in FontEnum())
+ * https://github.com/microsoft/terminal/blob/main/src/propsheet/misc.cpp#L465
+ * https://github.com/microsoft/terminal/blob/main/src/propsheet/misc.cpp#L607
+ *
+ * @see IsValidConsoleFont()
+ **/
BOOL
IsValidConsoleFont2(
_In_ PLOGFONTW lplf,
@@ -212,7 +761,7 @@ IsValidConsoleFont2(
* - If it is an Asian TrueType font, it must also be an Asian character set.
*
* See also Raymond Chen's blog: https://devblogs.microsoft.com/oldnewthing/?p=26843
- * and MIT-licensed Microsoft Terminal source code: https://github.com/microsoft/Terminal/blob/master/src/propsheet/misc.cpp
+ * and MIT-licensed Microsoft Terminal source code: https://github.com/microsoft/terminal/blob/main/src/propsheet/misc.cpp
* for other details.
*
* To install additional TrueType fonts to be available for the console,
@@ -241,27 +790,27 @@ IsValidConsoleFont2(
/* ... if they are not in the list of additional TrueType fonts to include */
!IsAdditionalTTFont(FaceName) )
{
- DPRINT1("Font '%S' rejected because it%s (lfPitchAndFamily = %d)\n",
- FaceName,
- !(lplf->lfPitchAndFamily & FIXED_PITCH) ? "'s not FIXED_PITCH"
- : (!(lpntm->ntmFlags & NTM_NONNEGATIVE_AC) ? " has negative A or C space"
- : " is broken"),
- lplf->lfPitchAndFamily);
+ DBGFNT("Font '%S' rejected because it%s (lfPitchAndFamily = %d)\n",
+ FaceName,
+ !(lplf->lfPitchAndFamily & FIXED_PITCH) ? "'s not FIXED_PITCH"
+ : (!(lpntm->ntmFlags & NTM_NONNEGATIVE_AC) ? " has negative A or C space"
+ : " is broken"),
+ lplf->lfPitchAndFamily);
return FALSE;
}
/* Reject TrueType fonts that are not FF_MODERN */
if ((FontType == TRUETYPE_FONTTYPE) && ((lplf->lfPitchAndFamily & 0xF0) != FF_MODERN))
{
- DPRINT1("TrueType font '%S' rejected because it's not FF_MODERN (lfPitchAndFamily = %d)\n",
- FaceName, lplf->lfPitchAndFamily);
+ DBGFNT("TrueType font '%S' rejected because it's not FF_MODERN (lfPitchAndFamily = %d)\n",
+ FaceName, lplf->lfPitchAndFamily);
return FALSE;
}
/* Reject vertical fonts (tategaki) */
if (FaceName[0] == L'@')
{
- DPRINT1("Font '%S' rejected because it's vertical\n", FaceName);
+ DBGFNT("Font '%S' rejected because it's vertical\n", FaceName);
return FALSE;
}
@@ -276,14 +825,10 @@ IsValidConsoleFont2(
* Here we are inclusive and check for any CJK character set,
* instead of looking just at the current one via CodePageToCharSet().
*/
- if (!IsCJKCharSet(lplf->lfCharSet)
-#if 1 // FIXME: Temporary HACK!
- && wcscmp(FaceName, L"Terminal") != 0
-#endif
- )
+ if (!IsCJKCharSet(lplf->lfCharSet))
{
- DPRINT1("TrueType font '%S' rejected because it's not Asian charset (lfCharSet = %d)\n",
- FaceName, lplf->lfCharSet);
+ DBGFNT("TrueType font '%S' rejected because it's not Asian charset (lfCharSet = %d)\n",
+ FaceName, lplf->lfCharSet);
return FALSE;
}
@@ -299,8 +844,8 @@ IsValidConsoleFont2(
if (IsAdditionalTTFont(FaceName) && !IsAdditionalTTFontCP(FaceName, 0) &&
!IsCJKCharSet(lplf->lfCharSet))
{
- DPRINT1("Cached TrueType font '%S' rejected because it claims a code page that is not Asian charset (lfCharSet = %d)\n",
- FaceName, lplf->lfCharSet);
+ DBGFNT("Cached TrueType font '%S' rejected because it claims a code page that is not Asian charset (lfCharSet = %d)\n",
+ FaceName, lplf->lfCharSet);
return FALSE;
}
}
@@ -309,15 +854,15 @@ IsValidConsoleFont2(
/* Reject non-TrueType fonts that do not have an Asian character set */
if (!IsCJKCharSet(lplf->lfCharSet) && (lplf->lfCharSet != OEM_CHARSET))
{
- DPRINT1("Non-TrueType font '%S' rejected because it's not Asian charset or OEM_CHARSET (lfCharSet = %d)\n",
- FaceName, lplf->lfCharSet);
+ DBGFNT("Non-TrueType font '%S' rejected because it's not Asian charset or OEM_CHARSET (lfCharSet = %d)\n",
+ FaceName, lplf->lfCharSet);
return FALSE;
}
/* Reject non-TrueType fonts that are not Terminal */
- if (wcscmp(FaceName, L"Terminal") != 0)
+ if (wcscmp(FaceName, TERMINAL_FACENAME) != 0)
{
- DPRINT1("Non-TrueType font '%S' rejected because it's not 'Terminal'\n", FaceName);
+ DBGFNT("Non-TrueType font '%S' rejected because it's not 'Terminal'\n", FaceName);
return FALSE;
}
}
@@ -332,8 +877,8 @@ IsValidConsoleFont2(
(lplf->lfCharSet != DEFAULT_CHARSET) &&
(lplf->lfCharSet != OEM_CHARSET))
{
- DPRINT1("Non-TrueType font '%S' rejected because it's not ANSI_CHARSET or DEFAULT_CHARSET or OEM_CHARSET (lfCharSet = %d)\n",
- FaceName, lplf->lfCharSet);
+ DBGFNT("Non-TrueType font '%S' rejected because it's not ANSI_CHARSET or DEFAULT_CHARSET or OEM_CHARSET (lfCharSet = %d)\n",
+ FaceName, lplf->lfCharSet);
return FALSE;
}
}
@@ -348,6 +893,9 @@ typedef struct _IS_VALID_CONSOLE_FONT_PARAM
UINT CodePage;
} IS_VALID_CONSOLE_FONT_PARAM, *PIS_VALID_CONSOLE_FONT_PARAM;
+/**
+ * @brief EnumFontFamiliesEx() callback helper for IsValidConsoleFont().
+ **/
static BOOL CALLBACK
IsValidConsoleFontProc(
_In_ PLOGFONTW lplf,
@@ -362,6 +910,23 @@ IsValidConsoleFontProc(
return FALSE;
}
+/**
+ * @brief
+ * Validates whether a given font can be supported in the console,
+ * under the specified code page.
+ *
+ * @param[in] FaceName
+ * The face name of the font to validate.
+ *
+ * @param[in] CodePage
+ * The code page the font has to support.
+ *
+ * @return
+ * @b TRUE if the font is valid and supported in the console,
+ * @b FALSE if not.
+ *
+ * @see IsValidConsoleFont2()
+ **/
BOOL
IsValidConsoleFont(
// _In_reads_or_z_(LF_FACESIZE)
@@ -460,7 +1025,7 @@ InitTTFontCache(VOID)
FontEntry = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*FontEntry));
if (!FontEntry)
{
- DPRINT1("InitTTFontCache: Failed to allocate memory, continuing...\n");
+ DBGFNT1("InitTTFontCache: Failed to allocate memory, continuing...\n");
continue;
}
diff --git a/win32ss/user/winsrv/concfg/font.h b/win32ss/user/winsrv/concfg/font.h
index 870b3805a63..6faa59dad33 100644
--- a/win32ss/user/winsrv/concfg/font.h
+++ b/win32ss/user/winsrv/concfg/font.h
@@ -1,10 +1,9 @@
/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS Console Server DLL
- * FILE: win32ss/user/winsrv/concfg/font.h
- * PURPOSE: Console Fonts Management
- * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
- * Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
+ * PROJECT: ReactOS Console Server DLL
+ * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Console GDI Fonts Management.
+ * COPYRIGHT: Copyright 2017-2022 Hermès Bélusca-Maïto
+ * Copyright 2017 Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
*/
#pragma once
@@ -13,6 +12,12 @@
#define INVALID_CP ((UINT)-1)
+#ifndef CP_UTF8
+#define CP_UTF8 65001
+#endif
+
+#define CP_USA 437 // United States (OEM)
+
#define CP_SHIFTJIS 932 // Japanese Shift-JIS
#define CP_HANGUL 949 // Korean Hangul/Wansung
#define CP_JOHAB 1361 // Korean Johab
@@ -70,15 +75,27 @@ BYTE
CodePageToCharSet(
_In_ UINT CodePage);
+// FIXME: Will be redefined once we support a font cache.
+typedef struct _FONT_DATA
+{
+ _Inout_updates_z_(LF_FACESIZE) PWSTR FaceName;
+ ULONG Weight;
+ ULONG Family;
+ COORD Size;
+ BYTE CharSet;
+} FONT_DATA, *PFONT_DATA;
+
HFONT
CreateConsoleFontEx(
_In_ LONG Height,
_In_opt_ LONG Width,
_Inout_updates_z_(LF_FACESIZE)
PWSTR FaceName,
- _In_ ULONG FontFamily,
_In_ ULONG FontWeight,
- _In_ UINT CodePage);
+ _In_ ULONG FontFamily,
+ _In_ UINT CodePage,
+ _In_ BOOL UseDefaultFallback,
+ _Out_ PFONT_DATA FontData);
HFONT
CreateConsoleFont2(
diff --git a/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c b/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
index a1d180d5f93..8b6f67c7e9d 100644
--- a/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
+++ b/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
@@ -3,7 +3,7 @@
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
* PURPOSE: GUI Console Window Class
- * PROGRAMMERS: G� van Geldorp
+ * PROGRAMMERS: Gé van Geldorp
* Johannes Anderwald
* Jeffrey Morlan
* Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
@@ -518,31 +518,54 @@ CreateDerivedFont(HFONT OrgFont,
}
BOOL
-InitFonts(PGUI_CONSOLE_DATA GuiData,
- LPWSTR FaceName, // Points to a WCHAR array of LF_FACESIZE elements.
- ULONG FontFamily,
- COORD FontSize,
- ULONG FontWeight)
+InitFonts(
+ _Inout_ PGUI_CONSOLE_DATA GuiData,
+ _In_reads_or_z_(LF_FACESIZE)
+ PCWSTR FaceName,
+ _In_ ULONG FontWeight,
+ _In_ ULONG FontFamily,
+ _In_ COORD FontSize,
+ _In_opt_ UINT CodePage,
+ _In_ BOOL UseDefaultFallback)
{
HDC hDC;
HFONT hFont;
+ FONT_DATA FontData;
+ UINT OldCharWidth = GuiData->CharWidth;
+ UINT OldCharHeight = GuiData->CharHeight;
+ COORD OldFontSize = GuiData->GuiInfo.FontSize;
+ WCHAR NewFaceName[LF_FACESIZE];
+
+ /* Default to current code page if none has been provided */
+ if (!CodePage)
+ CodePage = GuiData->Console->OutputCodePage;
/*
- * Initialize a new NORMAL font and get its character cell size.
+ * Initialize a new NORMAL font.
*/
+
+ /* Copy the requested face name into the local buffer.
+ * It will be modified in output by CreateConsoleFontEx()
+ * to hold a possible fallback font face name. */
+ StringCchCopyNW(NewFaceName, ARRAYSIZE(NewFaceName),
+ FaceName, LF_FACESIZE);
+
/* NOTE: FontSize is always in cell height/width units (pixels) */
hFont = CreateConsoleFontEx((LONG)(ULONG)FontSize.Y,
(LONG)(ULONG)FontSize.X,
- FaceName,
- FontFamily,
+ NewFaceName,
FontWeight,
- GuiData->Console->OutputCodePage);
- if (hFont == NULL)
+ FontFamily,
+ CodePage,
+ UseDefaultFallback,
+ &FontData);
+ if (!hFont)
{
- DPRINT1("InitFonts: CreateConsoleFontEx failed\n");
+ DPRINT1("InitFonts: CreateConsoleFontEx('%S') failed\n", NewFaceName);
return FALSE;
}
+ /* Retrieve its character cell size */
hDC = GetDC(GuiData->hWindow);
if (!GetFontCellSize(hDC, hFont, &GuiData->CharHeight, &GuiData->CharWidth))
{
@@ -561,35 +584,43 @@ InitFonts(PGUI_CONSOLE_DATA GuiData,
GuiData->Font[FONT_NORMAL] = hFont;
/*
- * Now build the other fonts (bold, underlined, mixed).
+ * Now build the optional fonts (bold, underlined, mixed).
+ * Do not error in case they fail to be created.
*/
GuiData->Font[FONT_BOLD] =
CreateDerivedFont(GuiData->Font[FONT_NORMAL],
- FontWeight < FW_BOLD ? FW_BOLD : FontWeight,
+ max(FW_BOLD, FontData.Weight),
FALSE,
FALSE);
GuiData->Font[FONT_UNDERLINE] =
CreateDerivedFont(GuiData->Font[FONT_NORMAL],
- FontWeight,
+ FontData.Weight,
TRUE,
FALSE);
GuiData->Font[FONT_BOLD | FONT_UNDERLINE] =
CreateDerivedFont(GuiData->Font[FONT_NORMAL],
- FontWeight < FW_BOLD ? FW_BOLD : FontWeight,
+ max(FW_BOLD, FontData.Weight),
TRUE,
FALSE);
/*
- * Save the settings.
+ * Save the new font characteristics.
*/
- if (FaceName != GuiData->GuiInfo.FaceName)
+ StringCchCopyNW(GuiData->GuiInfo.FaceName,
+ ARRAYSIZE(GuiData->GuiInfo.FaceName),
+ NewFaceName, ARRAYSIZE(NewFaceName));
+ GuiData->GuiInfo.FontWeight = FontData.Weight;
+ GuiData->GuiInfo.FontFamily = FontData.Family;
+ GuiData->GuiInfo.FontSize = FontData.Size;
+
+ /* Resize the terminal, in case the new font has a different size */
+ if ((OldCharWidth != GuiData->CharWidth) ||
+ (OldCharHeight != GuiData->CharHeight) ||
+ (OldFontSize.X != FontData.Size.X ||
+ OldFontSize.Y != FontData.Size.Y))
{
- StringCchCopyNW(GuiData->GuiInfo.FaceName, ARRAYSIZE(GuiData->GuiInfo.FaceName),
- FaceName, LF_FACESIZE);
+ TermResizeTerminal(GuiData->Console);
}
- GuiData->GuiInfo.FontFamily = FontFamily;
- GuiData->GuiInfo.FontSize = FontSize;
- GuiData->GuiInfo.FontWeight = FontWeight;
return TRUE;
}
@@ -615,14 +646,40 @@ OnNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
/* Initialize the fonts */
if (!InitFonts(GuiData,
GuiData->GuiInfo.FaceName,
+ GuiData->GuiInfo.FontWeight,
GuiData->GuiInfo.FontFamily,
GuiData->GuiInfo.FontSize,
- GuiData->GuiInfo.FontWeight))
+ 0, FALSE))
{
- DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
- GuiData->hWindow = NULL;
- NtSetEvent(GuiData->hGuiInitEvent, NULL);
- return FALSE;
+ /* Reset only the output code page if we don't have a suitable
+ * font for it, possibly falling back to "United States (OEM)". */
+ UINT AltCodePage = GetOEMCP();
+
+ if (AltCodePage == Console->OutputCodePage)
+ AltCodePage = CP_USA;
+
+ DPRINT1("Could not initialize font '%S' for code page %d - Resetting CP to %d\n",
+ GuiData->GuiInfo.FaceName, Console->OutputCodePage, AltCodePage);
+
+ CON_SET_OUTPUT_CP(Console, AltCodePage);
+
+ /* We will use a fallback font if we cannot find
+ * anything for this replacement code page. */
+ if (!InitFonts(GuiData,
+ GuiData->GuiInfo.FaceName,
+ GuiData->GuiInfo.FontWeight,
+ GuiData->GuiInfo.FontFamily,
+ GuiData->GuiInfo.FontSize,
+ 0, TRUE))
+ {
+ DPRINT1("Failed to initialize font '%S' for code page %d\n",
+ GuiData->GuiInfo.FaceName, Console->OutputCodePage);
+
+ DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
+ GuiData->hWindow = NULL;
+ NtSetEvent(GuiData->hGuiInitEvent, NULL);
+ return FALSE;
+ }
}
/* Initialize the terminal framebuffer */
diff --git a/win32ss/user/winsrv/consrv/frontends/gui/guisettings.c b/win32ss/user/winsrv/consrv/frontends/gui/guisettings.c
index 1ee89ed8a47..38dec0bbd75 100644
--- a/win32ss/user/winsrv/consrv/frontends/gui/guisettings.c
+++ b/win32ss/user/winsrv/consrv/frontends/gui/guisettings.c
@@ -163,9 +163,9 @@ GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData,
/* GUI Information */
StringCchCopyNW(pSharedInfo->FaceName, ARRAYSIZE(pSharedInfo->FaceName),
GuiData->GuiInfo.FaceName, ARRAYSIZE(GuiData->GuiInfo.FaceName));
+ pSharedInfo->FontWeight = GuiData->GuiInfo.FontWeight;
pSharedInfo->FontFamily = GuiData->GuiInfo.FontFamily;
pSharedInfo->FontSize = GuiData->GuiInfo.FontSize;
- pSharedInfo->FontWeight = GuiData->GuiInfo.FontWeight;
pSharedInfo->FullScreen = GuiData->GuiInfo.FullScreen;
pSharedInfo->AutoPosition = GuiData->GuiInfo.AutoPosition;
pSharedInfo->WindowPosition = GuiData->GuiInfo.WindowOrigin;
@@ -312,28 +312,29 @@ GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData,
// TODO: Check that GuiData->hWindow == pConInfo->hWnd
- /* Retrieve terminal informations */
-
/* Console information */
/*
* Apply the settings
*/
- /* Set the console informations */
+ /* Refresh the additional TrueType fonts cache now,
+ * as ConSrvApplyUserSettings() could change the output
+ * code page and trigger a font change in the terminal. */
+ RefreshTTFontCache();
+
+ /* Apply the generic console settings */
ConSrvApplyUserSettings(Console, pConInfo);
- /* Set the terminal informations */
+ /* Set the terminal settings */
- /* Refresh the additional TrueType fonts cache and change the font */
- RefreshTTFontCache();
+ /* Now, attempt to change the font to what the user specified */
InitFonts(GuiData,
pConInfo->FaceName,
+ pConInfo->FontWeight,
pConInfo->FontFamily,
pConInfo->FontSize,
- pConInfo->FontWeight);
- // HACK, needed because changing font may change the size of the window
- /**/TermResizeTerminal(Console);/**/
+ 0, FALSE);
/* Move the window to the user's values */
GuiData->GuiInfo.AutoPosition = !!pConInfo->AutoPosition;
diff --git a/win32ss/user/winsrv/consrv/frontends/gui/guisettings.h b/win32ss/user/winsrv/consrv/frontends/gui/guisettings.h
index e67c537ee8f..71e968a57a6 100644
--- a/win32ss/user/winsrv/consrv/frontends/gui/guisettings.h
+++ b/win32ss/user/winsrv/consrv/frontends/gui/guisettings.h
@@ -16,9 +16,9 @@
typedef struct _GUI_CONSOLE_INFO
{
WCHAR FaceName[LF_FACESIZE];
+ ULONG FontWeight;
ULONG FontFamily;
COORD FontSize;
- ULONG FontWeight;
BOOL FullScreen; /* Whether the console is displayed in full-screen or windowed mode */
// ULONG HardwareState; /* _GDI_MANAGED, _DIRECT */
diff --git a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
index 778b11073c1..0594ff4a0c8 100644
--- a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
+++ b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
@@ -3,7 +3,7 @@
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
* PURPOSE: GUI Terminal Front-End
- * PROGRAMMERS: G� van Geldorp
+ * PROGRAMMERS: Gé van Geldorp
* Johannes Anderwald
* Jeffrey Morlan
* Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
@@ -1038,9 +1038,28 @@ static BOOL NTAPI
GuiSetCodePage(IN OUT PFRONTEND This,
UINT CodePage)
{
- // TODO: Find a suitable console font for the given code page,
- // and set it if found; otherwise fail the call, or fall back
- // to some default font...
+ PGUI_CONSOLE_DATA GuiData = This->Context;
+
+ /*
+ * Attempt to reinitialize the current font for the new code page,
+ * trying to keep the current font with the same characteristics.
+ * If the current font does not support the new code page, switch
+ * to a different font supporting the code page but having similar
+ * characteristics.
+ * If no font can be found for this code page, stay using the
+ * original font and refuse changing the code page.
+ */
+ if (!InitFonts(GuiData,
+ GuiData->GuiInfo.FaceName,
+ GuiData->GuiInfo.FontWeight,
+ GuiData->GuiInfo.FontFamily,
+ GuiData->GuiInfo.FontSize,
+ CodePage, FALSE))
+ {
+ DPRINT1("Failed to initialize font '%S' for code page %d - Refuse CP change\n",
+ GuiData->GuiInfo.FaceName, CodePage);
+ return FALSE;
+ }
return TRUE;
}
diff --git a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.h b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.h
index 4ed6c1e8c19..e83f2a0cf96 100644
--- a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.h
+++ b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.h
@@ -3,7 +3,7 @@
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv/frontends/gui/guiterm.h
* PURPOSE: GUI Terminal Front-End
- * PROGRAMMERS: G� van Geldorp
+ * PROGRAMMERS: Gé van Geldorp
* Johannes Anderwald
* Jeffrey Morlan
* Hermes Belusca-Maito (hermes.belusca(a)sfr.fr)
@@ -65,11 +65,16 @@ GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData);
/* conwnd.c */
BOOL
-InitFonts(PGUI_CONSOLE_DATA GuiData,
- LPWSTR FaceName, // Points to a WCHAR array of LF_FACESIZE elements.
- ULONG FontFamily,
- COORD FontSize,
- ULONG FontWeight);
+InitFonts(
+ _Inout_ PGUI_CONSOLE_DATA GuiData,
+ _In_reads_or_z_(LF_FACESIZE)
+ PCWSTR FaceName,
+ _In_ ULONG FontWeight,
+ _In_ ULONG FontFamily,
+ _In_ COORD FontSize,
+ _In_opt_ UINT CodePage,
+ _In_ BOOL UseDefaultFallback);
+
VOID
DeleteFonts(PGUI_CONSOLE_DATA GuiData);
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3e44a5d71c6ef6f0d6662…
commit 3e44a5d71c6ef6f0d66625369f2633318a73a493
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Fri Jan 14 22:00:55 2022 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Tue Feb 8 15:58:02 2022 +0100
[KERNEL32][CONSRV] Retrieve the best-suited language ID corresponding to the active console output code page. (#4301)
CORE-17601, CORE-17803
Replaces PR #4281.
Implement SrvGetConsoleLangId() (server-side) and set the new current
thread's locale after connecting to a console, or changing its output
code page.
Based on API tracing on Windows 2003, as well as on comments and code
gathered from: https://github.com/microsoft/terminal
Tests results are listed in PR #4301.
---
dll/win32/kernel32/client/console/console.c | 57 +++++++++++++++++++++----
dll/win32/kernel32/client/console/init.c | 9 ++--
dll/win32/kernel32/include/console.h | 3 +-
sdk/include/reactos/subsys/win/conmsg.h | 7 ++++
win32ss/user/winsrv/concfg/font.h | 9 ++++
win32ss/user/winsrv/consrv/console.c | 64 +++++++++++++++++++++++++++--
6 files changed, 131 insertions(+), 18 deletions(-)
diff --git a/dll/win32/kernel32/client/console/console.c b/dll/win32/kernel32/client/console/console.c
index 5c687dfde1d..7a5b3b61c34 100644
--- a/dll/win32/kernel32/client/console/console.c
+++ b/dll/win32/kernel32/client/console/console.c
@@ -1375,8 +1375,6 @@ AllocConsole(VOID)
ULONG AppNameLength = 128 * sizeof(WCHAR);
ULONG CurDirLength = (MAX_PATH + 1) * sizeof(WCHAR);
- LCID lcid;
-
RtlEnterCriticalSection(&ConsoleLock);
if (NtCurrentPeb()->ProcessParameters->ConsoleHandle)
@@ -1427,8 +1425,8 @@ AllocConsole(VOID)
/* Initialize Console Ctrl Handling */
InitializeCtrlHandling();
- /* Sets the current console locale for this thread */
- SetTEBLangID(lcid);
+ /* Sync the current thread's LangId with the console's one */
+ SetTEBLangID();
}
Quit:
@@ -2500,6 +2498,9 @@ SetConsoleOutputCP(UINT wCodePageID)
return FALSE;
}
+ /* Sync the current thread's LangId with the console's one */
+ SetTEBLangID();
+
return TRUE;
}
@@ -2676,9 +2677,7 @@ AttachConsole(DWORD dwProcessId)
{
BOOL Success;
CONSOLE_START_INFO ConsoleStartInfo;
-
DWORD dummy;
- LCID lcid;
RtlEnterCriticalSection(&ConsoleLock);
@@ -2711,8 +2710,8 @@ AttachConsole(DWORD dwProcessId)
/* Initialize Console Ctrl Handling */
InitializeCtrlHandling();
- /* Sets the current console locale for this thread */
- SetTEBLangID(lcid);
+ /* Sync the current thread's LangId with the console's one */
+ SetTEBLangID();
}
Quit:
@@ -3067,6 +3066,48 @@ UnregisterConsoleIME(VOID)
return FALSE;
}
+/**
+ * @brief
+ * Internal helper function used to synchronize the current
+ * thread's language ID with the one from the console.
+ **/
+VOID
+SetTEBLangID(VOID)
+{
+ CONSOLE_API_MESSAGE ApiMessage;
+ PCONSOLE_GETLANGID LangIdRequest = &ApiMessage.Data.LangIdRequest;
+
+ /* Retrieve the "best-suited" language ID corresponding
+ * to the active console output code page. */
+ LangIdRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+
+ CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+ NULL,
+ CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepGetLangId),
+ sizeof(*LangIdRequest));
+ if (!NT_SUCCESS(ApiMessage.Status))
+ {
+ /*
+ * No best console language ID: keep the current thread's one.
+ * Since this internal function only modifies an optional setting,
+ * don't set any last error, as it could otherwise mess with the
+ * main last error set by the caller.
+ */
+ return;
+ }
+
+ /*
+ * We succeeded, set the current thread's language ID by
+ * modifying its locale -- Windows <= 2003 does not have
+ * the concept of a separate thread UI language.
+ * Ignore the returned value.
+ */
+ if (!SetThreadLocale(MAKELCID(LangIdRequest->LangId, SORT_DEFAULT)))
+ {
+ DPRINT1("SetTEBLangID: Could not set thread locale to console lang ID %lu\n",
+ LangIdRequest->LangId);
+ }
+}
static
BOOL
diff --git a/dll/win32/kernel32/client/console/init.c b/dll/win32/kernel32/client/console/init.c
index 3186bf4e71f..eb97ac73ded 100644
--- a/dll/win32/kernel32/client/console/init.c
+++ b/dll/win32/kernel32/client/console/init.c
@@ -342,14 +342,13 @@ ConDllInitialize(IN ULONG Reason,
PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
BOOLEAN InServerProcess = FALSE;
CONSRV_API_CONNECTINFO ConnectInfo;
- LCID lcid;
if (Reason != DLL_PROCESS_ATTACH)
{
if ((Reason == DLL_THREAD_ATTACH) && IsConsoleApp())
{
- /* Sets the current console locale for the new thread */
- SetTEBLangID(lcid);
+ /* Sync the new thread's LangId with the console's one */
+ SetTEBLangID();
}
else if (Reason == DLL_PROCESS_DETACH)
{
@@ -522,8 +521,8 @@ ConDllInitialize(IN ULONG Reason,
InputWaitHandle = ConnectInfo.ConsoleStartInfo.InputWaitHandle;
- /* Sets the current console locale for this thread */
- SetTEBLangID(lcid);
+ /* Sync the current thread's LangId with the console's one */
+ SetTEBLangID();
}
DPRINT("Console setup: 0x%p, 0x%p, 0x%p, 0x%p\n",
diff --git a/dll/win32/kernel32/include/console.h b/dll/win32/kernel32/include/console.h
index b6462af46b6..5fe84cb304c 100644
--- a/dll/win32/kernel32/include/console.h
+++ b/dll/win32/kernel32/include/console.h
@@ -60,7 +60,8 @@ GetConsoleInputWaitHandle(VOID);
HANDLE
TranslateStdHandle(HANDLE hHandle);
-#define SetTEBLangID(p) (p)
+VOID
+SetTEBLangID(VOID);
VOID
SetUpConsoleInfo(IN BOOLEAN CaptureTitle,
diff --git a/sdk/include/reactos/subsys/win/conmsg.h b/sdk/include/reactos/subsys/win/conmsg.h
index b96441dd9aa..dd50cb11437 100644
--- a/sdk/include/reactos/subsys/win/conmsg.h
+++ b/sdk/include/reactos/subsys/win/conmsg.h
@@ -859,6 +859,12 @@ typedef struct _CONSOLE_SETINPUTOUTPUTCP
HANDLE EventHandle;
} CONSOLE_SETINPUTOUTPUTCP, *PCONSOLE_SETINPUTOUTPUTCP;
+typedef struct _CONSOLE_GETLANGID
+{
+ HANDLE ConsoleHandle;
+ LANGID LangId;
+} CONSOLE_GETLANGID, *PCONSOLE_GETLANGID;
+
typedef struct _CONSOLE_GETKBDLAYOUTNAME
{
HANDLE ConsoleHandle;
@@ -991,6 +997,7 @@ typedef struct _CONSOLE_API_MESSAGE
/* Input and Output Code Pages; keyboard */
CONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest;
CONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest;
+ CONSOLE_GETLANGID LangIdRequest;
CONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest;
/* Virtual DOS Machine */
diff --git a/win32ss/user/winsrv/concfg/font.h b/win32ss/user/winsrv/concfg/font.h
index 15224b3f9d6..870b3805a63 100644
--- a/win32ss/user/winsrv/concfg/font.h
+++ b/win32ss/user/winsrv/concfg/font.h
@@ -19,6 +19,15 @@
#define CP_GB2312 936 // Chinese Simplified (GB2312)
#define CP_BIG5 950 // Chinese Traditional (Big5)
+/*
+ * "Human-understandable" names for the previous standard code pages.
+ * Taken from https://github.com/microsoft/terminal/blob/main/src/inc/unicode.hpp
+ */
+#define CP_JAPANESE CP_SHIFTJIS
+#define CP_KOREAN CP_HANGUL
+#define CP_CHINESE_SIMPLIFIED CP_GB2312
+#define CP_CHINESE_TRADITIONAL CP_BIG5
+
/* IsFarEastCP(CodePage) */
#define IsCJKCodePage(CodePage) \
((CodePage) == CP_SHIFTJIS || (CodePage) == CP_HANGUL || \
diff --git a/win32ss/user/winsrv/consrv/console.c b/win32ss/user/winsrv/consrv/console.c
index 1e35fdea127..cc269604ba7 100644
--- a/win32ss/user/winsrv/consrv/console.c
+++ b/win32ss/user/winsrv/consrv/console.c
@@ -16,7 +16,7 @@
#define COBJMACROS
#include <shlobj.h>
-
+#include "../concfg/font.h"
#include <alias.h>
#include <history.h>
#include "procinit.h"
@@ -2014,10 +2014,66 @@ CSR_API(SrvSetConsoleNlsMode)
}
/* API_NUMBER: ConsolepGetLangId */
-CSR_API(SrvGetConsoleLangId)
+CON_API(SrvGetConsoleLangId,
+ CONSOLE_GETLANGID, LangIdRequest)
{
- DPRINT1("%s not yet implemented\n", __FUNCTION__);
- return STATUS_NOT_IMPLEMENTED;
+ /*
+ * Quoting MS Terminal, see function GetConsoleLangId() at
+ * https://github.com/microsoft/terminal/blob/main/src/host/srvinit.cpp#L655
+ * "Only attempt to return the Lang ID if the Windows ACP on console
+ * launch was an East Asian Code Page."
+ *
+ * The underlying logic is as follows:
+ *
+ * - When the current user's UI language is *not* CJK, the user expects
+ * to not see any CJK output to the console by default, even if its
+ * output has been set to a CJK code page (this is possible when CJK
+ * fonts are installed on the system). That is, of course, unless if
+ * the attached console program chooses to actually output CJK text.
+ * Whatever current language of the running program's thread should
+ * be kept: STATUS_NOT_SUPPORTED is returned.
+ *
+ * - When the current user's UI language *is* CJK, the user expects to
+ * see CJK output to the console by default when its code page is CJK.
+ * A valid LangId is returned in this case to ensure this.
+ * However, if the console code page is not CJK, then it is evident
+ * that CJK text will not be able to be correctly shown, and therefore
+ * we should fall back to a standard language that can be shown, namely
+ * en-US english, instead of keeping the current language.
+ */
+
+ BYTE UserCharSet = CodePageToCharSet(GetACP());
+ if (!IsCJKCharSet(UserCharSet))
+ return STATUS_NOT_SUPPORTED;
+
+ /* Return a "best-suited" language ID corresponding
+ * to the active console output code page. */
+ switch (Console->OutputCodePage)
+ {
+/** ReactOS-specific: do nothing if the code page is UTF-8. This will allow
+ ** programs to naturally output in whatever current language they are. **/
+ case CP_UTF8:
+ return STATUS_NOT_SUPPORTED;
+/** End ReactOS-specific **/
+ case CP_JAPANESE:
+ LangIdRequest->LangId = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT);
+ break;
+ case CP_KOREAN:
+ LangIdRequest->LangId = MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
+ break;
+ case CP_CHINESE_SIMPLIFIED:
+ LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
+ break;
+ case CP_CHINESE_TRADITIONAL:
+ LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
+ break;
+ default:
+ /* Default to en-US english otherwise */
+ LangIdRequest->LangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
+ break;
+ }
+
+ return STATUS_SUCCESS;
}
/* EOF */