Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

646 lines
20 KiB

/**************************************************************************\
* Module Name: immhotky.c (user32 side IME hotkey handling)
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* IME hot key management routines for imm32 dll
*
* History:
* 03-Jan-1996 hiroyama Created
\**************************************************************************/
#include "precomp.h"
#pragma hdrstop
typedef struct tagFE_KEYBOARDS {
BOOLEAN fJPN : 1;
BOOLEAN fCHT : 1;
BOOLEAN fCHS : 1;
BOOLEAN fKOR : 1;
} FE_KEYBOARDS;
//
// internal functions
//
BOOL CliSaveImeHotKey(DWORD dwID, UINT uModifiers, UINT uVKey, HKL hkl, BOOL fDelete);
BOOL CliImmSetHotKeyWorker(DWORD dwID, UINT uModifiers, UINT uVKey, HKL hkl, DWORD dwAction);
VOID NumToHexAscii(DWORD, PTSTR);
BOOL CliGetImeHotKeysFromRegistry(void);
BOOL CliSetSingleHotKey(PKEY_BASIC_INFORMATION pKeyInfo, HANDLE hKey);
VOID CliSetDefaultImeHotKeys(PCIMEHOTKEY ph, INT num, BOOL fCheckExistingHotKey);
VOID CliGetPreloadKeyboardLayouts(FE_KEYBOARDS* pFeKbds);
//
// IMM hotkey related registry keys under HKEY_CURRENT_USER
//
CONST TCHAR *szaRegImmHotKeys[] = {
TEXT("Control Panel"),
TEXT("Input Method"),
TEXT("Hot Keys"),
NULL
};
CONST TCHAR szRegImeHotKey[] = TEXT("Control Panel\\Input Method\\Hot Keys");
CONST TCHAR szRegKeyboardPreload[] = TEXT("Keyboard Layout\\Preload");
CONST TCHAR szRegVK[] = TEXT("Virtual Key");
CONST TCHAR szRegMOD[] = TEXT("Key Modifiers");
CONST TCHAR szRegHKL[] = TEXT("Target IME");
//
// Default IME HotKey Tables
//
// CR:takaok - move this to the resource if you have time
//
CONST IMEHOTKEY DefaultHotKeyTableJ[]= {
{IME_JHOTKEY_CLOSE_OPEN, VK_KANJI, MOD_IGNORE_ALL_MODIFIER, NULL}
};
CONST INT DefaultHotKeyNumJ = sizeof(DefaultHotKeyTableJ) / sizeof(IMEHOTKEY);
CONST IMEHOTKEY DefaultHotKeyTableT[] = {
{ IME_THOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_BOTH_SIDES|MOD_CONTROL, NULL },
{ IME_THOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_BOTH_SIDES|MOD_SHIFT, NULL }
};
CONST INT DefaultHotKeyNumT = sizeof(DefaultHotKeyTableT) / sizeof(IMEHOTKEY);
CONST IMEHOTKEY DefaultHotKeyTableC[] = {
{ IME_CHOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_BOTH_SIDES|MOD_CONTROL, NULL },
{ IME_CHOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_BOTH_SIDES|MOD_SHIFT, NULL }
};
CONST INT DefaultHotKeyNumC = sizeof(DefaultHotKeyTableC) / sizeof(IMEHOTKEY);
#if 0 // just FYI.
CONST IMEHOTKEY DefaultHotKeyTableK[] = {
{ IME_KHOTKEY_ENGLISH, VK_HANGEUL, MOD_IGNORE_ALL_MODIFIER, NULL },
{ IME_KHOTKEY_SHAPE_TOGGLE, VK_JUNJA, MOD_IGNORE_ALL_MODIFIER, NULL },
{ IME_KHOTKEY_HANJACONVERT, VK_HANJA, MOD_IGNORE_ALL_MODIFIER, NULL }
};
CONST INT DefaultHotKeyNumK = sizeof(DefaultHotKeyTableK) / sizeof(IMEHOTKEY);
#endif
//
// Set language flags.
//
VOID SetFeKeyboardFlags(LANGID langid, FE_KEYBOARDS* pFeKbds)
{
switch (langid) {
case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL):
case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG):
pFeKbds->fCHT = TRUE;
break;
case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED):
case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE):
pFeKbds->fCHS = TRUE;
break;
case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT):
pFeKbds->fJPN = TRUE;
break;
case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT):
pFeKbds->fKOR = TRUE;
break;
}
}
/***************************************************************************\
* ImmInitializeHotkeys()
*
* Called from user\client\UpdatePerUserSystemParameters()
*
* Read the User registry and set the IME hotkey.
*
* History:
* 25-Mar-1996 TakaoK Created
\***************************************************************************/
VOID CliImmInitializeHotKeys(DWORD dwAction, HKL hkl)
{
FE_KEYBOARDS feKbds = { 0, 0, 0, 0, };
BOOL fFoundAny;
UNREFERENCED_PARAMETER(hkl);
// First, initialize the hotkey list
CliImmSetHotKeyWorker(0, 0, 0, NULL, ISHK_INITIALIZE);
// Check if the user has customized IME hotkeys
// (they're stored in the registry)
fFoundAny = CliGetImeHotKeysFromRegistry();
if (dwAction == ISHK_INITIALIZE) {
TAGMSG0(DBGTAG_IMM, "Setting IME HotKeys for Init.\n");
// Get the user's default locale and set its flag
SetFeKeyboardFlags(LANGIDFROMLCID(GetUserDefaultLCID()), &feKbds);
// Get preloaded keyboards' locales and set their flags
CliGetPreloadKeyboardLayouts(&feKbds);
}
else {
UINT i;
UINT nLayouts;
LPHKL lphkl;
TAGMSG0(DBGTAG_IMM, "Setting IME HotKeys for Add.\n");
nLayouts = NtUserGetKeyboardLayoutList(0, NULL);
if (nLayouts == 0) {
return;
}
lphkl = UserLocalAlloc(0, nLayouts * sizeof(HKL));
if (lphkl == NULL) {
return;
}
NtUserGetKeyboardLayoutList(nLayouts, lphkl);
for (i = 0; i < nLayouts; ++i) {
//
// Set language flags. By its definition, LOWORD(hkl) is LANGID
//
SetFeKeyboardFlags(LOWORD(HandleToUlong(lphkl[i])), &feKbds);
}
UserLocalFree(lphkl);
}
if (feKbds.fJPN) {
TAGMSG0(DBGTAG_IMM, "JPN KL Preloaded.\n");
CliSetDefaultImeHotKeys(DefaultHotKeyTableJ, DefaultHotKeyNumJ, fFoundAny);
}
if (feKbds.fKOR) {
TAGMSG0(DBGTAG_IMM, "KOR KL Preloaded, but KOR hotkeys will not be registered.\n");
}
if (feKbds.fCHT) {
TAGMSG0(DBGTAG_IMM, "CHT KL Preloaded.\n");
CliSetDefaultImeHotKeys(DefaultHotKeyTableT, DefaultHotKeyNumT, fFoundAny);
}
if (feKbds.fCHS) {
TAGMSG0(DBGTAG_IMM, "CHS KL Preloaded.\n");
CliSetDefaultImeHotKeys(DefaultHotKeyTableC, DefaultHotKeyNumC, fFoundAny);
}
}
VOID CliSetDefaultImeHotKeys(PCIMEHOTKEY ph, INT num, BOOL fNeedToCheckExistingHotKey)
{
IMEHOTKEY hkt;
while( num-- > 0 ) {
//
// Set IME hotkey only if there is no such
// hotkey in the registry
//
if (!fNeedToCheckExistingHotKey ||
!NtUserGetImeHotKey(ph->dwHotKeyID, &hkt.uModifiers, &hkt.uVKey, &hkt.hKL)) {
CliImmSetHotKeyWorker(ph->dwHotKeyID,
ph->uModifiers,
ph->uVKey,
ph->hKL,
ISHK_ADD);
}
ph++;
}
}
/***************************************************************************\
* CliGetPreloadKeyboardLayouts()
*
* Read the User registry and enumerate values in Keyboard Layouts\Preload
* to see which FE languages are to be preloaded.
*
* History:
* 03-Dec-1997 Hiroyama Created
\***************************************************************************/
VOID CliGetPreloadKeyboardLayouts(FE_KEYBOARDS* pFeKbds)
{
UINT i;
WCHAR szPreLoadee[4]; // up to 999 preloads
WCHAR lpszName[KL_NAMELENGTH];
UNICODE_STRING UnicodeString;
HKL hkl;
for (i = 1; i < 1000; i++) {
wsprintf(szPreLoadee, L"%d", i);
if ((GetPrivateProfileStringW(
L"Preload",
szPreLoadee,
L"", // default = NULL
lpszName, // output buffer
KL_NAMELENGTH,
L"keyboardlayout.ini") == -1 ) || (*lpszName == L'\0')) {
break;
}
RtlInitUnicodeString(&UnicodeString, lpszName);
RtlUnicodeStringToInteger(&UnicodeString, 16L, (PULONG)&hkl);
RIPMSG2(RIP_VERBOSE, "PreLoaded HKL(%d): %08X\n", i, hkl);
//
// Set language flags. By its definition, LOWORD(hkl) is LANGID
//
SetFeKeyboardFlags(LOWORD(HandleToUlong(hkl)), pFeKbds);
}
}
BOOL CliGetImeHotKeysFromRegistry()
{
BOOL fFoundAny = FALSE;
HANDLE hCurrentUserKey;
HANDLE hKeyHotKeys;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING SubKeyName;
NTSTATUS Status;
ULONG uIndex;
//
// Open the current user registry key
//
Status = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &hCurrentUserKey);
if (!NT_SUCCESS(Status)) {
return fFoundAny;
}
RtlInitUnicodeString( &SubKeyName, szRegImeHotKey );
InitializeObjectAttributes( &Obja,
&SubKeyName,
OBJ_CASE_INSENSITIVE,
hCurrentUserKey,
NULL);
Status = NtOpenKey( &hKeyHotKeys, KEY_READ, &Obja );
if (!NT_SUCCESS(Status)) {
NtClose( hCurrentUserKey );
return fFoundAny;
}
for (uIndex = 0; TRUE; uIndex++) {
BYTE KeyBuffer[sizeof(KEY_BASIC_INFORMATION) + 16 * sizeof(WCHAR)];
PKEY_BASIC_INFORMATION pKeyInfo;
ULONG ResultLength;
pKeyInfo = (PKEY_BASIC_INFORMATION)KeyBuffer;
Status = NtEnumerateKey(hKeyHotKeys,
uIndex,
KeyBasicInformation,
pKeyInfo,
sizeof( KeyBuffer ),
&ResultLength );
if (NT_SUCCESS(Status)) {
if (CliSetSingleHotKey(pKeyInfo, hKeyHotKeys)) {
fFoundAny = TRUE;
}
} else if (Status == STATUS_NO_MORE_ENTRIES) {
break;
}
}
NtClose(hKeyHotKeys);
NtClose(hCurrentUserKey);
return fFoundAny;
}
DWORD CliReadRegistryValue(HANDLE hKey, PCWSTR pName)
{
BYTE ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 16 * sizeof(UCHAR)];
PKEY_VALUE_PARTIAL_INFORMATION pKeyValue;
UNICODE_STRING ValueName;
ULONG ResultLength;
NTSTATUS Status;
pKeyValue = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
RtlInitUnicodeString(&ValueName, pName);
Status = NtQueryValueKey(hKey,
&ValueName,
KeyValuePartialInformation,
pKeyValue,
sizeof(ValueBuffer),
&ResultLength );
if (NT_SUCCESS(Status) && pKeyValue->DataLength > 3) {
//
// In Win95 registry, these items are written as BYTE data...
//
return (DWORD)(MAKEWORD( pKeyValue->Data[0], pKeyValue->Data[1])) |
(((DWORD)(MAKEWORD( pKeyValue->Data[2], pKeyValue->Data[3]))) << 16);
}
return 0;
}
BOOL CliSetSingleHotKey(PKEY_BASIC_INFORMATION pKeyInfo, HANDLE hKey)
{
UNICODE_STRING SubKeyName;
HANDLE hKeySingleHotKey;
OBJECT_ATTRIBUTES Obja;
DWORD dwID = 0;
UINT uVKey = 0;
UINT uModifiers = 0;
HKL hKL = NULL;
NTSTATUS Status;
SubKeyName.Buffer = (PWSTR)&(pKeyInfo->Name[0]);
SubKeyName.Length = (USHORT)pKeyInfo->NameLength;
SubKeyName.MaximumLength = (USHORT)pKeyInfo->NameLength;
InitializeObjectAttributes(&Obja,
&SubKeyName,
OBJ_CASE_INSENSITIVE,
hKey,
NULL);
Status = NtOpenKey(&hKeySingleHotKey, KEY_READ, &Obja);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
RtlUnicodeStringToInteger(&SubKeyName, 16L, &dwID);
uVKey = CliReadRegistryValue(hKeySingleHotKey, szRegVK);
uModifiers = CliReadRegistryValue(hKeySingleHotKey, szRegMOD);
hKL = (HKL)LongToHandle( CliReadRegistryValue(hKeySingleHotKey, szRegHKL) );
NtClose(hKeySingleHotKey);
return CliImmSetHotKeyWorker(dwID, uModifiers, uVKey, hKL, ISHK_ADD);
}
/***************************************************************************\
* ImmSetHotKey()
*
* Private API for IMEs and the control panel.
*
* History:
* 25-Mar-1996 TakaoK Created
\***************************************************************************/
FUNCLOG4(LOG_GENERAL, BOOL, WINAPI, CliImmSetHotKey, DWORD, dwID, UINT, uModifiers, UINT, uVKey, HKL, hkl)
BOOL WINAPI CliImmSetHotKey(
DWORD dwID,
UINT uModifiers,
UINT uVKey,
HKL hkl)
{
BOOL fResult;
BOOL fTmp;
BOOL fDelete = (uVKey == 0 );
if (fDelete) {
//
// Removing an IME hotkey from the list in the kernel side
// should not be failed, if we succeed to remove the IME
// hotkey entry from the registry. Therefore CliSaveImeHotKey
// is called first.
//
fResult = CliSaveImeHotKey( dwID, uModifiers, uVKey, hkl, fDelete );
if (fResult) {
fTmp = CliImmSetHotKeyWorker( dwID, uModifiers, uVKey, hkl, ISHK_REMOVE );
UserAssert(fTmp);
}
} else {
//
// CliImmSetHotKeyWorker should be called first since
// adding an IME hotkey into the list in the kernel side
// will be failed in various reasons.
//
fResult = CliImmSetHotKeyWorker(dwID, uModifiers, uVKey, hkl, ISHK_ADD);
if (fResult) {
fResult = CliSaveImeHotKey(dwID, uModifiers, uVKey, hkl, fDelete);
if (!fResult) {
//
// We failed to save the hotkey to the registry.
// We need to remove the entry from the IME hotkey
// list in the kernel side.
//
fTmp = CliImmSetHotKeyWorker(dwID, uModifiers, uVKey, hkl, ISHK_REMOVE);
UserAssert(fTmp);
}
}
}
return fResult;
}
/***************************************************************************\
* CliSaveImeHotKey()
*
* Put/Remove the specified IME hotkey entry from the registry
*
* History:
* 25-Mar-1996 TakaoK Created
\***************************************************************************/
BOOL CliSaveImeHotKey(DWORD id, UINT mod, UINT vk, HKL hkl, BOOL fDelete)
{
HKEY hKey, hKeyParent;
INT i;
LONG lResult;
TCHAR szHex[16];
if (fDelete) {
TCHAR szRegTmp[(sizeof(szRegImeHotKey) / sizeof(TCHAR) + 1 + 8 + 1)];
lstrcpy(szRegTmp, szRegImeHotKey);
lstrcat(szRegTmp, TEXT("\\"));
NumToHexAscii(id, szHex);
lstrcat(szRegTmp, szHex);
lResult = RegDeleteKeyW(HKEY_CURRENT_USER, szRegTmp);
if (lResult != ERROR_SUCCESS) {
RIPERR1(lResult, RIP_WARNING,
"CliSaveImeHotKey: deleting %s failed", szRegTmp);
return FALSE;
}
return TRUE;
}
hKeyParent = HKEY_CURRENT_USER;
for (i = 0; szaRegImmHotKeys[i] != NULL; i++) {
lResult = RegCreateKeyExW(hKeyParent,
szaRegImmHotKeys[i],
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE|KEY_READ,
NULL,
&hKey,
NULL );
RegCloseKey(hKeyParent);
if (lResult == ERROR_SUCCESS) {
hKeyParent = hKey;
} else {
RIPERR1(lResult, RIP_WARNING,
"CliSaveImeHotKey: creating %s failed", szaRegImmHotKeys[i]);
return FALSE;
}
}
NumToHexAscii(id, szHex);
lResult = RegCreateKeyExW(hKeyParent,
szHex,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE|KEY_READ,
NULL,
&hKey,
NULL );
RegCloseKey(hKeyParent);
if (lResult != ERROR_SUCCESS) {
RIPERR1(lResult, RIP_WARNING,
"CliSaveImeHotKey: creating %s failed", szHex );
return FALSE;
}
lResult = RegSetValueExW(hKey,
szRegVK,
0,
REG_BINARY,
(LPBYTE)&vk,
sizeof(DWORD));
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
CliSaveImeHotKey(id, vk, mod, hkl, TRUE);
RIPERR1( lResult, RIP_WARNING,
"SaveImeHotKey:setting value on %s failed", szRegVK );
return ( FALSE );
}
lResult = RegSetValueExW(hKey,
szRegMOD,
0,
REG_BINARY,
(LPBYTE)&mod,
sizeof(DWORD));
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
CliSaveImeHotKey(id, vk, mod, hkl, TRUE);
RIPERR1(lResult, RIP_WARNING,
"CliSaveImeHotKey: setting value on %s failed", szRegMOD);
return FALSE;
}
lResult = RegSetValueExW(hKey,
szRegHKL,
0,
REG_BINARY,
(LPBYTE)&hkl,
sizeof(DWORD));
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
CliSaveImeHotKey(id, vk, mod, hkl, TRUE);
RIPERR1(lResult, RIP_WARNING,
"CliSaveImeHotKey: setting value on %s failed", szRegHKL);
return FALSE;
}
RegCloseKey(hKey);
return TRUE;
}
BOOL CliImmSetHotKeyWorker(
DWORD dwID,
UINT uModifiers,
UINT uVKey,
HKL hkl,
DWORD dwAction)
{
//
// if we're adding an IME hotkey entry, let's check
// the parameters before calling the kernel side code
//
if (dwAction == ISHK_ADD) {
if (dwID >= IME_HOTKEY_DSWITCH_FIRST &&
dwID <= IME_HOTKEY_DSWITCH_LAST) {
//
// IME direct switching hot key - switch to
// the keyboard layout specified.
// We need to specify keyboard layout.
//
if (hkl == NULL) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "hkl should be specified");
return FALSE;
}
} else {
//
// normal hot keys - change the mode of current iME
//
// Because it should be effective in all IME no matter
// which IME is active we should not specify a target IME
//
if (hkl != NULL) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "hkl shouldn't be specified");
return FALSE;
}
if (dwID >= IME_KHOTKEY_FIRST && dwID <= IME_KHOTKEY_LAST) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Hotkey for Korean IMEs are invalid.");
return FALSE;
}
}
if (uModifiers & MOD_MODIFY_KEYS) {
//
// Because normal keyboard has left and right key for
// these keys, you should specify left or right ( or both )
//
if ((uModifiers & MOD_BOTH_SIDES) == 0) {
RIPERR3(ERROR_INVALID_PARAMETER, RIP_WARNING, "invalid modifiers %x for id %x vKey %x", uModifiers, dwID, uVKey);
return FALSE;
}
}
#if 0 // Skip this check for now
//
// It doesn't make sense if vkey is same as modifiers
//
if ( ((uModifiers & MOD_ALT) && (uVKey == VK_MENU)) ||
((uModifiers & MOD_CONTROL) && (uVKey == VK_CONTROL)) ||
((uModifiers & MOD_SHIFT) && (uVKey == VK_SHIFT)) ||
((uModifiers & MOD_WIN) && ((uVKey == VK_LWIN)||(uVKey == VK_RWIN)))
) {
RIPERR0( ERROR_INVALID_PARAMETER, RIP_WARNING, "vkey and modifiers are same");
return FALSE;
}
#endif
}
return NtUserSetImeHotKey(dwID, uModifiers, uVKey, hkl, dwAction);
}
//
// NumToHexAscii
//
// convert a DWORD into the hex string
// (e.g. 0x31 -> "00000031")
//
// 29-Jan-1996 takaok ported from Win95.
//
static CONST TCHAR szHexString[] = TEXT("0123456789ABCDEF");
VOID
NumToHexAscii(
DWORD dwNum,
PWSTR szAscii)
{
int i;
for (i = 7; i >= 0; i--) {
szAscii[i] = szHexString[dwNum & 0x0000000f];
dwNum >>= 4;
}
szAscii[8] = TEXT('\0');
return;
}