Source code of Windows XP (NT5)
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.

558 lines
15 KiB

  1. /*****************************************************************************
  2. *
  3. * DIEmK.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * Emulation module for keyboard.
  10. *
  11. * Contents:
  12. *
  13. * CEm_Kbd_CreateInstance
  14. * CEm_Kbd_InitKeys
  15. * CEm_LL_KbdHook
  16. *
  17. *****************************************************************************/
  18. #include "dinputpr.h"
  19. /*****************************************************************************
  20. *
  21. * The sqiffle for this file.
  22. *
  23. *****************************************************************************/
  24. #define sqfl sqflEm
  25. /*****************************************************************************
  26. *
  27. * Keyboard emulation
  28. *
  29. *****************************************************************************/
  30. STDMETHODIMP CEm_Kbd_Acquire(PEM this, BOOL fAcquire);
  31. static BYTE s_rgbKbd[DIKBD_CKEYS];
  32. HHOOK g_hhkKbd;
  33. LPBYTE g_pbKbdXlat;
  34. ED s_edKbd = {
  35. &s_rgbKbd,
  36. 0,
  37. CEm_Kbd_Acquire,
  38. -1,
  39. cbX(s_rgbKbd),
  40. 0x0,
  41. };
  42. static BOOL s_fFarEastKbd;
  43. static BOOL fKbdCaptured;
  44. static BOOL fNoWinKey;
  45. /*****************************************************************************
  46. *
  47. * @doc INTERNAL
  48. *
  49. * @func LRESULT | CEm_Kbd_KeyboardHook |
  50. *
  51. * Thread-specific keyboard hook filter.
  52. *
  53. * Note that we need only one of these, since only the foreground
  54. * window will require a hook.
  55. *
  56. * @parm int | nCode |
  57. *
  58. * Notification code.
  59. *
  60. * @parm WPARAM | wp |
  61. *
  62. * VK_* code.
  63. *
  64. * @parm LPARAM | lp |
  65. *
  66. * Key message information.
  67. *
  68. * @returns
  69. *
  70. * Always chains to the next hook.
  71. *
  72. *****************************************************************************/
  73. LRESULT CALLBACK
  74. CEm_Kbd_KeyboardHook(int nCode, WPARAM wp, LPARAM lp)
  75. {
  76. BYTE bScan = 0x0;
  77. BYTE bAction;
  78. LRESULT lr;
  79. if (nCode == HC_ACTION || nCode == HC_NOREMOVE) {
  80. bScan = LOBYTE(HIWORD(lp));
  81. if (HIWORD(lp) & KF_EXTENDED) {
  82. bScan |= 0x80;
  83. }
  84. if (HIWORD(lp) & KF_UP) {
  85. bAction = 0;
  86. } else {
  87. bAction = 0x80;
  88. }
  89. bScan = g_pbKbdXlat[bScan];
  90. if( s_fFarEastKbd )
  91. {
  92. /*
  93. * Manually toggle these keys on make, ignore break
  94. */
  95. if( ( bScan == DIK_KANA )
  96. ||( bScan == DIK_KANJI )
  97. ||( bScan == DIK_CAPITAL ) )
  98. {
  99. if( bAction )
  100. {
  101. bAction = s_rgbKbd[bScan] ^ 0x80;
  102. }
  103. else
  104. {
  105. goto KbdHook_Skip;
  106. }
  107. }
  108. }
  109. CEm_AddEvent(&s_edKbd, bAction, bScan, GetMessageTime());
  110. KbdHook_Skip:;
  111. }
  112. lr = CallNextHookEx(g_hhkKbd, nCode, wp, lp);
  113. if( fKbdCaptured ) {
  114. // test Alt+Tab
  115. if( ((HIWORD(lp) & KF_ALTDOWN) && (bScan == 0x0F))
  116. || ((bScan == 0x38 || bScan == 0xb8) && bAction == 0)
  117. ) {
  118. } else {
  119. return TRUE;
  120. }
  121. } else if (fNoWinKey) {
  122. //If left_Winkey or right_WinKey pressed. We really should use virtual keys
  123. // if we could, but unfortunately no virtual key info is available.
  124. if( bScan == 0xdb || bScan == 0xdc ) {
  125. return TRUE;
  126. }
  127. }
  128. return lr;
  129. }
  130. /*****************************************************************************
  131. *
  132. * @doc INTERNAL
  133. *
  134. * @func HRESULT | CEm_Kbd_Hook_Acquire |
  135. *
  136. * Acquire/unacquire a keyboard via a thread hook.
  137. *
  138. * @parm PEM | pem |
  139. *
  140. * Device being acquired.
  141. *
  142. * @parm BOOL | fAcquire |
  143. *
  144. * Whether the device is being acquired or unacquired.
  145. *
  146. *****************************************************************************/
  147. STDMETHODIMP
  148. CEm_Kbd_Hook_Acquire(PEM this, BOOL fAcquire)
  149. {
  150. HRESULT hres;
  151. EnterProc(CEm_Kbd_Hook_Acquire, (_ "pu", this, fAcquire));
  152. AssertF(this->dwSignature == CEM_SIGNATURE);
  153. DllEnterCrit();
  154. if (fAcquire) { /* Install the hook */
  155. if (this->vi.hwnd) {
  156. if (!g_hhkKbd) {
  157. g_hhkKbd = SetWindowsHookEx(WH_KEYBOARD,
  158. CEm_Kbd_KeyboardHook, g_hinst,
  159. GetWindowThreadProcessId(this->vi.hwnd, 0));
  160. hres = S_OK;
  161. }
  162. else
  163. hres = E_FAIL; //already hooked
  164. } else {
  165. RPF("Kbd::Acquire: Background mode not supported");
  166. hres = E_FAIL;
  167. }
  168. } else { /* Remove the hook */
  169. UnhookWindowsHookEx(g_hhkKbd);
  170. g_hhkKbd = 0;
  171. hres = S_OK;
  172. }
  173. DllLeaveCrit();
  174. ExitOleProc();
  175. return hres;
  176. }
  177. /*****************************************************************************
  178. *
  179. * @doc INTERNAL
  180. *
  181. * @func HRESULT | CEm_Kbd_Acquire |
  182. *
  183. * Acquire/unacquire a keyboard in a manner consistent with the
  184. * emulation level.
  185. *
  186. * @parm PEM | pem |
  187. *
  188. * Device being acquired.
  189. *
  190. *****************************************************************************/
  191. STDMETHODIMP
  192. CEm_Kbd_Acquire(PEM this, BOOL fAcquire)
  193. {
  194. HRESULT hres;
  195. EnterProc(CEm_Kbd_Acquire, (_ "pu", this, fAcquire));
  196. AssertF(this->dwSignature == CEM_SIGNATURE);
  197. fKbdCaptured = FALSE;
  198. fNoWinKey = FALSE;
  199. if( fAcquire ) {
  200. if( this->vi.fl & VIFL_CAPTURED ) {
  201. fKbdCaptured = TRUE;
  202. } else if( this->vi.fl & VIFL_NOWINKEY ) {
  203. fNoWinKey = TRUE;
  204. }
  205. }
  206. #ifdef USE_SLOW_LL_HOOKS
  207. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_KBD ||
  208. DIGETEMFL(this->vi.fl) == DIEMFL_KBD2);
  209. if (this->vi.fl & DIMAKEEMFL(DIEMFL_KBD)) {
  210. AssertF(g_fUseLLHooks);
  211. hres = CEm_LL_Acquire(this, fAcquire, this->vi.fl, LLTS_KBD);
  212. } else {
  213. hres = CEm_Kbd_Hook_Acquire(this, fAcquire);
  214. }
  215. #else
  216. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_KBD2);
  217. hres = CEm_Kbd_Hook_Acquire(this, fAcquire);
  218. #endif
  219. ExitOleProc();
  220. return hres;
  221. }
  222. /*****************************************************************************
  223. *
  224. * @doc INTERNAL
  225. *
  226. * @func HRESULT | CEm_Kbd_CreateInstance |
  227. *
  228. * Create a keyboard thing. Also record what emulation
  229. * level we ended up with so the caller knows.
  230. *
  231. * @parm PVXDDEVICEFORMAT | pdevf |
  232. *
  233. * What the object should look like. The
  234. * <e VXDDEVICEFORMAT.dwEmulation> field is updated to specify
  235. * exactly what emulation we ended up with.
  236. *
  237. * @parm PVXDINSTANCE * | ppviOut |
  238. *
  239. * The answer goes here.
  240. *
  241. *****************************************************************************/
  242. HRESULT EXTERNAL
  243. CEm_Kbd_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
  244. {
  245. LPBYTE pbKbdXlat;
  246. #ifdef WINNT
  247. /*
  248. * In Win2K/WinXP, for legacy free machine, GetKeyboardType will return
  249. * unreliable result for non-PS2 keyboard. We will use the first time result
  250. * from GetKeyboardType (for GUID_SysKeyboard) which is also used by Generic
  251. * Input to do translation.
  252. * Related Windows bug: 363700.
  253. */
  254. if( !g_pbKbdXlat ) {
  255. #endif
  256. pbKbdXlat = (LPBYTE)pdevf->dwExtra;
  257. if (!pbKbdXlat) {
  258. pbKbdXlat = pvFindResource(g_hinst, IDDATA_KBD_PCENH, RT_RCDATA);
  259. }
  260. AssertF(pbKbdXlat);
  261. AssertF(fLimpFF(g_pbKbdXlat, g_pbKbdXlat == pbKbdXlat));
  262. g_pbKbdXlat = pbKbdXlat;
  263. #ifdef WINNT
  264. }
  265. #endif
  266. #ifdef USE_SLOW_LL_HOOKS
  267. /*
  268. * Note carefully the test. It handles the cases where
  269. *
  270. * 0. The app did not ask for emulation, so we give it the
  271. * best we can. (dwEmulation == 0)
  272. * 1. The app explicitly asked for emulation 1.
  273. * (dwEmulation == DIEMFL_KBD)
  274. * 2. The app explicitly asked for emulation 2.
  275. * (dwEmulation == DIEMFL_KBD2)
  276. * 3. The registry explicitly asked for both emulation modes.
  277. * (dwEmulation == DIEMFL_KBD | DIEMFL_KBD2)
  278. * Give it the best we can. (I.e., same as case 0.)
  279. *
  280. * All platforms support emulation 2. Not all platforms support
  281. * emulation 1. If we want emulation 1 but can't get it, then
  282. * we fall back on emulation 2.
  283. */
  284. /*
  285. * First, if we don't have support for emulation 1, then clearly
  286. * we have to use emulation 2.
  287. */
  288. if (!g_fUseLLHooks
  289. #ifdef DEBUG
  290. || (g_flEmulation & DIEMFL_KBD2)
  291. #endif
  292. ) {
  293. pdevf->dwEmulation = DIEMFL_KBD2;
  294. } else
  295. /*
  296. * Otherwise, we have to choose between 1 and 2. The only case
  297. * where we choose 2 is if 2 is explicitly requested.
  298. */
  299. if (pdevf->dwEmulation == DIEMFL_KBD2) {
  300. /* Do nothing */
  301. } else
  302. /*
  303. * All other cases get 1.
  304. */
  305. {
  306. pdevf->dwEmulation = DIEMFL_KBD;
  307. }
  308. /*
  309. * Assert that we never give emulation 1 when it doesn't exist.
  310. */
  311. AssertF(fLimpFF(pdevf->dwEmulation & DIEMFL_KBD, g_fUseLLHooks));
  312. /*
  313. * Assert that exactly one emulation flag is selected.
  314. */
  315. AssertF(pdevf->dwEmulation == DIEMFL_KBD ||
  316. pdevf->dwEmulation == DIEMFL_KBD2);
  317. #else
  318. /*
  319. * We are being compiled for "emulation 2 only", so that simplifies
  320. * matters immensely.
  321. */
  322. pdevf->dwEmulation = DIEMFL_KBD2;
  323. #endif
  324. return CEm_CreateInstance(pdevf, ppviOut, &s_edKbd);
  325. }
  326. /*****************************************************************************
  327. *
  328. * @doc INTERNAL
  329. *
  330. * @func HRESULT | CEm_Kbd_InitKeys |
  331. *
  332. * Initialize pieces of the keyboard state in preparation for
  333. * acquisition.
  334. *
  335. * @parm PVXDDWORDDATA | pvdd |
  336. *
  337. * The states of the <c VK_KANA> and <c VK_CAPITAL> keys.
  338. *
  339. *****************************************************************************/
  340. HRESULT EXTERNAL
  341. CEm_Kbd_InitKeys(PVXDDWORDDATA pvdd)
  342. {
  343. /* Do this only when not acquired */
  344. if (s_edKbd.cAcquire < 0) {
  345. ZeroX(s_rgbKbd);
  346. if (pvdd->dw & 1) {
  347. s_rgbKbd[DIK_KANA] = 0x80;
  348. }
  349. if (pvdd->dw & 2) {
  350. s_rgbKbd[DIK_CAPITAL] = 0x80;
  351. }
  352. if (pvdd->dw & 8) {
  353. s_rgbKbd[DIK_KANJI] = 0x80;
  354. }
  355. s_fFarEastKbd = ((pvdd->dw & 16)) != 0;
  356. }
  357. return S_OK;
  358. }
  359. #ifdef USE_SLOW_LL_HOOKS
  360. /*****************************************************************************
  361. *
  362. * @doc INTERNAL
  363. *
  364. * @func LRESULT | CEm_LL_KbdHook |
  365. *
  366. * Low-level keyboard hook filter.
  367. *
  368. * @parm int | nCode |
  369. *
  370. * Notification code.
  371. *
  372. * @parm WPARAM | wp |
  373. *
  374. * WM_* keyboard message.
  375. *
  376. * @parm LPARAM | lp |
  377. *
  378. * Key message information.
  379. *
  380. * @returns
  381. *
  382. * Always chains to the next hook.
  383. *
  384. *****************************************************************************/
  385. LRESULT CALLBACK
  386. CEm_LL_KbdHook(int nCode, WPARAM wp, LPARAM lp)
  387. {
  388. PLLTHREADSTATE plts;
  389. PKBDLLHOOKSTRUCT pllhs = (PV)lp;
  390. if (nCode == HC_ACTION) {
  391. BYTE bScan = 0x0;
  392. BYTE bAction;
  393. D(DWORD tmStart = GetTickCount());
  394. wp; /* We don't care what the msg is */
  395. bScan = (BYTE)pllhs->scanCode;
  396. if( !bScan )
  397. {
  398. /*
  399. * ISSUE-2001/03/29-timgill Special case for non-standard VK codes
  400. * The bonus keys on some USB keyboards have zero scan code and
  401. * the extended key flag is clear.
  402. * Get the scan code by mapping the VK, then map the
  403. * scan code back, if it is the same as the original VK assume
  404. * the scan code is not extended otherwise assume it is.
  405. * This is no where near full proof and only works at all
  406. * because non-extended scan codes are matched first so extended
  407. * scan codes normally fail to translate back.
  408. */
  409. bScan = (BYTE)MapVirtualKey( pllhs->vkCode, 0 );
  410. if( MapVirtualKey( bScan, 3 ) != pllhs->vkCode )
  411. {
  412. bScan |= 0x80;
  413. }
  414. }
  415. else if (pllhs->flags & LLKHF_EXTENDED) {
  416. bScan |= 0x80;
  417. }
  418. if (pllhs->flags & LLKHF_UP) {
  419. bAction = 0;
  420. } else {
  421. bAction = 0x80;
  422. }
  423. bScan = g_pbKbdXlat[bScan];
  424. if( s_fFarEastKbd )
  425. {
  426. /*
  427. * Manually toggle these keys on make, ignore break
  428. */
  429. if( ( bScan == DIK_KANA )
  430. ||( bScan == DIK_KANJI )
  431. ||( bScan == DIK_CAPITAL ) )
  432. {
  433. if( bAction )
  434. {
  435. bAction = s_rgbKbd[bScan] ^ 0x80;
  436. }
  437. else
  438. {
  439. D(SquirtSqflPtszV(sqflTrace | sqfl,
  440. TEXT("KBD! vk=%02x, scan=%02x, fl=%08x, tm=%08x")
  441. TEXT(" being skipped"),
  442. pllhs->vkCode, pllhs->scanCode, pllhs->flags,
  443. pllhs->time );)
  444. goto LLKbdHook_Skip;
  445. }
  446. }
  447. }
  448. CEm_AddEvent(&s_edKbd, bAction, bScan, GetTickCount());
  449. D(SquirtSqflPtszV(sqflTrace | sqfl,
  450. TEXT("KBD! vk=%02x, scan=%02x, fl=%08x, tm=%08x, ")
  451. TEXT("in=%08x, out=%08x"),
  452. pllhs->vkCode, pllhs->scanCode, pllhs->flags,
  453. pllhs->time, tmStart, GetTickCount()));
  454. LLKbdHook_Skip:;
  455. }
  456. /*
  457. * ISSUE-2001/03/29-timgill Need method for detecting Ctrl-Alt-Del
  458. * If Ctrl+Alt+Del, then force global unacquire!
  459. * Need to re-sync Ctrl, Alt, and Del on next keypress.
  460. * Unfortunately, there is no way to find out if Ctrl+Alt+Del
  461. * has been pressed...
  462. */
  463. plts = g_plts;
  464. if (plts) {
  465. LRESULT lr;
  466. lr = CallNextHookEx(plts->rglhs[LLTS_KBD].hhk, nCode, wp, lp);
  467. if( fKbdCaptured ) {
  468. if( ((pllhs->flags & LLKHF_ALTDOWN) && (pllhs->vkCode == VK_TAB)) ||
  469. ((pllhs->flags & LLKHF_UP) && (pllhs->vkCode == VK_LMENU || pllhs->vkCode == VK_RMENU))
  470. ) {
  471. return lr;
  472. } else {
  473. return TRUE;
  474. }
  475. } else if (fNoWinKey) {
  476. if( pllhs->vkCode == VK_LWIN || pllhs->vkCode == VK_RWIN ) {
  477. return TRUE;
  478. } else {
  479. return lr;
  480. }
  481. } else {
  482. return lr;
  483. }
  484. } else {
  485. /*
  486. * This can happen if a message gets posted to the hook after
  487. * releasing the last acquire but before we completely unhook.
  488. */
  489. RPF( "DINPUT: Keyboard hook not passed on to next hook" );
  490. return 1;
  491. }
  492. }
  493. #endif /* USE_SLOW_LL_HOOKS */