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.

548 lines
13 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);
  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)
  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. else
  116. {
  117. if (g_imWin95Data.imLowLevelHooks)
  118. {
  119. //
  120. // Uninstall hooks
  121. //
  122. for (imPatch = IM_MAX; imPatch > 0; imPatch--)
  123. {
  124. EnableFnPatch(&g_imPatches[imPatch-1], PATCH_DEACTIVATE);
  125. }
  126. //
  127. // GlobalSmartUnPageLock() stuff we needed
  128. //
  129. GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)&g_imSharedData));
  130. GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)DrvMouseEvent));
  131. g_imWin95Data.imLowLevelHooks = FALSE;
  132. }
  133. }
  134. DebugExitBOOL(OSIInstallControlledHooks16, rc);
  135. return(rc);
  136. }
  137. #pragma optimize("gle", off)
  138. void IMInject(BOOL fOn)
  139. {
  140. if (fOn)
  141. {
  142. #ifdef DEBUG
  143. DWORD tmp;
  144. //
  145. // Disable interrupts then turn injection global on
  146. // But before we do this, we must make sure that we aren't going
  147. // to have to fault in a new stack page. Since this is on a 32-bit
  148. // thread, we will be in trouble.
  149. //
  150. tmp = GetSelectorBase(SELECTOROF(((LPVOID)&fOn))) +
  151. OFFSETOF((LPVOID)&fOn);
  152. if ((tmp & 0xFFFFF000) != ((tmp - 0x100) & 0xFFFFF000))
  153. {
  154. ERROR_OUT(("Close to page boundary on 32-bit stack %08lx", tmp));
  155. }
  156. #endif // DEBUG
  157. _asm cli
  158. g_imWin95Data.imInjecting = TRUE;
  159. }
  160. else
  161. {
  162. //
  163. // Turn injection global off then enable interrupts
  164. //
  165. g_imWin95Data.imInjecting = FALSE;
  166. _asm sti
  167. }
  168. }
  169. #pragma optimize("", on)
  170. //
  171. // OSIInjectMouseEvent16()
  172. //
  173. void WINAPI OSIInjectMouseEvent16
  174. (
  175. UINT flags,
  176. int x,
  177. int y,
  178. UINT mouseData,
  179. DWORD dwExtraInfo
  180. )
  181. {
  182. DebugEntry(OSIInjectMouseEvent16);
  183. if (flags & IM_MOUSEEVENTF_BUTTONDOWN_FLAGS)
  184. {
  185. ++g_imMouseDowns;
  186. }
  187. //
  188. // We disable interrupts, call the real mouse_event, reenable
  189. // interrupts. That way our mouse_event patch is serialized.
  190. // And we can check imInjecting.
  191. //
  192. IMInject(TRUE);
  193. CallMouseEvent(flags, x, y, mouseData, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
  194. IMInject(FALSE);
  195. if (flags & IM_MOUSEEVENTF_BUTTONUP_FLAGS)
  196. {
  197. --g_imMouseDowns;
  198. ASSERT(g_imMouseDowns >= 0);
  199. }
  200. DebugExitVOID(OSIInjectMouseEvent16);
  201. }
  202. //
  203. // OSIInjectKeyboardEvent16()
  204. //
  205. void WINAPI OSIInjectKeyboardEvent16
  206. (
  207. UINT flags,
  208. WORD vkCode,
  209. WORD scanCode,
  210. DWORD dwExtraInfo
  211. )
  212. {
  213. DebugEntry(OSIInjectKeyboardEvent16);
  214. //
  215. // First, fix up the flags
  216. //
  217. if (flags & KEYEVENTF_KEYUP)
  218. {
  219. // Put 0x80 in the HIBYTE of vkCode, this means a keyup
  220. vkCode = (WORD)(BYTE)vkCode | USERKEYEVENTF_KEYUP;
  221. }
  222. if (flags & KEYEVENTF_EXTENDEDKEY)
  223. {
  224. // Put 0x01 in the HIBYTE of scanCode, this means extended
  225. scanCode = (WORD)(BYTE)scanCode | USERKEYEVENTF_EXTENDEDKEY;
  226. }
  227. //
  228. // We disable interrupts, call the real keybd_event, reenable
  229. // interrupts. That way our keybd_event patch is serialized.
  230. // And we can check the imfInject variable.
  231. //
  232. IMInject(TRUE);
  233. CallKeyboardEvent(vkCode, scanCode, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
  234. IMInject(FALSE);
  235. DebugExitVOID(OSIInjectKeyboardEvent16);
  236. }
  237. //
  238. // Win16lock pulse points when injecting mouse down/up sequences into 16-bit
  239. // modal loop apps.
  240. //
  241. //
  242. // IMCheckWin16LockPulse()
  243. // This pulses the win16lock if we are in the middle of injecting a mouse
  244. // down-up sequence to a 16-bit app shared on this machine. We do this to
  245. // prevent deadlock, caused by 16-bit dudes going into modal loops, not
  246. // releasing the win16lock. Our 32-bit thread would get stuck on the win16
  247. // lock trying to play back the rest of the sequence.
  248. //
  249. void IMCheckWin16LockPulse(void)
  250. {
  251. DebugEntry(IMCheckWin16LockPulse);
  252. if ((g_imMouseDowns > 0) &&
  253. (GetProcessDword(0, GPD_FLAGS) & GPF_WIN16_PROCESS))
  254. {
  255. TRACE_OUT(("Pulsing win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
  256. _LeaveWin16Lock();
  257. _EnterWin16Lock();
  258. TRACE_OUT(("Pulsed win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
  259. }
  260. DebugExitVOID(IMCheckWin16LockPulse);
  261. }
  262. int WINAPI DrvGetAsyncKeyState(int vk)
  263. {
  264. int retVal;
  265. DebugEntry(DrvGetAsyncKeyState);
  266. // Pulse BEFORE we call to USER
  267. IMCheckWin16LockPulse();
  268. EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_DISABLE);
  269. retVal = GetAsyncKeyState(vk);
  270. EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_ENABLE);
  271. DebugExitBOOL(DrvGetAsyncKeyState, retVal);
  272. return(retVal);
  273. }
  274. //
  275. // DrvGetCursorPos()
  276. //
  277. BOOL WINAPI DrvGetCursorPos(LPPOINT lppt)
  278. {
  279. BOOL retVal;
  280. DebugEntry(DrvGetCursorPos);
  281. // Pulse BEFORE calling USER
  282. IMCheckWin16LockPulse();
  283. EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_DISABLE);
  284. retVal = RealGetCursorPos(lppt);
  285. EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_ENABLE);
  286. DebugExitBOOL(DrvGetCursorPos, retVal);
  287. return(retVal);
  288. }
  289. //
  290. // DrvMouseEvent()
  291. // mouse_event interrupt patch
  292. //
  293. void WINAPI DrvMouseEvent
  294. (
  295. UINT regAX,
  296. UINT regBX,
  297. UINT regCX,
  298. UINT regDX,
  299. UINT regSI,
  300. UINT regDI
  301. )
  302. {
  303. BOOL fAllow;
  304. //
  305. // If this is injected by us, just pass it through.
  306. //
  307. fAllow = TRUE;
  308. if (g_imWin95Data.imInjecting)
  309. {
  310. DC_QUIT;
  311. }
  312. //
  313. // NOTE:
  314. // flags is in AX
  315. // x coord is in BX
  316. // y coord is in CX
  317. // mousedata is in DX
  318. // dwExtraInfo is in DI, SI
  319. //
  320. if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
  321. {
  322. //
  323. // If this is a button click, take control back
  324. //
  325. if (regAX &
  326. (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_MIDDLEDOWN))
  327. {
  328. if (!g_imSharedData.imUnattended)
  329. {
  330. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, FALSE, 0);
  331. }
  332. }
  333. if (!g_imSharedData.imSuspended)
  334. fAllow = FALSE;
  335. }
  336. DC_EXIT_POINT:
  337. if (fAllow)
  338. {
  339. EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_DISABLE);
  340. CallMouseEvent(regAX, regBX, regCX, regDX, regSI, regDI);
  341. EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_ENABLE);
  342. }
  343. }
  344. //
  345. // DrvKeyboardEvent()
  346. // keybd_event interrupt patch
  347. //
  348. void WINAPI DrvKeyboardEvent
  349. (
  350. UINT regAX,
  351. UINT regBX,
  352. UINT regSI,
  353. UINT regDI
  354. )
  355. {
  356. BOOL fAllow;
  357. //
  358. // If this is injected by us, pass it through. Do the same for
  359. // critical errors, since everything is frozen and we can't play back
  360. // input if we wanted to.
  361. //
  362. // If the scan-code (in regBX) is 0 we assume that the input
  363. // is injected by an application (such as an IME) and we don't
  364. // want to block this or take control.
  365. //
  366. fAllow = TRUE;
  367. if (g_imWin95Data.imInjecting || !regBX)
  368. {
  369. DC_QUIT;
  370. }
  371. //
  372. // NOTE:
  373. // vkCode is in AX, LOBYTE is vkCode, HIBYTE is state
  374. // scanCode is in BX
  375. // dwExtraInfo is in DI, SI
  376. //
  377. if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
  378. {
  379. if (!(regAX & USERKEYEVENTF_KEYUP))
  380. {
  381. //
  382. // This is a key down. Take control back (except for ALT key),
  383. // and kill control allowability if it's the ESC key.
  384. //
  385. if (LOBYTE(regAX) == VK_ESCAPE)
  386. {
  387. PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
  388. }
  389. else if (LOBYTE(regAX != VK_MENU))
  390. {
  391. if (!g_imSharedData.imUnattended)
  392. {
  393. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  394. }
  395. }
  396. }
  397. //
  398. // Don't discard toggle keys. The enabled/disabled function
  399. // is already set before we see the keystroke. If we discard,
  400. // the lights are incorrect.
  401. //
  402. if (!IM_KEY_IS_TOGGLE(LOBYTE(regAX)) && !g_imSharedData.imSuspended)
  403. {
  404. fAllow = FALSE;
  405. }
  406. }
  407. DC_EXIT_POINT:
  408. if (fAllow)
  409. {
  410. EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_DISABLE);
  411. CallKeyboardEvent(regAX, regBX, regSI, regDI);
  412. EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_ENABLE);
  413. }
  414. }
  415. //
  416. // DrvSignalProc32()
  417. // This patches USER's SignalProc32 export and watches for the FORCE_LOCK
  418. // signals. KERNEL32 calls them before/after putting up critical error and
  419. // fault dialogs. That's how we know when one is coming up, and can
  420. // temporarily suspend remote control of your machine so you can dismiss
  421. // them. Usually the thread they are on is boosted so high priority that
  422. // nothing else can run, and so NM can't pump in input from the remote.
  423. //
  424. BOOL WINAPI DrvSignalProc32
  425. (
  426. DWORD dwSignal,
  427. DWORD dwID,
  428. DWORD dwFlags,
  429. WORD hTask16
  430. )
  431. {
  432. BOOL fRet;
  433. DebugEntry(DrvSignalProc32);
  434. if (dwSignal == SIG_PRE_FORCE_LOCK)
  435. {
  436. TRACE_OUT(("Disabling remote control before critical dialog, count %ld",
  437. g_imSharedData.imSuspended));
  438. ++g_imSharedData.imSuspended;
  439. }
  440. EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_DISABLE);
  441. fRet = SignalProc32(dwSignal, dwID, dwFlags, hTask16);
  442. EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_ENABLE);
  443. if (dwSignal == SIG_POST_FORCE_LOCK)
  444. {
  445. --g_imSharedData.imSuspended;
  446. TRACE_OUT(("Enabling remote control after critical dialog, count %ld",
  447. g_imSharedData.imSuspended));
  448. }
  449. DebugExitBOOL(DrvSignalProc32, fRet);
  450. return(fRet);
  451. }