From 25b7447818f0fad1116070598bc88730c90c86b0 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 31 Oct 2023 22:37:49 +0900 Subject: [PATCH] [SETUPLIB][NTUSER] Toggle input language/layout on Alt+Shift / Ctrl+Shift (#5839) - Respect the toggle key settings. - Change the hot key settings in base/setup/lib/mui.c. - Revert IntDefWindowProc function about Alt+Shift handling. - Delete some code in co_IntProcessKeyboardMessage for Alt+Shift handling. - Add IntGetNextKL, IntLanguageToggle, and IntCheckLanguageToggle helper functions. - Modify ProcessKeyEvent and UserGetLanguageToggle functions to support [Left Alt]+Shift and Ctrl+Shift. - Improve WM_INPUTLANGCHANGEREQUEST handling. - Message handling shouldn't access kbswitch directly. CORE-10667 --- base/setup/lib/mui.c | 5 +- win32ss/user/ntuser/defwnd.c | 19 ---- win32ss/user/ntuser/input.h | 4 +- win32ss/user/ntuser/keyboard.c | 128 ++++++++++++++++++++++++++- win32ss/user/ntuser/misc.c | 18 ++-- win32ss/user/ntuser/msgqueue.c | 64 -------------- win32ss/user/ntuser/ntuser.h | 2 +- win32ss/user/ntuser/sysparams.c | 7 +- win32ss/user/user32/windows/defwnd.c | 27 +++--- 9 files changed, 164 insertions(+), 110 deletions(-) diff --git a/base/setup/lib/mui.c b/base/setup/lib/mui.c index 8b511cfa56a..cc2bda08eef 100644 --- a/base/setup/lib/mui.c +++ b/base/setup/lib/mui.c @@ -359,10 +359,7 @@ AddKbLayoutsToRegistry( uIndex++; } - if (uIndex > 1) - AddHotkeySettings(L"2", L"2", L"1"); - else - AddHotkeySettings(L"3", L"3", L"3"); + AddHotkeySettings(L"1", L"1", L"2"); NtClose(SubKeyHandle); NtClose(KeyHandle); diff --git a/win32ss/user/ntuser/defwnd.c b/win32ss/user/ntuser/defwnd.c index de4b4e12e18..83050064b8c 100644 --- a/win32ss/user/ntuser/defwnd.c +++ b/win32ss/user/ntuser/defwnd.c @@ -531,7 +531,6 @@ DefWndScreenshot(PWND pWnd) /* Win32k counterpart of User DefWindowProc */ -/* Win: xxxRealDefWindowProc */ LRESULT FASTCALL IntDefWindowProc( PWND Wnd, @@ -946,24 +945,6 @@ IntDefWindowProc( wParamTmp = UserGetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW; co_IntSendMessage( Active, WM_SYSCOMMAND, wParamTmp, wParam ); } - else if (wParam == VK_SHIFT) // Alt+Shift - { - RTL_ATOM ClassAtom = 0; - UNICODE_STRING ustrClass, ustrWindow; - HWND hwndSwitch; - - RtlInitUnicodeString(&ustrClass, L"kbswitcher"); - RtlInitUnicodeString(&ustrWindow, L""); - - IntGetAtomFromStringOrAtom(&ustrClass, &ClassAtom); - - hwndSwitch = IntFindWindow(UserGetDesktopWindow(), NULL, ClassAtom, &ustrWindow); - if (hwndSwitch) - { -#define ID_NEXTLAYOUT 10003 - UserPostMessage(hwndSwitch, WM_COMMAND, ID_NEXTLAYOUT, (LPARAM)UserHMGetHandle(Wnd)); - } - } } else if( wParam == VK_F10 ) { diff --git a/win32ss/user/ntuser/input.h b/win32ss/user/ntuser/input.h index 3cae36048f1..35105947d68 100644 --- a/win32ss/user/ntuser/input.h +++ b/win32ss/user/ntuser/input.h @@ -75,8 +75,10 @@ VOID NTAPI UserProcessKeyboardInput(PKEYBOARD_INPUT_DATA pKeyInput); BOOL NTAPI UserSendKeyboardInput(KEYBDINPUT *pKbdInput, BOOL bInjected); PKL NTAPI UserHklToKbl(HKL hKl); BOOL NTAPI UserSetDefaultInputLang(HKL hKl); -extern int gLanguageToggleKeyState; +extern INT gLanguageToggleKeyState; extern DWORD gdwLanguageToggleKey; +extern INT gLayoutToggleKeyState; +extern DWORD gdwLayoutToggleKey; /* Mouse */ WORD FASTCALL UserGetMouseButtonsState(VOID); diff --git a/win32ss/user/ntuser/keyboard.c b/win32ss/user/ntuser/keyboard.c index 61f59296be0..3d8bed1ad25 100644 --- a/win32ss/user/ntuser/keyboard.c +++ b/win32ss/user/ntuser/keyboard.c @@ -15,8 +15,10 @@ static BYTE gafAsyncKeyStateRecentDown[256 / 8]; // 1 bit per key static PKEYBOARD_INDICATOR_TRANSLATION gpKeyboardIndicatorTrans = NULL; static KEYBOARD_INDICATOR_PARAMETERS gIndicators = {0, 0}; KEYBOARD_ATTRIBUTES gKeyboardInfo; -int gLanguageToggleKeyState = 0; -DWORD gdwLanguageToggleKey = 0; +INT gLanguageToggleKeyState = 0; +DWORD gdwLanguageToggleKey = 1; +INT gLayoutToggleKeyState = 0; +DWORD gdwLayoutToggleKey = 2; /* FUNCTIONS *****************************************************************/ @@ -791,6 +793,92 @@ cleanup: UserReleaseDC(pWnd, hdc, FALSE); } +/* Find the next/previous keyboard layout of the same/different language */ +static PKL FASTCALL +IntGetNextKL( + _In_ PKL pKL, + _In_ BOOL bNext, + _In_ BOOL bSameLang) +{ + PKL pFirstKL = pKL; + LANGID LangID = LOWORD(pKL->hkl); + + do + { + pKL = (bNext ? pKL->pklNext : pKL->pklPrev); + + if (!(pKL->dwKL_Flags & KLF_UNLOAD) && bSameLang == (LangID == LOWORD(pKL->hkl))) + return pKL; + } while (pKL != pFirstKL); + + return pFirstKL; +} + +/* Perform layout toggle by [Left Alt]+Shift or Ctrl+Shift */ +static VOID +IntLanguageToggle( + _In_ PUSER_MESSAGE_QUEUE pFocusQueue, + _In_ BOOL bSameLang, + _In_ INT nKeyState) +{ + PWND pWnd = pFocusQueue->spwndFocus; + HWND hWnd; + WPARAM wParam = 0; + PTHREADINFO pti; + PKL pkl; + + if (!pWnd) + pWnd = pFocusQueue->spwndActive; + if (!pWnd) + return; + + pti = pWnd->head.pti; + pkl = pti->KeyboardLayout; + + if (nKeyState == INPUTLANGCHANGE_FORWARD) + pkl = IntGetNextKL(pkl, TRUE, bSameLang); + else if (nKeyState == INPUTLANGCHANGE_BACKWARD) + pkl = IntGetNextKL(pkl, FALSE, bSameLang); + + if (gSystemFS & pkl->dwFontSigs) + wParam |= INPUTLANGCHANGE_SYSCHARSET; + + hWnd = UserHMGetHandle(pWnd); + UserPostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, wParam, (LPARAM)pkl->hkl); +} + +/* Check Language Toggle by [Left Alt]+Shift or Ctrl+Shift */ +static BOOL +IntCheckLanguageToggle( + _In_ PUSER_MESSAGE_QUEUE pFocusQueue, + _In_ BOOL bIsDown, + _In_ WORD wVk, + _Inout_ PINT pKeyState) +{ + if (bIsDown) /* Toggle key combination is pressed? */ + { + if (wVk == VK_LSHIFT) + *pKeyState = INPUTLANGCHANGE_FORWARD; + else if (wVk == VK_RSHIFT) + *pKeyState = INPUTLANGCHANGE_BACKWARD; + else if (!wVk && IS_KEY_DOWN(gafAsyncKeyState, VK_LSHIFT)) + *pKeyState = INPUTLANGCHANGE_FORWARD; + else if (!wVk && IS_KEY_DOWN(gafAsyncKeyState, VK_RSHIFT)) + *pKeyState = INPUTLANGCHANGE_BACKWARD; + else + return FALSE; + } + else + { + if (*pKeyState == 0) + return FALSE; + + IntLanguageToggle(pFocusQueue, (pKeyState == &gLayoutToggleKeyState), *pKeyState); + *pKeyState = 0; + } + return TRUE; +} + /* * UserSendKeyboardInput * @@ -808,6 +896,7 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d BOOL bWasSimpleDown = FALSE, bPostMsg = TRUE, bIsSimpleDown; MSG Msg; static BOOL bMenuDownRecently = FALSE; + BOOL bLangToggled = FALSE; /* Get virtual key without shifts (VK_(L|R)* -> VK_*) */ wSimpleVk = IntSimplifyVk(wVk); @@ -906,6 +995,41 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk); } + /* + * Check Language/Layout Toggle by [Left Alt]+Shift or Ctrl+Shift. + * @see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976564%28v=technet.10%29 + */ + if (gdwLanguageToggleKey == 1 || gdwLanguageToggleKey == 2) + { + if (wSimpleVk == VK_SHIFT) /* Shift key is pressed or released */ + { + UINT targetKey = ((gdwLanguageToggleKey == 1) ? VK_LMENU : VK_CONTROL); + if (IS_KEY_DOWN(gafAsyncKeyState, targetKey)) + bLangToggled = IntCheckLanguageToggle(pFocusQueue, bIsDown, wVk, &gLanguageToggleKeyState); + } + else if ((wSimpleVk == VK_MENU && gdwLanguageToggleKey == 1) || + (wSimpleVk == VK_CONTROL && gdwLanguageToggleKey == 2)) + { + if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT)) + bLangToggled = IntCheckLanguageToggle(pFocusQueue, bIsDown, 0, &gLanguageToggleKeyState); + } + } + if (!bLangToggled && (gdwLayoutToggleKey == 1 || gdwLayoutToggleKey == 2)) + { + if (wSimpleVk == VK_SHIFT) /* Shift key is pressed or released */ + { + UINT targetKey = ((gdwLayoutToggleKey == 1) ? VK_LMENU : VK_CONTROL); + if (IS_KEY_DOWN(gafAsyncKeyState, targetKey)) + IntCheckLanguageToggle(pFocusQueue, bIsDown, wVk, &gLayoutToggleKeyState); + } + else if ((wSimpleVk == VK_MENU && gdwLayoutToggleKey == 1) || + (wSimpleVk == VK_CONTROL && gdwLayoutToggleKey == 2)) + { + if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT)) + IntCheckLanguageToggle(pFocusQueue, bIsDown, 0, &gLayoutToggleKeyState); + } + } + if (bIsDown && wVk == VK_SNAPSHOT) { if (pFocusQueue && diff --git a/win32ss/user/ntuser/misc.c b/win32ss/user/ntuser/misc.c index ae458de7a42..b5b0f27ac0a 100644 --- a/win32ss/user/ntuser/misc.c +++ b/win32ss/user/ntuser/misc.c @@ -76,19 +76,27 @@ IntTID2PTI(HANDLE id) return pti; } +/** + * @see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976564%28v=technet.10%29 + */ DWORD FASTCALL -UserGetLanguageToggle(VOID) +UserGetLanguageToggle( + _In_ PCWSTR pszType, + _In_ DWORD dwDefaultValue) { NTSTATUS Status; - DWORD dwValue = 0; + DWORD dwValue = dwDefaultValue; + WCHAR szBuff[4]; - Status = RegReadUserSetting(L"Keyboard Layout\\Toggle", L"Layout Hotkey", REG_SZ, &dwValue, sizeof(dwValue)); + Status = RegReadUserSetting(L"Keyboard Layout\\Toggle", pszType, REG_SZ, szBuff, sizeof(szBuff)); if (NT_SUCCESS(Status)) { - dwValue = atoi((char *)&dwValue); - TRACE("Layout Hotkey %d\n",dwValue); + szBuff[RTL_NUMBER_OF(szBuff) - 1] = UNICODE_NULL; + dwValue = _wtoi(szBuff); } + + TRACE("%ls: %lu\n", pszType, dwValue); return dwValue; } diff --git a/win32ss/user/ntuser/msgqueue.c b/win32ss/user/ntuser/msgqueue.c index 7f308530749..a808073374d 100644 --- a/win32ss/user/ntuser/msgqueue.c +++ b/win32ss/user/ntuser/msgqueue.c @@ -1770,7 +1770,6 @@ BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages) PWND pWnd; UINT ImmRet; BOOL Ret = TRUE; - WPARAM wParam = Msg->wParam; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); if (Msg->message == VK_PACKET) @@ -1852,69 +1851,6 @@ BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages) } } - if ( *RemoveMessages && (Msg->message == WM_SYSKEYDOWN || Msg->message == WM_KEYDOWN) ) - { - if (gdwLanguageToggleKey < 3) - { - if (IS_KEY_DOWN(gafAsyncKeyState, gdwLanguageToggleKey == 1 ? VK_LMENU : VK_CONTROL)) // L Alt 1 or Ctrl 2 . - { - if ( wParam == VK_LSHIFT ) gLanguageToggleKeyState = INPUTLANGCHANGE_FORWARD; // Left Alt - Left Shift, Next - //// FIXME : It seems to always be VK_LSHIFT. - if ( wParam == VK_RSHIFT ) gLanguageToggleKeyState = INPUTLANGCHANGE_BACKWARD; // Left Alt - Right Shift, Previous - } - } - } - - //// Key Up! Alt Key Ctrl Key - if ( *RemoveMessages && (Msg->message == WM_SYSKEYUP || Msg->message == WM_KEYUP) ) - { - // When initializing win32k: Reading from the registry hotkey combination - // to switch the keyboard layout and store it to global variable. - // Using this combination of hotkeys in this function - - if ( gdwLanguageToggleKey < 3 && - IS_KEY_DOWN(gafAsyncKeyState, gdwLanguageToggleKey == 1 ? VK_LMENU : VK_CONTROL) ) - { - if ( Msg->wParam == VK_SHIFT && !(IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))) - { - WPARAM wParamILR; - PKL pkl = pti->KeyboardLayout; - - if (pWnd) UserDerefObjectCo(pWnd); - - //// Seems to override message window. - if (!(pWnd = pti->MessageQueue->spwndFocus)) - { - pWnd = pti->MessageQueue->spwndActive; - } - if (pWnd) UserRefObjectCo(pWnd, &Ref); - - if (pkl != NULL && gLanguageToggleKeyState) - { - TRACE("Posting WM_INPUTLANGCHANGEREQUEST KeyState %d\n", gLanguageToggleKeyState ); - - wParamILR = gLanguageToggleKeyState; - // If system character set and font signature send flag. - if ( gSystemFS & pkl->dwFontSigs ) - { - wParamILR |= INPUTLANGCHANGE_SYSCHARSET; - } - - UserPostMessage( UserHMGetHandle(pWnd), - WM_INPUTLANGCHANGEREQUEST, - wParamILR, - (LPARAM)pkl->hkl ); - - gLanguageToggleKeyState = 0; - //// Keep looping. - Ret = FALSE; - //// Skip the rest. - goto Exit; - } - } - } - } - if (co_HOOK_CallHooks( WH_KEYBOARD, *RemoveMessages ? HC_ACTION : HC_NOREMOVE, LOWORD(Msg->wParam), diff --git a/win32ss/user/ntuser/ntuser.h b/win32ss/user/ntuser/ntuser.h index 01232f9323a..7c878c4f793 100644 --- a/win32ss/user/ntuser/ntuser.h +++ b/win32ss/user/ntuser/ntuser.h @@ -37,7 +37,7 @@ VOID FASTCALL UserEnterExclusive(VOID); VOID FASTCALL UserLeave(VOID); BOOL FASTCALL UserIsEntered(VOID); BOOL FASTCALL UserIsEnteredExclusive(VOID); -DWORD FASTCALL UserGetLanguageToggle(VOID); +DWORD FASTCALL UserGetLanguageToggle(_In_ LPCWSTR pszType, _In_ DWORD dwDefaultValue); _Success_(return != FALSE) BOOL diff --git a/win32ss/user/ntuser/sysparams.c b/win32ss/user/ntuser/sysparams.c index 552a63e1f6a..95bfbe7ca78 100644 --- a/win32ss/user/ntuser/sysparams.c +++ b/win32ss/user/ntuser/sysparams.c @@ -355,7 +355,8 @@ SpiUpdatePerUserSystemParameters(VOID) if (SPITESTPREF(UPM_COMBOBOXANIMATION)) gpsi->PUSIFlags |= PUSIF_COMBOBOXANIMATION; if (SPITESTPREF(UPM_LISTBOXSMOOTHSCROLLING)) gpsi->PUSIFlags |= PUSIF_LISTBOXSMOOTHSCROLLING; } - gdwLanguageToggleKey = UserGetLanguageToggle(); + gdwLanguageToggleKey = UserGetLanguageToggle(L"Language Hotkey", 1); + gdwLayoutToggleKey = UserGetLanguageToggle(L"Layout Hotkey", 2); g_bWindowSnapEnabled = IntIsWindowSnapEnabled(); } @@ -1470,9 +1471,9 @@ SpiGetSet(UINT uiAction, UINT uiParam, PVOID pvParam, FLONG fl) } case SPI_SETLANGTOGGLE: - gdwLanguageToggleKey = UserGetLanguageToggle(); + gdwLayoutToggleKey = UserGetLanguageToggle(L"Layout Hotkey", 2); + gdwLanguageToggleKey = UserGetLanguageToggle(L"Language Hotkey", 1); return gdwLanguageToggleKey; - break; case SPI_GETWINDOWSEXTENSION: ERR("SPI_GETWINDOWSEXTENSION is unimplemented\n"); diff --git a/win32ss/user/user32/windows/defwnd.c b/win32ss/user/user32/windows/defwnd.c index cbb421209dc..dbc5b43e9ff 100644 --- a/win32ss/user/user32/windows/defwnd.c +++ b/win32ss/user/user32/windows/defwnd.c @@ -547,22 +547,27 @@ User32DefWindowProc(HWND hWnd, case WM_INPUTLANGCHANGEREQUEST: { - HKL NewHkl; + HKL hNewKL; + HWND hwndFocus; - if(wParam & INPUTLANGCHANGE_BACKWARD - && wParam & INPUTLANGCHANGE_FORWARD) - { + if ((wParam & INPUTLANGCHANGE_BACKWARD) && (wParam & INPUTLANGCHANGE_FORWARD)) return FALSE; + + hwndFocus = GetFocus(); + if (hwndFocus && hwndFocus != hWnd && + GetClassLongPtrW(hWnd, GCW_ATOM) != (ULONG_PTR)WC_DIALOG) + { + return SendMessageW(hwndFocus, Msg, wParam, lParam); } - //FIXME: What to do with INPUTLANGCHANGE_SYSCHARSET ? - - if(wParam & INPUTLANGCHANGE_BACKWARD) NewHkl = (HKL) HKL_PREV; - else if(wParam & INPUTLANGCHANGE_FORWARD) NewHkl = (HKL) HKL_NEXT; - else NewHkl = (HKL) lParam; - - NtUserActivateKeyboardLayout(NewHkl, KLF_SETFORPROCESS); + if (wParam & INPUTLANGCHANGE_FORWARD) + hNewKL = (HKL)UlongToHandle(HKL_NEXT); + else if (wParam & INPUTLANGCHANGE_BACKWARD) + hNewKL = (HKL)UlongToHandle(HKL_PREV); + else + hNewKL = (HKL)lParam; + NtUserActivateKeyboardLayout(hNewKL, KLF_SETFORPROCESS); return TRUE; }