https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3e44a5d71c6ef6f0d66625...
commit 3e44a5d71c6ef6f0d66625369f2633318a73a493 Author: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org AuthorDate: Fri Jan 14 22:00:55 2022 +0100 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@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 */