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.

646 lines
15 KiB

  1. //
  2. // IM.C
  3. // Input Manager
  4. //
  5. // Copyright(c) 1997-
  6. //
  7. #include <as16.h>
  8. //
  9. // IM_DDInit()
  10. // This creates the resources we need for controlling
  11. //
  12. BOOL IM_DDInit(void)
  13. {
  14. UINT uSel;
  15. BOOL rc = FALSE;
  16. DebugEntry(IM_DDInit);
  17. //
  18. // Create interrupt patches for mouse_event and keybd_event
  19. //
  20. uSel = CreateFnPatch(mouse_event, ASMMouseEvent, &g_imPatches[IM_MOUSEEVENT], 0);
  21. if (!uSel)
  22. {
  23. ERROR_OUT(("Couldn't find mouse_event"));
  24. DC_QUIT;
  25. }
  26. g_imPatches[IM_MOUSEEVENT].fInterruptable = TRUE;
  27. if (!CreateFnPatch(keybd_event, ASMKeyboardEvent, &g_imPatches[IM_KEYBOARDEVENT], uSel))
  28. {
  29. ERROR_OUT(("Couldn't find keybd_event"));
  30. DC_QUIT;
  31. }
  32. g_imPatches[IM_KEYBOARDEVENT].fInterruptable = TRUE;
  33. //
  34. // Create patch for SignalProc32 so we can find out when fault/hung
  35. // dialogs from KERNEL32 come up.
  36. //
  37. if (!CreateFnPatch(SignalProc32, DrvSignalProc32, &g_imPatches[IM_SIGNALPROC32], 0))
  38. {
  39. ERROR_OUT(("Couldn't patch SignalProc32"));
  40. DC_QUIT;
  41. }
  42. //
  43. // Create patches for win16lock pulsing in 16-bit app modal loops
  44. //
  45. uSel = CreateFnPatch(RealGetCursorPos, DrvGetCursorPos, &g_imPatches[IM_GETCURSORPOS], 0);
  46. if (!uSel)
  47. {
  48. ERROR_OUT(("Couldn't find GetCursorPos"));
  49. DC_QUIT;
  50. }
  51. if (!CreateFnPatch(GetAsyncKeyState, DrvGetAsyncKeyState, &g_imPatches[IM_GETASYNCKEYSTATE], 0))
  52. {
  53. ERROR_OUT(("Couldn't find GetAsyncKeyState"));
  54. DC_QUIT;
  55. }
  56. rc = TRUE;
  57. DC_EXIT_POINT:
  58. DebugExitBOOL(IM_DDInit, rc);
  59. return(rc);
  60. }
  61. //
  62. // IM_DDTerm()
  63. // This cleans up any resources we needed for controlling
  64. //
  65. void IM_DDTerm(void)
  66. {
  67. IM_PATCH imPatch;
  68. DebugEntry(IM_DDTerm);
  69. //
  70. // Force undo of hooks
  71. //
  72. OSIInstallControlledHooks16(FALSE, FALSE);
  73. //
  74. // Destroy patches
  75. //
  76. for (imPatch = IM_FIRST; imPatch < IM_MAX; imPatch++)
  77. {
  78. DestroyFnPatch(&g_imPatches[imPatch]);
  79. }
  80. DebugExitVOID(IM_DDTerm);
  81. }
  82. //
  83. // OSIInstallControlledHooks16()
  84. //
  85. // This installs/removes the input hooks we need to allow this machine to
  86. // be controlled.
  87. //
  88. BOOL WINAPI OSIInstallControlledHooks16(BOOL fEnable, BOOL fDesktop)
  89. {
  90. BOOL rc = TRUE;
  91. IM_PATCH imPatch;
  92. DebugEntry(OSIInstallControlledHooks16);
  93. if (fEnable)
  94. {
  95. if (!g_imWin95Data.imLowLevelHooks)
  96. {
  97. g_imWin95Data.imLowLevelHooks = TRUE;
  98. g_imMouseDowns = 0;
  99. //
  100. // GlobalSmartPageLock() stuff we need:
  101. // * Our code segment
  102. // * Our data segment
  103. //
  104. GlobalSmartPageLock((HGLOBAL)SELECTOROF((LPVOID)DrvMouseEvent));
  105. GlobalSmartPageLock((HGLOBAL)SELECTOROF((LPVOID)&g_imSharedData));
  106. //
  107. // Install hooks
  108. //
  109. for (imPatch = IM_FIRST; imPatch < IM_MAX; imPatch++)
  110. {
  111. EnableFnPatch(&g_imPatches[imPatch], PATCH_ACTIVATE);
  112. }
  113. }
  114. //
  115. // Install high-level mouse hook
  116. if (!fDesktop)
  117. {
  118. if (!g_imWin95Data.imhHighLevelMouseHook)
  119. {
  120. //
  121. // Install the mouse hook.
  122. //
  123. g_imWin95Data.imhHighLevelMouseHook = SetWindowsHookEx(WH_MOUSE,
  124. IMMouseHookProc, g_hInstAs16, 0);
  125. if (!g_imWin95Data.imhHighLevelMouseHook)
  126. {
  127. ERROR_OUT(("Failed to install mouse hook"));
  128. rc = FALSE;
  129. }
  130. }
  131. }
  132. }
  133. else
  134. {
  135. if (g_imWin95Data.imLowLevelHooks)
  136. {
  137. //
  138. // Uninstall hooks
  139. //
  140. for (imPatch = IM_MAX; imPatch > 0; imPatch--)
  141. {
  142. EnableFnPatch(&g_imPatches[imPatch-1], PATCH_DEACTIVATE);
  143. }
  144. //
  145. // GlobalSmartUnPageLock() stuff we needed
  146. //
  147. GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)&g_imSharedData));
  148. GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)DrvMouseEvent));
  149. g_imWin95Data.imLowLevelHooks = FALSE;
  150. }
  151. if (!fDesktop)
  152. {
  153. if (g_imWin95Data.imhHighLevelMouseHook)
  154. {
  155. //
  156. // Remove the mouse hook.
  157. //
  158. UnhookWindowsHookEx(g_imWin95Data.imhHighLevelMouseHook);
  159. g_imWin95Data.imhHighLevelMouseHook = NULL;
  160. }
  161. }
  162. }
  163. DebugExitBOOL(OSIInstallControlledHooks16, rc);
  164. return(rc);
  165. }
  166. #pragma optimize("gle", off)
  167. void IMInject(BOOL fOn)
  168. {
  169. if (fOn)
  170. {
  171. #ifdef DEBUG
  172. DWORD tmp;
  173. //
  174. // Disable interrupts then turn injection global on
  175. // But before we do this, we must make sure that we aren't going
  176. // to have to fault in a new stack page. Since this is on a 32-bit
  177. // thread, we will be in trouble.
  178. //
  179. tmp = GetSelectorBase(SELECTOROF(((LPVOID)&fOn))) +
  180. OFFSETOF((LPVOID)&fOn);
  181. if ((tmp & 0xFFFFF000) != ((tmp - 0x100) & 0xFFFFF000))
  182. {
  183. ERROR_OUT(("Close to page boundary on 32-bit stack %08lx", tmp));
  184. }
  185. #endif // DEBUG
  186. _asm cli
  187. g_imWin95Data.imInjecting = TRUE;
  188. }
  189. else
  190. {
  191. //
  192. // Turn injection global off then enable interrupts
  193. //
  194. g_imWin95Data.imInjecting = FALSE;
  195. _asm sti
  196. }
  197. }
  198. #pragma optimize("", on)
  199. //
  200. // OSIInjectMouseEvent16()
  201. //
  202. void WINAPI OSIInjectMouseEvent16
  203. (
  204. UINT flags,
  205. int x,
  206. int y,
  207. UINT mouseData,
  208. DWORD dwExtraInfo
  209. )
  210. {
  211. DebugEntry(OSIInjectMouseEvent16);
  212. if (flags & IM_MOUSEEVENTF_BUTTONDOWN_FLAGS)
  213. {
  214. ++g_imMouseDowns;
  215. }
  216. //
  217. // We disable interrupts, call the real mouse_event, reenable
  218. // interrupts. That way our mouse_event patch is serialized.
  219. // And we can check imInjecting.
  220. //
  221. IMInject(TRUE);
  222. CallMouseEvent(flags, x, y, mouseData, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
  223. IMInject(FALSE);
  224. if (flags & IM_MOUSEEVENTF_BUTTONUP_FLAGS)
  225. {
  226. --g_imMouseDowns;
  227. ASSERT(g_imMouseDowns >= 0);
  228. }
  229. DebugExitVOID(OSIInjectMouseEvent16);
  230. }
  231. //
  232. // OSIInjectKeyboardEvent16()
  233. //
  234. void WINAPI OSIInjectKeyboardEvent16
  235. (
  236. UINT flags,
  237. WORD vkCode,
  238. WORD scanCode,
  239. DWORD dwExtraInfo
  240. )
  241. {
  242. DebugEntry(OSIInjectKeyboardEvent16);
  243. //
  244. // First, fix up the flags
  245. //
  246. if (flags & KEYEVENTF_KEYUP)
  247. {
  248. // Put 0x80 in the HIBYTE of vkCode, this means a keyup
  249. vkCode = (WORD)(BYTE)vkCode | USERKEYEVENTF_KEYUP;
  250. }
  251. if (flags & KEYEVENTF_EXTENDEDKEY)
  252. {
  253. // Put 0x01 in the HIBYTE of scanCode, this means extended
  254. scanCode = (WORD)(BYTE)scanCode | USERKEYEVENTF_EXTENDEDKEY;
  255. }
  256. //
  257. // We disable interrupts, call the real keybd_event, reenable
  258. // interrupts. That way our keybd_event patch is serialized.
  259. // And we can check the imfInject variable.
  260. //
  261. IMInject(TRUE);
  262. CallKeyboardEvent(vkCode, scanCode, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
  263. IMInject(FALSE);
  264. DebugExitVOID(OSIInjectKeyboardEvent16);
  265. }
  266. //
  267. // Win16lock pulse points when injecting mouse down/up sequences into 16-bit
  268. // modal loop apps.
  269. //
  270. //
  271. // IMCheckWin16LockPulse()
  272. // This pulses the win16lock if we are in the middle of injecting a mouse
  273. // down-up sequence to a 16-bit app shared on this machine. We do this to
  274. // prevent deadlock, caused by 16-bit dudes going into modal loops, not
  275. // releasing the win16lock. Our 32-bit thread would get stuck on the win16
  276. // lock trying to play back the rest of the sequence.
  277. //
  278. void IMCheckWin16LockPulse(void)
  279. {
  280. DebugEntry(IMCheckWin16LockPulse);
  281. if ((g_imMouseDowns > 0) &&
  282. (GetProcessDword(0, GPD_FLAGS) & GPF_WIN16_PROCESS))
  283. {
  284. TRACE_OUT(("Pulsing win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
  285. _LeaveWin16Lock();
  286. _EnterWin16Lock();
  287. TRACE_OUT(("Pulsed win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
  288. }
  289. DebugExitVOID(IMCheckWin16LockPulse);
  290. }
  291. int WINAPI DrvGetAsyncKeyState(int vk)
  292. {
  293. int retVal;
  294. DebugEntry(DrvGetAsyncKeyState);
  295. // Pulse BEFORE we call to USER
  296. IMCheckWin16LockPulse();
  297. EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_DISABLE);
  298. retVal = GetAsyncKeyState(vk);
  299. EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_ENABLE);
  300. DebugExitBOOL(DrvGetAsyncKeyState, retVal);
  301. return(retVal);
  302. }
  303. //
  304. // DrvGetCursorPos()
  305. //
  306. BOOL WINAPI DrvGetCursorPos(LPPOINT lppt)
  307. {
  308. BOOL retVal;
  309. DebugEntry(DrvGetCursorPos);
  310. // Pulse BEFORE calling USER
  311. IMCheckWin16LockPulse();
  312. EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_DISABLE);
  313. retVal = RealGetCursorPos(lppt);
  314. EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_ENABLE);
  315. DebugExitBOOL(DrvGetCursorPos, retVal);
  316. return(retVal);
  317. }
  318. //
  319. // IMMouseHookProc()
  320. // High level mouse hook to make sure mice messages are going where we
  321. // think they should when your machine is being controlled.
  322. //
  323. LRESULT CALLBACK IMMouseHookProc
  324. (
  325. int code,
  326. WPARAM wParam,
  327. LPARAM lParam
  328. )
  329. {
  330. LRESULT rc;
  331. BOOL fBlock = FALSE;
  332. LPMOUSEHOOKSTRUCT lpMseHook = (LPMOUSEHOOKSTRUCT)lParam;
  333. DebugEntry(IMMouseHookProc);
  334. if (code < 0)
  335. {
  336. //
  337. // Only pass along
  338. //
  339. DC_QUIT;
  340. }
  341. //
  342. // Decide if we should block this event. We will if it is not destined
  343. // for a hosted window and not destined for a screen saver.
  344. //
  345. if (!HET_WindowIsHosted(lpMseHook->hwnd) &&
  346. !OSIIsWindowScreenSaver16(lpMseHook->hwnd))
  347. {
  348. fBlock = TRUE;
  349. }
  350. TRACE_OUT(("MOUSEHOOK hwnd %04x -> block: %s",
  351. lpMseHook->hwnd,
  352. (fBlock ? (LPSTR)"YES" : (LPSTR)"NO")));
  353. DC_EXIT_POINT:
  354. //
  355. // Call the next hook
  356. //
  357. rc = CallNextHookEx(g_imWin95Data.imhHighLevelMouseHook, code, wParam, lParam);
  358. if (fBlock)
  359. {
  360. //
  361. // To block further processing in USER, return TRUE
  362. //
  363. rc = TRUE;
  364. }
  365. DebugExitDWORD(IMMouseHookProc, rc);
  366. return(rc);
  367. }
  368. //
  369. // DrvMouseEvent()
  370. // mouse_event interrupt patch
  371. //
  372. void WINAPI DrvMouseEvent
  373. (
  374. UINT regAX,
  375. UINT regBX,
  376. UINT regCX,
  377. UINT regDX,
  378. UINT regSI,
  379. UINT regDI
  380. )
  381. {
  382. BOOL fAllow;
  383. //
  384. // If this is injected by us, just pass it through.
  385. //
  386. fAllow = TRUE;
  387. if (g_imWin95Data.imInjecting)
  388. {
  389. DC_QUIT;
  390. }
  391. //
  392. // NOTE:
  393. // flags is in AX
  394. // x coord is in BX
  395. // y coord is in CX
  396. // mousedata is in DX
  397. // dwExtraInfo is in DI, SI
  398. //
  399. if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
  400. {
  401. //
  402. // If this is a button click, take control back
  403. //
  404. if (regAX &
  405. (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_MIDDLEDOWN))
  406. {
  407. if (!g_imSharedData.imUnattended)
  408. {
  409. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, FALSE, 0);
  410. }
  411. }
  412. if (!g_imSharedData.imSuspended)
  413. fAllow = FALSE;
  414. }
  415. DC_EXIT_POINT:
  416. if (fAllow)
  417. {
  418. EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_DISABLE);
  419. CallMouseEvent(regAX, regBX, regCX, regDX, regSI, regDI);
  420. EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_ENABLE);
  421. }
  422. }
  423. //
  424. // DrvKeyboardEvent()
  425. // keybd_event interrupt patch
  426. //
  427. void WINAPI DrvKeyboardEvent
  428. (
  429. UINT regAX,
  430. UINT regBX,
  431. UINT regSI,
  432. UINT regDI
  433. )
  434. {
  435. BOOL fAllow;
  436. //
  437. // If this is injected by us, pass it through. Do the same for
  438. // critical errors, since everything is frozen and we can't play back
  439. // input if we wanted to.
  440. //
  441. // If the scan-code (in regBX) is 0 we assume that the input
  442. // is injected by an application (such as an IME) and we don't
  443. // want to block this or take control.
  444. //
  445. fAllow = TRUE;
  446. if (g_imWin95Data.imInjecting || !regBX)
  447. {
  448. DC_QUIT;
  449. }
  450. //
  451. // NOTE:
  452. // vkCode is in AX, LOBYTE is vkCode, HIBYTE is state
  453. // scanCode is in BX
  454. // dwExtraInfo is in DI, SI
  455. //
  456. if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
  457. {
  458. if (!(regAX & USERKEYEVENTF_KEYUP))
  459. {
  460. //
  461. // This is a key down. Take control back (except for ALT key),
  462. // and kill control allowability if it's the ESC key.
  463. //
  464. if (LOBYTE(regAX) == VK_ESCAPE)
  465. {
  466. PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
  467. }
  468. else if (LOBYTE(regAX != VK_MENU))
  469. {
  470. if (!g_imSharedData.imUnattended)
  471. {
  472. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  473. }
  474. }
  475. }
  476. //
  477. // Don't discard toggle keys. The enabled/disabled function
  478. // is already set before we see the keystroke. If we discard,
  479. // the lights are incorrect.
  480. //
  481. if (!IM_KEY_IS_TOGGLE(LOBYTE(regAX)) && !g_imSharedData.imSuspended)
  482. {
  483. fAllow = FALSE;
  484. }
  485. }
  486. DC_EXIT_POINT:
  487. if (fAllow)
  488. {
  489. EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_DISABLE);
  490. CallKeyboardEvent(regAX, regBX, regSI, regDI);
  491. EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_ENABLE);
  492. }
  493. }
  494. //
  495. // DrvSignalProc32()
  496. // This patches USER's SignalProc32 export and watches for the FORCE_LOCK
  497. // signals. KERNEL32 calls them before/after putting up critical error and
  498. // fault dialogs. That's how we know when one is coming up, and can
  499. // temporarily suspend remote control of your machine so you can dismiss
  500. // them. Usually the thread they are on is boosted so high priority that
  501. // nothing else can run, and so NM can't pump in input from the remote.
  502. //
  503. BOOL WINAPI DrvSignalProc32
  504. (
  505. DWORD dwSignal,
  506. DWORD dwID,
  507. DWORD dwFlags,
  508. WORD hTask16
  509. )
  510. {
  511. BOOL fRet;
  512. DebugEntry(DrvSignalProc32);
  513. if (dwSignal == SIG_PRE_FORCE_LOCK)
  514. {
  515. TRACE_OUT(("Disabling remote control before critical dialog, count %ld",
  516. g_imSharedData.imSuspended));
  517. ++g_imSharedData.imSuspended;
  518. }
  519. EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_DISABLE);
  520. fRet = SignalProc32(dwSignal, dwID, dwFlags, hTask16);
  521. EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_ENABLE);
  522. if (dwSignal == SIG_POST_FORCE_LOCK)
  523. {
  524. --g_imSharedData.imSuspended;
  525. TRACE_OUT(("Enabling remote control after critical dialog, count %ld",
  526. g_imSharedData.imSuspended));
  527. }
  528. DebugExitBOOL(DrvSignalProc32, fRet);
  529. return(fRet);
  530. }