|
|
/****************************** 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; }
|