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

  1. /****************************** Module Header ******************************\
  2. * Module Name: imehotky.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Contents: Manage IME hotkey
  7. *
  8. * There are the following two kind of hotkeys defined in the IME specification.
  9. *
  10. * 1) IME hotkeys that changes the mode/status of current IME
  11. * 2) IME hotkeys that causes IME (keyboard layout) change
  12. *
  13. * History:
  14. * 10-Sep-1995 takaok Created for NT 3.51.
  15. * 15-Mar-1996 takaok Ported to NT 4.0
  16. \***************************************************************************/
  17. #include "precomp.h"
  18. #pragma hdrstop
  19. PIMEHOTKEYOBJ DeleteImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete);
  20. VOID AddImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd);
  21. PIMEHOTKEYOBJ FindImeHotKeyByKey(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey);
  22. PIMEHOTKEYOBJ FindImeHotKeyByID(PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID);
  23. PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey, LANGID langId);
  24. #define L_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
  25. #define L_JPN MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
  26. #define L_KOR MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT)
  27. #define L_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
  28. enum {
  29. ILANG_NO_MATCH = 0, // 0: does not match.
  30. ILANG_MATCH_SYSTEM, // 1: matches the system locale
  31. ILANG_MATCH_THREAD, // 2: matches the thread locale
  32. ILANG_MATCH_PERFECT, // 3: matches the current HKL, or direct KL switching hotkey.
  33. };
  34. // Make sure constants are within the range we expect
  35. #if IME_CHOTKEY_FIRST != 0x10 || IME_JHOTKEY_FIRST != 0x30 || IME_KHOTKEY_FIRST != 0x50 || IME_THOTKEY_FIRST != 0x70
  36. #error unexpected IME_xHOTKEY range !
  37. #endif
  38. LANGID GetHotKeyLangID(DWORD dwHotKeyID)
  39. {
  40. LANGID langId = -1;
  41. static CONST LANGID aLangId[] = {
  42. ~0, // 0x00 - 0x0f: illegal
  43. L_CHS, L_CHS, // 0x10 - 0x2f
  44. L_JPN, L_JPN, // 0x30 - 0x4f
  45. L_KOR, L_KOR, // 0x50 - 0x6f
  46. L_CHT, L_CHT, // 0x70 - 0x8f
  47. };
  48. if (dwHotKeyID >= IME_CHOTKEY_FIRST && dwHotKeyID <= IME_THOTKEY_LAST) {
  49. langId = aLangId[dwHotKeyID >> 4];
  50. }
  51. else {
  52. langId = LANG_NEUTRAL;
  53. }
  54. // Because KOR IME does not want IME hot key handling
  55. UserAssert(langId != L_KOR);
  56. return langId;
  57. }
  58. BOOL
  59. GetImeHotKey(
  60. DWORD dwHotKeyID,
  61. PUINT puModifiers,
  62. PUINT puVKey,
  63. HKL *phKL )
  64. {
  65. PIMEHOTKEYOBJ ph;
  66. ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
  67. if ( ph == NULL ) {
  68. RIPERR0(ERROR_HOTKEY_NOT_REGISTERED, RIP_VERBOSE, "No such IME hotkey");
  69. return (FALSE);
  70. }
  71. //
  72. // it is OK for NULL phKL, if the target hKL is NULL
  73. //
  74. if ( phKL ) {
  75. *phKL = ph->hk.hKL;
  76. } else if ( ph->hk.hKL != NULL ) {
  77. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "phKL is null");
  78. return (FALSE);
  79. }
  80. *puModifiers = ph->hk.uModifiers;
  81. *puVKey = ph->hk.uVKey;
  82. return (TRUE);
  83. }
  84. //
  85. // Insert/remove the specified IME hotkey into/from
  86. // the IME hotkey list (gpImeHotKeyListHeader).
  87. //
  88. BOOL
  89. SetImeHotKey(
  90. DWORD dwHotKeyID,
  91. UINT uModifiers,
  92. UINT uVKey,
  93. HKL hKL,
  94. DWORD dwAction )
  95. {
  96. PIMEHOTKEYOBJ ph;
  97. switch ( dwAction ) {
  98. case ISHK_REMOVE:
  99. ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
  100. if ( ph != NULL ) {
  101. if ( DeleteImeHotKey( &gpImeHotKeyListHeader, ph ) == ph ) {
  102. UserFreePool( ph );
  103. return ( TRUE );
  104. } else {
  105. RIPMSG0( RIP_ERROR, "IME hotkey list is messed up" );
  106. return FALSE;
  107. }
  108. } else {
  109. RIPERR0( ERROR_INVALID_PARAMETER,
  110. RIP_WARNING,
  111. "no such IME hotkey registered");
  112. return FALSE;
  113. }
  114. break;
  115. case ISHK_INITIALIZE:
  116. ph = gpImeHotKeyListHeader;
  117. while ( ph != NULL ) {
  118. PIMEHOTKEYOBJ phNext;
  119. phNext = ph->pNext;
  120. UserFreePool( ph );
  121. ph = phNext;
  122. }
  123. gpImeHotKeyListHeader = NULL;
  124. return TRUE;
  125. case ISHK_ADD:
  126. if (dwHotKeyID >= IME_KHOTKEY_FIRST && dwHotKeyID <= IME_KHOTKEY_LAST) {
  127. // Korean IME does not want any IMM hotkey handling.
  128. // We should not register any Korean IME hot keys.
  129. return FALSE;
  130. }
  131. if ((WORD)uVKey == VK_PACKET) {
  132. //
  133. // VK_PACKET should not be a IME hot key.
  134. //
  135. return FALSE;
  136. }
  137. ph = FindImeHotKeyByKeyWithLang(gpImeHotKeyListHeader,
  138. uModifiers & MOD_MODIFY_KEYS,
  139. uModifiers & MOD_BOTH_SIDES,
  140. uVKey,
  141. GetHotKeyLangID(dwHotKeyID));
  142. if ( ph != NULL ) {
  143. if ( ph->hk.dwHotKeyID != dwHotKeyID ) {
  144. RIPERR0( ERROR_HOTKEY_ALREADY_REGISTERED,
  145. RIP_WARNING,
  146. "There is an IME hotkey that has the same vkey/modifiers/Lang Id");
  147. return FALSE;
  148. }
  149. // So far we found a hotkey that has the
  150. // same vkey and same ID.
  151. // But because modifiers may be slightly
  152. // different, so go ahead and change it.
  153. } else {
  154. //
  155. // the specified vkey/modifiers combination cound not be found
  156. // in the hotkey list. The caller may want to change the key
  157. // assignment of an existing hotkey or add a new hotkey.
  158. //
  159. ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID );
  160. }
  161. if ( ph == NULL ) {
  162. //
  163. // adding a new hotkey
  164. //
  165. ph = (PIMEHOTKEYOBJ)UserAllocPool( sizeof(IMEHOTKEYOBJ), TAG_IMEHOTKEY );
  166. if ( ph == NULL ) {
  167. RIPERR0( ERROR_OUTOFMEMORY,
  168. RIP_WARNING,
  169. "Memory allocation failed in SetImeHotKey");
  170. return FALSE;
  171. }
  172. ph->hk.dwHotKeyID = dwHotKeyID;
  173. ph->hk.uModifiers = uModifiers;
  174. ph->hk.uVKey = uVKey;
  175. ph->hk.hKL = hKL;
  176. ph->pNext = NULL;
  177. AddImeHotKey( &gpImeHotKeyListHeader, ph );
  178. } else {
  179. //
  180. // changing an existing hotkey
  181. //
  182. ph->hk.uModifiers = uModifiers;
  183. ph->hk.uVKey = uVKey;
  184. ph->hk.hKL = hKL;
  185. }
  186. return TRUE;
  187. }
  188. return FALSE;
  189. }
  190. PIMEHOTKEYOBJ DeleteImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete )
  191. {
  192. PIMEHOTKEYOBJ ph;
  193. if ( pDelete == *ppHead ) {
  194. *ppHead = pDelete->pNext;
  195. return pDelete;
  196. }
  197. for ( ph = *ppHead; ph != NULL; ph = ph->pNext ) {
  198. if ( ph->pNext == pDelete ) {
  199. ph->pNext = pDelete->pNext;
  200. return pDelete;
  201. }
  202. }
  203. return NULL;
  204. }
  205. VOID AddImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd )
  206. {
  207. PIMEHOTKEYOBJ ph;
  208. if ( *ppHead == NULL ) {
  209. *ppHead = pAdd;
  210. } else {
  211. ph = *ppHead;
  212. while( ph->pNext != NULL )
  213. ph = ph->pNext;
  214. ph->pNext = pAdd;
  215. }
  216. return;
  217. }
  218. VOID FreeImeHotKeys(VOID)
  219. {
  220. PIMEHOTKEYOBJ phk;
  221. while (gpImeHotKeyListHeader != NULL) {
  222. phk = gpImeHotKeyListHeader->pNext;
  223. UserFreePool(gpImeHotKeyListHeader);
  224. gpImeHotKeyListHeader = phk;
  225. }
  226. }
  227. LCID glcidSystem;
  228. int GetLangIdMatchLevel(HKL hkl, LANGID langId)
  229. {
  230. if (langId == LANG_NEUTRAL) {
  231. //
  232. // If langId is LANG_NEUTRAL, the hot key does not depend on
  233. // the current HKL. Make it perfect match always.
  234. //
  235. return ILANG_MATCH_PERFECT;
  236. }
  237. {
  238. LCID lcid;
  239. #ifdef CUAS_ENABLE
  240. PTHREADINFO ptiCurrent = PtiCurrent();
  241. BOOL bMSCTF = FALSE;
  242. try {
  243. bMSCTF = ((ptiCurrent->pClientInfo->CI_flags & CI_CUAS_MSCTF_RUNNING) != 0);
  244. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  245. }
  246. if (bMSCTF && !IS_IME_KBDLAYOUT(hkl)) {
  247. return ILANG_NO_MATCH;
  248. }
  249. #endif // CUAS_ENABLE
  250. if (LOWORD(HandleToUlong(hkl)) == langId) {
  251. // langId matches the current KL locale
  252. return ILANG_MATCH_PERFECT;
  253. }
  254. try {
  255. lcid = NtCurrentTeb()->CurrentLocale;
  256. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  257. lcid = LOCALE_NEUTRAL;
  258. }
  259. if (LANGIDFROMLCID(lcid) == langId) {
  260. // langId matches the current thread's locale
  261. return ILANG_MATCH_THREAD;
  262. }
  263. if (glcidSystem == 0) {
  264. // If we've not got system default locale yet, get it here.
  265. ZwQueryDefaultLocale(FALSE, &glcidSystem);
  266. }
  267. if (LANGIDFROMLCID(glcidSystem) == langId) {
  268. // langId matches the system locale.
  269. return ILANG_MATCH_SYSTEM;
  270. }
  271. }
  272. return ILANG_NO_MATCH;
  273. }
  274. ////////////////////////////////////////////////////////////////////////
  275. // FindImeHotKeyByKey()
  276. // Return Value:
  277. // pHotKey - IMEHOTKEY pointer with the key,
  278. // else NULL - failure
  279. //
  280. // Finds the best matching of IME hot keys considering the current
  281. // input locale.
  282. //
  283. ////////////////////////////////////////////////////////////////////////
  284. PIMEHOTKEYOBJ FindImeHotKeyByKey( // Finds pHotKey with this input key
  285. PIMEHOTKEYOBJ pHead,
  286. UINT uModifyKeys, // the modify keys of this input key
  287. UINT uRL, // the right and left hand side
  288. UINT uVKey) // the input key
  289. {
  290. PTHREADINFO ptiCurrent = PtiCurrent();
  291. PIMEHOTKEYOBJ phResult = NULL;
  292. PIMEHOTKEYOBJ ph;
  293. HKL hkl = GetActiveHKL();
  294. WORD langPrimary = PRIMARYLANGID(LOWORD(HandleToUlong(hkl)));
  295. int iLevel = ILANG_NO_MATCH;
  296. for (ph = pHead; ph != NULL; ph = ph->pNext) {
  297. if (ph->hk.uVKey == uVKey) {
  298. BOOL fDoCheck = FALSE;
  299. // Check if the modifiers match
  300. if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) {
  301. fDoCheck = TRUE;
  302. } else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) {
  303. continue;
  304. }
  305. if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL ||
  306. (ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) {
  307. fDoCheck = TRUE;
  308. }
  309. if (fDoCheck) {
  310. LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID);
  311. int iMatch = GetLangIdMatchLevel(hkl, langId);
  312. #if 0 // Test only
  313. if (iMatch != ILANG_NO_MATCH) {
  314. DbgPrint("GetIdMatchLevel(%X, %X)=%d\n", hkl, langId);
  315. }
  316. #endif
  317. if (iMatch == ILANG_MATCH_PERFECT) {
  318. // Perfect match !
  319. return ph;
  320. }
  321. // If the hotkey is DSWITCH, GetLangIdMatchLevel() must return 3.
  322. UserAssert(ph->hk.dwHotKeyID < IME_HOTKEY_DSWITCH_FIRST ||
  323. ph->hk.dwHotKeyID > IME_HOTKEY_DSWITCH_LAST);
  324. if (langPrimary == LANG_KOREAN) {
  325. // Korean IME wants no hotkeys except the direct
  326. // keyboard layout switching hotkeys.
  327. continue;
  328. }
  329. if (iMatch == ILANG_NO_MATCH) {
  330. // Special case for CHT/CHS toggle
  331. if (ph->hk.dwHotKeyID == IME_CHOTKEY_IME_NONIME_TOGGLE ||
  332. ph->hk.dwHotKeyID == IME_THOTKEY_IME_NONIME_TOGGLE) {
  333. //
  334. // If the key is for CHT/CHS toggle and the previous
  335. // hkl is either CHT/CHS, it is a IME hotkey.
  336. //
  337. if (LOWORD(HandleToUlong(ptiCurrent->hklPrev)) == langId) {
  338. #if 0 // Test only
  339. DbgPrint("FindImeHotKeyByKey() found CHT/CHS hotkey.\n");
  340. #endif
  341. return ph;
  342. }
  343. }
  344. }
  345. else if (iMatch > iLevel) {
  346. // Current ph is the strongest candidate so far.
  347. iLevel = iMatch;
  348. phResult = ph;
  349. }
  350. }
  351. }
  352. }
  353. return phResult;
  354. }
  355. /**********************************************************************/
  356. /* FindImeHotKeyByID() */
  357. /* Return Value: */
  358. /* pHotKey - IMEHOTKEY pointer with the dwHotKeyID, */
  359. /* else NULL - failure */
  360. /**********************************************************************/
  361. PIMEHOTKEYOBJ FindImeHotKeyByID( PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID )
  362. {
  363. PIMEHOTKEYOBJ ph;
  364. for ( ph = pHead; ph != NULL; ph = ph->pNext ) {
  365. if ( ph->hk.dwHotKeyID == dwHotKeyID )
  366. return (ph);
  367. }
  368. return (PIMEHOTKEYOBJ)NULL;
  369. }
  370. /**********************************************************************/
  371. /* FindImeHotKeyByKeyWithLang() */
  372. /* Return Value: */
  373. /* pHotKey - IMEHOTKEY pointer with the key, */
  374. /* else NULL - failure */
  375. /**********************************************************************/
  376. PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang( // Finds pHotKey with this input key
  377. PIMEHOTKEYOBJ pHead,
  378. UINT uModifyKeys, // the modify keys of this input key
  379. UINT uRL, // the right and left hand side
  380. UINT uVKey, // the input key
  381. LANGID langIdKey) // the language id
  382. {
  383. PIMEHOTKEYOBJ ph;
  384. for (ph = pHead; ph != NULL; ph = ph->pNext) {
  385. if (ph->hk.uVKey == uVKey) {
  386. BOOL fDoCheck = FALSE;
  387. // Check if the modifiers match
  388. if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) {
  389. fDoCheck = TRUE;
  390. } else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) {
  391. continue;
  392. }
  393. if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL ||
  394. (ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) {
  395. fDoCheck = TRUE;
  396. }
  397. if (fDoCheck) {
  398. LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID);
  399. if (langIdKey == langId || langId == LANG_NEUTRAL) {
  400. return ph;
  401. }
  402. }
  403. }
  404. }
  405. return NULL;
  406. }
  407. PIMEHOTKEYOBJ
  408. CheckImeHotKey(
  409. PQ pq, // input queue
  410. UINT uVKey, // virtual key
  411. LPARAM lParam // lparam of WM_KEYxxx message
  412. )
  413. {
  414. static UINT uVKeySaved = 0;
  415. PIMEHOTKEYOBJ ph;
  416. UINT uModifiers = 0;
  417. BOOL fKeyUp;
  418. //
  419. // early return for key up message
  420. //
  421. fKeyUp = ( lParam & 0x80000000 ) ? TRUE : FALSE;
  422. if ( fKeyUp ) {
  423. //
  424. // if the uVKey is not same as the vkey
  425. // we previously saved, there is no chance
  426. // that this is a hotkey.
  427. //
  428. if ( uVKeySaved != uVKey ) {
  429. uVKeySaved = 0;
  430. return NULL;
  431. }
  432. uVKeySaved = 0;
  433. //
  434. // If it's same, we still need to check
  435. // the hotkey list because there is a
  436. // chance that the hotkey list is modified
  437. // between the key make and break.
  438. //
  439. }
  440. //
  441. // Current specification doesn't allow us to use a complex
  442. // hotkey such as LSHIFT+RMENU+SPACE
  443. //
  444. //
  445. // Setup the shift, control, alt key states
  446. //
  447. uModifiers |= TestKeyStateDown(pq, VK_LSHIFT) ? (MOD_SHIFT | MOD_LEFT) : 0;
  448. uModifiers |= TestKeyStateDown(pq, VK_RSHIFT) ? (MOD_SHIFT | MOD_RIGHT) : 0;
  449. uModifiers |= TestKeyStateDown(pq, VK_LCONTROL) ? (MOD_CONTROL | MOD_LEFT) : 0;
  450. uModifiers |= TestKeyStateDown(pq, VK_RCONTROL) ? (MOD_CONTROL | MOD_RIGHT) : 0;
  451. uModifiers |= TestKeyStateDown(pq, VK_LMENU) ? (MOD_ALT | MOD_LEFT) : 0;
  452. uModifiers |= TestKeyStateDown(pq, VK_RMENU) ? (MOD_ALT | MOD_RIGHT) : 0;
  453. ph = FindImeHotKeyByKey( gpImeHotKeyListHeader,
  454. uModifiers & MOD_MODIFY_KEYS,
  455. uModifiers & MOD_BOTH_SIDES,
  456. uVKey );
  457. if ( ph != NULL ) {
  458. if ( fKeyUp ) {
  459. if ( ph->hk.uModifiers & MOD_ON_KEYUP ) {
  460. return ph;
  461. }
  462. } else {
  463. if ( ph->hk.uModifiers & MOD_ON_KEYUP ) {
  464. //
  465. // save vkey for next keyup message time
  466. //
  467. // when ALT+Z is a hotkey, we don't want
  468. // to handle #2 as the hotkey sequence.
  469. // 1) ALT make -> 'Z' make -> 'Z' break
  470. // 2) 'Z' make -> ALT make -> 'Z' break
  471. //
  472. uVKeySaved = uVKey;
  473. } else {
  474. return ph;
  475. }
  476. }
  477. }
  478. return NULL;
  479. }