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.
 
 
 
 
 
 

559 lines
18 KiB

/****************************** Module Header ******************************\
* Module Name: imehotky.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Contents: Manage IME hotkey
*
* There are the following two kind of hotkeys defined in the IME specification.
*
* 1) IME hotkeys that changes the mode/status of current IME
* 2) IME hotkeys that causes IME (keyboard layout) change
*
* History:
* 10-Sep-1995 takaok Created for NT 3.51.
* 15-Mar-1996 takaok Ported to NT 4.0
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
PIMEHOTKEYOBJ DeleteImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete);
VOID AddImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd);
PIMEHOTKEYOBJ FindImeHotKeyByKey(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey);
PIMEHOTKEYOBJ FindImeHotKeyByID(PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID);
PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey, LANGID langId);
#define L_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
#define L_JPN MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
#define L_KOR MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT)
#define L_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
enum {
ILANG_NO_MATCH = 0, // 0: does not match.
ILANG_MATCH_SYSTEM, // 1: matches the system locale
ILANG_MATCH_THREAD, // 2: matches the thread locale
ILANG_MATCH_PERFECT, // 3: matches the current HKL, or direct KL switching hotkey.
};
// Make sure constants are within the range we expect
#if IME_CHOTKEY_FIRST != 0x10 || IME_JHOTKEY_FIRST != 0x30 || IME_KHOTKEY_FIRST != 0x50 || IME_THOTKEY_FIRST != 0x70
#error unexpected IME_xHOTKEY range !
#endif
LANGID GetHotKeyLangID(DWORD dwHotKeyID)
{
LANGID langId = -1;
static CONST LANGID aLangId[] = {
~0, // 0x00 - 0x0f: illegal
L_CHS, L_CHS, // 0x10 - 0x2f
L_JPN, L_JPN, // 0x30 - 0x4f
L_KOR, L_KOR, // 0x50 - 0x6f
L_CHT, L_CHT, // 0x70 - 0x8f
};
if (dwHotKeyID >= IME_CHOTKEY_FIRST && dwHotKeyID <= IME_THOTKEY_LAST) {
langId = aLangId[dwHotKeyID >> 4];
}
else {
langId = LANG_NEUTRAL;
}
// Because KOR IME does not want IME hot key handling
UserAssert(langId != L_KOR);
return langId;
}
BOOL
GetImeHotKey(
DWORD dwHotKeyID,
PUINT puModifiers,
PUINT puVKey,
HKL *phKL )
{
PIMEHOTKEYOBJ ph;
ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
if ( ph == NULL ) {
RIPERR0(ERROR_HOTKEY_NOT_REGISTERED, RIP_VERBOSE, "No such IME hotkey");
return (FALSE);
}
//
// it is OK for NULL phKL, if the target hKL is NULL
//
if ( phKL ) {
*phKL = ph->hk.hKL;
} else if ( ph->hk.hKL != NULL ) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "phKL is null");
return (FALSE);
}
*puModifiers = ph->hk.uModifiers;
*puVKey = ph->hk.uVKey;
return (TRUE);
}
//
// Insert/remove the specified IME hotkey into/from
// the IME hotkey list (gpImeHotKeyListHeader).
//
BOOL
SetImeHotKey(
DWORD dwHotKeyID,
UINT uModifiers,
UINT uVKey,
HKL hKL,
DWORD dwAction )
{
PIMEHOTKEYOBJ ph;
switch ( dwAction ) {
case ISHK_REMOVE:
ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
if ( ph != NULL ) {
if ( DeleteImeHotKey( &gpImeHotKeyListHeader, ph ) == ph ) {
UserFreePool( ph );
return ( TRUE );
} else {
RIPMSG0( RIP_ERROR, "IME hotkey list is messed up" );
return FALSE;
}
} else {
RIPERR0( ERROR_INVALID_PARAMETER,
RIP_WARNING,
"no such IME hotkey registered");
return FALSE;
}
break;
case ISHK_INITIALIZE:
ph = gpImeHotKeyListHeader;
while ( ph != NULL ) {
PIMEHOTKEYOBJ phNext;
phNext = ph->pNext;
UserFreePool( ph );
ph = phNext;
}
gpImeHotKeyListHeader = NULL;
return TRUE;
case ISHK_ADD:
if (dwHotKeyID >= IME_KHOTKEY_FIRST && dwHotKeyID <= IME_KHOTKEY_LAST) {
// Korean IME does not want any IMM hotkey handling.
// We should not register any Korean IME hot keys.
return FALSE;
}
if ((WORD)uVKey == VK_PACKET) {
//
// VK_PACKET should not be a IME hot key.
//
return FALSE;
}
ph = FindImeHotKeyByKeyWithLang(gpImeHotKeyListHeader,
uModifiers & MOD_MODIFY_KEYS,
uModifiers & MOD_BOTH_SIDES,
uVKey,
GetHotKeyLangID(dwHotKeyID));
if ( ph != NULL ) {
if ( ph->hk.dwHotKeyID != dwHotKeyID ) {
RIPERR0( ERROR_HOTKEY_ALREADY_REGISTERED,
RIP_WARNING,
"There is an IME hotkey that has the same vkey/modifiers/Lang Id");
return FALSE;
}
// So far we found a hotkey that has the
// same vkey and same ID.
// But because modifiers may be slightly
// different, so go ahead and change it.
} else {
//
// the specified vkey/modifiers combination cound not be found
// in the hotkey list. The caller may want to change the key
// assignment of an existing hotkey or add a new hotkey.
//
ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
}
if ( ph == NULL ) {
//
// adding a new hotkey
//
ph = (PIMEHOTKEYOBJ)UserAllocPool( sizeof(IMEHOTKEYOBJ), TAG_IMEHOTKEY );
if ( ph == NULL ) {
RIPERR0( ERROR_OUTOFMEMORY,
RIP_WARNING,
"Memory allocation failed in SetImeHotKey");
return FALSE;
}
ph->hk.dwHotKeyID = dwHotKeyID;
ph->hk.uModifiers = uModifiers;
ph->hk.uVKey = uVKey;
ph->hk.hKL = hKL;
ph->pNext = NULL;
AddImeHotKey( &gpImeHotKeyListHeader, ph );
} else {
//
// changing an existing hotkey
//
ph->hk.uModifiers = uModifiers;
ph->hk.uVKey = uVKey;
ph->hk.hKL = hKL;
}
return TRUE;
}
return FALSE;
}
PIMEHOTKEYOBJ DeleteImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete )
{
PIMEHOTKEYOBJ ph;
if ( pDelete == *ppHead ) {
*ppHead = pDelete->pNext;
return pDelete;
}
for ( ph = *ppHead; ph != NULL; ph = ph->pNext ) {
if ( ph->pNext == pDelete ) {
ph->pNext = pDelete->pNext;
return pDelete;
}
}
return NULL;
}
VOID AddImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd )
{
PIMEHOTKEYOBJ ph;
if ( *ppHead == NULL ) {
*ppHead = pAdd;
} else {
ph = *ppHead;
while( ph->pNext != NULL )
ph = ph->pNext;
ph->pNext = pAdd;
}
return;
}
VOID FreeImeHotKeys(VOID)
{
PIMEHOTKEYOBJ phk;
while (gpImeHotKeyListHeader != NULL) {
phk = gpImeHotKeyListHeader->pNext;
UserFreePool(gpImeHotKeyListHeader);
gpImeHotKeyListHeader = phk;
}
}
LCID glcidSystem;
int GetLangIdMatchLevel(HKL hkl, LANGID langId)
{
if (langId == LANG_NEUTRAL) {
//
// If langId is LANG_NEUTRAL, the hot key does not depend on
// the current HKL. Make it perfect match always.
//
return ILANG_MATCH_PERFECT;
}
{
LCID lcid;
#ifdef CUAS_ENABLE
PTHREADINFO ptiCurrent = PtiCurrent();
BOOL bMSCTF = FALSE;
try {
bMSCTF = ((ptiCurrent->pClientInfo->CI_flags & CI_CUAS_MSCTF_RUNNING) != 0);
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
if (bMSCTF && !IS_IME_KBDLAYOUT(hkl)) {
return ILANG_NO_MATCH;
}
#endif // CUAS_ENABLE
if (LOWORD(HandleToUlong(hkl)) == langId) {
// langId matches the current KL locale
return ILANG_MATCH_PERFECT;
}
try {
lcid = NtCurrentTeb()->CurrentLocale;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
lcid = LOCALE_NEUTRAL;
}
if (LANGIDFROMLCID(lcid) == langId) {
// langId matches the current thread's locale
return ILANG_MATCH_THREAD;
}
if (glcidSystem == 0) {
// If we've not got system default locale yet, get it here.
ZwQueryDefaultLocale(FALSE, &glcidSystem);
}
if (LANGIDFROMLCID(glcidSystem) == langId) {
// langId matches the system locale.
return ILANG_MATCH_SYSTEM;
}
}
return ILANG_NO_MATCH;
}
////////////////////////////////////////////////////////////////////////
// FindImeHotKeyByKey()
// Return Value:
// pHotKey - IMEHOTKEY pointer with the key,
// else NULL - failure
//
// Finds the best matching of IME hot keys considering the current
// input locale.
//
////////////////////////////////////////////////////////////////////////
PIMEHOTKEYOBJ FindImeHotKeyByKey( // Finds pHotKey with this input key
PIMEHOTKEYOBJ pHead,
UINT uModifyKeys, // the modify keys of this input key
UINT uRL, // the right and left hand side
UINT uVKey) // the input key
{
PTHREADINFO ptiCurrent = PtiCurrent();
PIMEHOTKEYOBJ phResult = NULL;
PIMEHOTKEYOBJ ph;
HKL hkl = GetActiveHKL();
WORD langPrimary = PRIMARYLANGID(LOWORD(HandleToUlong(hkl)));
int iLevel = ILANG_NO_MATCH;
for (ph = pHead; ph != NULL; ph = ph->pNext) {
if (ph->hk.uVKey == uVKey) {
BOOL fDoCheck = FALSE;
// Check if the modifiers match
if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) {
fDoCheck = TRUE;
} else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) {
continue;
}
if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL ||
(ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) {
fDoCheck = TRUE;
}
if (fDoCheck) {
LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID);
int iMatch = GetLangIdMatchLevel(hkl, langId);
#if 0 // Test only
if (iMatch != ILANG_NO_MATCH) {
DbgPrint("GetIdMatchLevel(%X, %X)=%d\n", hkl, langId);
}
#endif
if (iMatch == ILANG_MATCH_PERFECT) {
// Perfect match !
return ph;
}
// If the hotkey is DSWITCH, GetLangIdMatchLevel() must return 3.
UserAssert(ph->hk.dwHotKeyID < IME_HOTKEY_DSWITCH_FIRST ||
ph->hk.dwHotKeyID > IME_HOTKEY_DSWITCH_LAST);
if (langPrimary == LANG_KOREAN) {
// Korean IME wants no hotkeys except the direct
// keyboard layout switching hotkeys.
continue;
}
if (iMatch == ILANG_NO_MATCH) {
// Special case for CHT/CHS toggle
if (ph->hk.dwHotKeyID == IME_CHOTKEY_IME_NONIME_TOGGLE ||
ph->hk.dwHotKeyID == IME_THOTKEY_IME_NONIME_TOGGLE) {
//
// If the key is for CHT/CHS toggle and the previous
// hkl is either CHT/CHS, it is a IME hotkey.
//
if (LOWORD(HandleToUlong(ptiCurrent->hklPrev)) == langId) {
#if 0 // Test only
DbgPrint("FindImeHotKeyByKey() found CHT/CHS hotkey.\n");
#endif
return ph;
}
}
}
else if (iMatch > iLevel) {
// Current ph is the strongest candidate so far.
iLevel = iMatch;
phResult = ph;
}
}
}
}
return phResult;
}
/**********************************************************************/
/* FindImeHotKeyByID() */
/* Return Value: */
/* pHotKey - IMEHOTKEY pointer with the dwHotKeyID, */
/* else NULL - failure */
/**********************************************************************/
PIMEHOTKEYOBJ FindImeHotKeyByID( PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID )
{
PIMEHOTKEYOBJ ph;
for ( ph = pHead; ph != NULL; ph = ph->pNext ) {
if ( ph->hk.dwHotKeyID == dwHotKeyID )
return (ph);
}
return (PIMEHOTKEYOBJ)NULL;
}
/**********************************************************************/
/* FindImeHotKeyByKeyWithLang() */
/* Return Value: */
/* pHotKey - IMEHOTKEY pointer with the key, */
/* else NULL - failure */
/**********************************************************************/
PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang( // Finds pHotKey with this input key
PIMEHOTKEYOBJ pHead,
UINT uModifyKeys, // the modify keys of this input key
UINT uRL, // the right and left hand side
UINT uVKey, // the input key
LANGID langIdKey) // the language id
{
PIMEHOTKEYOBJ ph;
for (ph = pHead; ph != NULL; ph = ph->pNext) {
if (ph->hk.uVKey == uVKey) {
BOOL fDoCheck = FALSE;
// Check if the modifiers match
if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) {
fDoCheck = TRUE;
} else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) {
continue;
}
if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL ||
(ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) {
fDoCheck = TRUE;
}
if (fDoCheck) {
LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID);
if (langIdKey == langId || langId == LANG_NEUTRAL) {
return ph;
}
}
}
}
return NULL;
}
PIMEHOTKEYOBJ
CheckImeHotKey(
PQ pq, // input queue
UINT uVKey, // virtual key
LPARAM lParam // lparam of WM_KEYxxx message
)
{
static UINT uVKeySaved = 0;
PIMEHOTKEYOBJ ph;
UINT uModifiers = 0;
BOOL fKeyUp;
//
// early return for key up message
//
fKeyUp = ( lParam & 0x80000000 ) ? TRUE : FALSE;
if ( fKeyUp ) {
//
// if the uVKey is not same as the vkey
// we previously saved, there is no chance
// that this is a hotkey.
//
if ( uVKeySaved != uVKey ) {
uVKeySaved = 0;
return NULL;
}
uVKeySaved = 0;
//
// If it's same, we still need to check
// the hotkey list because there is a
// chance that the hotkey list is modified
// between the key make and break.
//
}
//
// Current specification doesn't allow us to use a complex
// hotkey such as LSHIFT+RMENU+SPACE
//
//
// Setup the shift, control, alt key states
//
uModifiers |= TestKeyStateDown(pq, VK_LSHIFT) ? (MOD_SHIFT | MOD_LEFT) : 0;
uModifiers |= TestKeyStateDown(pq, VK_RSHIFT) ? (MOD_SHIFT | MOD_RIGHT) : 0;
uModifiers |= TestKeyStateDown(pq, VK_LCONTROL) ? (MOD_CONTROL | MOD_LEFT) : 0;
uModifiers |= TestKeyStateDown(pq, VK_RCONTROL) ? (MOD_CONTROL | MOD_RIGHT) : 0;
uModifiers |= TestKeyStateDown(pq, VK_LMENU) ? (MOD_ALT | MOD_LEFT) : 0;
uModifiers |= TestKeyStateDown(pq, VK_RMENU) ? (MOD_ALT | MOD_RIGHT) : 0;
ph = FindImeHotKeyByKey( gpImeHotKeyListHeader,
uModifiers & MOD_MODIFY_KEYS,
uModifiers & MOD_BOTH_SIDES,
uVKey );
if ( ph != NULL ) {
if ( fKeyUp ) {
if ( ph->hk.uModifiers & MOD_ON_KEYUP ) {
return ph;
}
} else {
if ( ph->hk.uModifiers & MOD_ON_KEYUP ) {
//
// save vkey for next keyup message time
//
// when ALT+Z is a hotkey, we don't want
// to handle #2 as the hotkey sequence.
// 1) ALT make -> 'Z' make -> 'Z' break
// 2) 'Z' make -> ALT make -> 'Z' break
//
uVKeySaved = uVKey;
} else {
return ph;
}
}
}
return NULL;
}