[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
This commit is contained in:
Katayama Hirofumi MZ 2023-10-31 22:37:49 +09:00 committed by GitHub
parent 8a049d0b68
commit 25b7447818
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 164 additions and 110 deletions

View file

@ -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);

View file

@ -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 )
{

View file

@ -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);

View file

@ -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 &&

View file

@ -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;
}

View file

@ -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),

View file

@ -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

View file

@ -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");

View file

@ -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;
}