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.

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