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.

607 lines
16 KiB

  1. #include "precomp.h"
  2. //
  3. // IM.CPP
  4. // Input Manager, NT specific code
  5. //
  6. #define MLZ_FILE_ZONE ZONE_INPUT
  7. //
  8. // OSI_InstallControlledHooks()
  9. //
  10. // Installs/removes input hooks for control
  11. //
  12. BOOL WINAPI OSI_InstallControlledHooks(BOOL fEnable)
  13. {
  14. BOOL rc = FALSE;
  15. DebugEntry(OSI_InstallControlledHooks);
  16. if (fEnable)
  17. {
  18. //
  19. // Create the service thread, it will install the hooks.
  20. //
  21. ASSERT(!g_imNTData.imLowLevelInputThread);
  22. if (!DCS_StartThread(IMLowLevelInputProcessor))
  23. {
  24. ERROR_OUT(( "Failed to create LL IM thread"));
  25. DC_QUIT;
  26. }
  27. }
  28. else
  29. {
  30. if (g_imNTData.imLowLevelInputThread != 0)
  31. {
  32. PostThreadMessage( g_imNTData.imLowLevelInputThread, WM_QUIT, 0, 0);
  33. g_imNTData.imLowLevelInputThread = 0;
  34. }
  35. }
  36. rc = TRUE;
  37. DC_EXIT_POINT:
  38. DebugExitBOOL(OSI_InstallControlledHooks, rc);
  39. return(rc);
  40. }
  41. // Name: IMLowLevelInputProcessor
  42. //
  43. // Purpose: Main function for the low-level input handler thread.
  44. //
  45. // Returns: wParam of the WM_QUIT message.
  46. //
  47. // Params: syncObject - sync object that allows this thread to signal
  48. // the creating thread via COM_SignalThreadStarted.
  49. //
  50. // Operation: This function is the start point for the low-level input
  51. // handler thread.
  52. //
  53. // We raise the priority of this thread to:
  54. // (a) ensure that we avoid hitting the low-level callback
  55. // timeout - which would cause us to miss events.
  56. // (b) minimize visible mouse movement lag on the screen.
  57. //
  58. // The thread installs the low-level hooks and enters a
  59. // GetMessage/DispatchMessage loop which handles the low-level
  60. // callbacks.
  61. //
  62. // The Share Core sends the thread a WM_QUIT message to
  63. // terminate it, which causes it to exit the message loop and
  64. // removes the low-level hooks before it terminates.
  65. //
  66. DWORD WINAPI IMLowLevelInputProcessor(LPVOID hEventWait)
  67. {
  68. MSG msg;
  69. UINT rc = 0;
  70. DebugEntry(IMLowLevelInputProcessor);
  71. TRACE_OUT(( "Thread started..."));
  72. //
  73. // Give ourseleves the highest possible priority (within our process
  74. // priority class) to ensure that the low-level events are serviced as
  75. // soon as possible.
  76. //
  77. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  78. g_imNTData.imLowLevelInputThread = GetCurrentThreadId();
  79. //
  80. // Install low-level input hooks.
  81. //
  82. g_imNTData.imhLowLevelMouseHook = SetWindowsHookEx(
  83. WH_MOUSE_LL,
  84. IMLowLevelMouseProc,
  85. g_asInstance,
  86. 0 );
  87. g_imNTData.imhLowLevelKeyboardHook = SetWindowsHookEx(
  88. WH_KEYBOARD_LL,
  89. IMLowLevelKeyboardProc,
  90. g_asInstance,
  91. 0 );
  92. //
  93. // We're done with our init code, for better or for worse. Let the
  94. // calling thread continue.
  95. //
  96. SetEvent((HANDLE)hEventWait);
  97. if ( (g_imNTData.imhLowLevelMouseHook == NULL) ||
  98. (g_imNTData.imhLowLevelKeyboardHook == NULL) )
  99. {
  100. ERROR_OUT(( "SetWindowsHookEx failed: hMouse(%u) hKeyboard(%u)",
  101. g_imNTData.imhLowLevelMouseHook, g_imNTData.imhLowLevelKeyboardHook ));
  102. DC_QUIT;
  103. }
  104. //
  105. // Do our message loop to get events
  106. //
  107. while (GetMessage(&msg, NULL, 0, 0))
  108. {
  109. TranslateMessage(&msg);
  110. DispatchMessage(&msg);
  111. }
  112. //
  113. // Remove hooks
  114. //
  115. if (g_imNTData.imhLowLevelMouseHook != NULL)
  116. {
  117. UnhookWindowsHookEx(g_imNTData.imhLowLevelMouseHook);
  118. g_imNTData.imhLowLevelMouseHook = NULL;
  119. }
  120. if (g_imNTData.imhLowLevelKeyboardHook != NULL)
  121. {
  122. UnhookWindowsHookEx(g_imNTData.imhLowLevelKeyboardHook);
  123. g_imNTData.imhLowLevelKeyboardHook = NULL;
  124. }
  125. DC_EXIT_POINT:
  126. DebugExitDWORD(IMLowLevelInputProcessor, rc);
  127. return(rc);
  128. }
  129. //
  130. // Name: IMOtherDesktopProc()
  131. //
  132. // This allows us to inject (but not block) input into other desktops
  133. // besides default, where the user's desktop resides. Specifically, the
  134. // winlogon desktop and/or the screensaver desktop.
  135. //
  136. // This is trickier than it might seem, because the winlogon desktop is
  137. // always around, but the screen saver one is transitory.
  138. //
  139. // The periodic SWL_ code, called when hosting, checks for the current
  140. // desktop and if it's switched posts us a message so we can change our
  141. // desktop and our hooks.
  142. //
  143. DWORD WINAPI IMOtherDesktopProc(LPVOID hEventWait)
  144. {
  145. MSG msg;
  146. UINT rc = 0;
  147. HDESK hDesktop;
  148. GUIEFFECTS effects;
  149. DebugEntry(IMOtherDesktopProc);
  150. TRACE_OUT(("Other desktop thread started..."));
  151. g_imNTData.imOtherDesktopThread = GetCurrentThreadId();
  152. //
  153. // Start out attached to the WinLogon desktop because it's always
  154. // around.
  155. //
  156. // Set our desktop to the winlogon desktop
  157. hDesktop = OpenDesktop(NAME_DESKTOP_WINLOGON,
  158. 0,
  159. FALSE,
  160. DESKTOP_JOURNALPLAYBACK);
  161. if ( !hDesktop )
  162. {
  163. WARNING_OUT(("OpenDesktop failed: %ld", GetLastError()));
  164. DC_QUIT;
  165. }
  166. else if (!SetThreadDesktop (hDesktop))
  167. {
  168. WARNING_OUT(("SetThreadDesktop failed: %ld", GetLastError()));
  169. DC_QUIT;
  170. }
  171. //
  172. // Attempt to load the driver dynamically on this thread also.
  173. //
  174. if (g_asNT5)
  175. {
  176. OSI_InitDriver50(TRUE);
  177. }
  178. // Let the calling thread continue.
  179. SetEvent((HANDLE)hEventWait);
  180. ZeroMemory(&effects, sizeof(effects));
  181. while (GetMessage(&msg, NULL, 0, 0))
  182. {
  183. switch(msg.message)
  184. {
  185. case OSI_WM_MOUSEINJECT:
  186. mouse_event(
  187. LOWORD(msg.wParam), // flags
  188. HIWORD(msg.lParam), // x
  189. LOWORD(msg.lParam), // y
  190. HIWORD(msg.wParam), // mouseData
  191. 0); // dwExtraInfo
  192. break;
  193. case OSI_WM_KEYBDINJECT:
  194. keybd_event(
  195. (BYTE)(LOWORD(msg.lParam)), // vkCode
  196. (BYTE)(HIWORD(msg.lParam)), // scanCode
  197. (DWORD)msg.wParam, // flags
  198. 0); // dwExtraInfo
  199. break;
  200. case OSI_WM_DESKTOPREPAINT:
  201. USR_RepaintWindow(NULL);
  202. break;
  203. case OSI_WM_INJECTSAS:
  204. {
  205. HWND hwndSAS;
  206. if ( hwndSAS = FindWindow("SAS window class",NULL))
  207. {
  208. PostMessage(hwndSAS,WM_HOTKEY,0,
  209. MAKELONG(0x8000|MOD_ALT|MOD_CONTROL,VK_DELETE));
  210. }
  211. else
  212. {
  213. WARNING_OUT(("SAS window not found, on screensaver desktop"));
  214. }
  215. break;
  216. }
  217. case OSI_WM_DESKTOPSWITCH:
  218. {
  219. HDESK hDesktopNew;
  220. TRACE_OUT(("OSI_WM_DESKTOPSWITCH: switching desktop from %d to %d",
  221. msg.wParam, msg.lParam));
  222. if (msg.lParam == DESKTOP_SCREENSAVER)
  223. {
  224. // We're switching TO the screensaver, attach to it.
  225. TRACE_OUT(("Switching TO screensaver"));
  226. hDesktopNew = OpenDesktop(NAME_DESKTOP_SCREENSAVER,
  227. 0, FALSE, DESKTOP_JOURNALPLAYBACK);
  228. }
  229. else if (msg.wParam == DESKTOP_SCREENSAVER)
  230. {
  231. //
  232. // We're switching FROM the screensaver, reattach to
  233. // winlogon
  234. //
  235. TRACE_OUT(("Switching FROM screensaver"));
  236. hDesktopNew = OpenDesktop(NAME_DESKTOP_WINLOGON,
  237. 0, FALSE, DESKTOP_JOURNALPLAYBACK);
  238. }
  239. else
  240. {
  241. hDesktopNew = NULL;
  242. }
  243. if (hDesktopNew != NULL)
  244. {
  245. if (!SetThreadDesktop(hDesktopNew))
  246. {
  247. WARNING_OUT(("SetThreadDesktop to 0x%08x, type %d failed",
  248. hDesktopNew, msg.lParam));
  249. }
  250. else
  251. {
  252. CloseHandle(hDesktop);
  253. hDesktop = hDesktopNew;
  254. }
  255. }
  256. break;
  257. }
  258. case OSI_WM_SETGUIEFFECTS:
  259. {
  260. HET_SetGUIEffects((msg.wParam != 0), &effects);
  261. break;
  262. }
  263. }
  264. }
  265. DC_EXIT_POINT:
  266. if (g_asNT5)
  267. {
  268. OSI_InitDriver50(FALSE);
  269. }
  270. if (hDesktop)
  271. {
  272. CloseHandle(hDesktop);
  273. }
  274. g_imNTData.imOtherDesktopThread = 0;
  275. DebugExitDWORD(IMOtherDesktopProc, rc);
  276. return(rc);
  277. }
  278. //
  279. // IMLowLevelMouseProc()
  280. // NT callback for low-level mouse events.
  281. //
  282. // It is installed and called on a secondary thread with high priority to
  283. // service the APC call outs. It follows the windows hook conventions for
  284. // parameters and return values--zero to accept the event, non-zero to
  285. // discard.
  286. //
  287. //
  288. LRESULT CALLBACK IMLowLevelMouseProc
  289. (
  290. int nCode,
  291. WPARAM wParam,
  292. LPARAM lParam
  293. )
  294. {
  295. LRESULT rc = 0;
  296. PMSLLHOOKSTRUCT pMouseEvent;
  297. DebugEntry(IMLowLevelMouseProc);
  298. pMouseEvent = (PMSLLHOOKSTRUCT)lParam;
  299. //
  300. // If this isn't for an event that is happening or it's one we
  301. // injected ourself, pass it through and no need for processing.
  302. //
  303. if ((nCode != HC_ACTION) || (pMouseEvent->flags & LLMHF_INJECTED))
  304. {
  305. DC_QUIT;
  306. }
  307. //
  308. // This is a local user event. If controlled, throw it away. Unless
  309. // it's a click, in that case post a REVOKECONTROL message.
  310. //
  311. if (g_imSharedData.imControlled)
  312. {
  313. //
  314. // If this is a button click, take control back
  315. //
  316. if ((wParam == WM_LBUTTONDOWN) ||
  317. (wParam == WM_RBUTTONDOWN) ||
  318. (wParam == WM_MBUTTONDOWN))
  319. {
  320. //
  321. // Don't take control back if this is unattended.
  322. //
  323. if (!g_imSharedData.imUnattended)
  324. {
  325. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  326. }
  327. }
  328. // Swallow event.
  329. rc = 1;
  330. }
  331. DC_EXIT_POINT:
  332. //
  333. // Don't pass on to the next hook (if there is one) if we are
  334. // discarding the event.
  335. //
  336. if (!rc)
  337. {
  338. rc = CallNextHookEx(g_imNTData.imhLowLevelMouseHook, nCode,
  339. wParam, lParam);
  340. }
  341. DebugExitDWORD(IMLowLevelMouseProc, rc);
  342. return(rc);
  343. }
  344. // Name: IMLowLevelKeyboardProc
  345. //
  346. // Purpose: Windows callback function for low-level keyboard events.
  347. //
  348. // Returns: 0 if event is to be passed on to USER.
  349. // 1 if event is to be discarded.
  350. //
  351. // Params: Low-level callback params (see Windows documentation).
  352. //
  353. // Operation: Determines whether to allow the given event into USER.
  354. //
  355. // We always pass on injected events.
  356. // The Control Arbitrator determines whether local events are
  357. // passed on.
  358. //
  359. LRESULT CALLBACK IMLowLevelKeyboardProc
  360. (
  361. int nCode,
  362. WPARAM wParam,
  363. LPARAM lParam
  364. )
  365. {
  366. LRESULT rc = 0;
  367. PKBDLLHOOKSTRUCT pKbdEvent;
  368. DebugEntry(IMLowLevelKeyboardProc);
  369. pKbdEvent = (PKBDLLHOOKSTRUCT)lParam;
  370. //
  371. // If this isn't for an action or it's an event we ourself originated,
  372. // let it through, and do no processing.
  373. //
  374. if ((nCode != HC_ACTION) || (pKbdEvent->flags & LLKHF_INJECTED))
  375. {
  376. DC_QUIT;
  377. }
  378. if (g_imSharedData.imControlled)
  379. {
  380. if (!(pKbdEvent->flags & LLKHF_UP))
  381. {
  382. //
  383. // This is a key down. Take control back, and kill control
  384. // allowability if it's the ESC key.
  385. //
  386. if ((pKbdEvent->vkCode & 0x00FF) == VK_ESCAPE)
  387. {
  388. // ESC key always disallows control, even in unattended mode
  389. PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
  390. }
  391. else if (!g_imSharedData.imUnattended)
  392. {
  393. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  394. }
  395. }
  396. //
  397. // Don't discard toggle keys. The enabled/disabled function
  398. // is already set before we see the keystroke. If we discard,
  399. // the lights are incorrect.
  400. //
  401. // LAURABU: How do we fix this in new model? Post a toggle-key
  402. // message and undo it (fake press)?
  403. //
  404. if (!IM_KEY_IS_TOGGLE(pKbdEvent->vkCode & 0x00FF))
  405. rc = 1;
  406. }
  407. DC_EXIT_POINT:
  408. //
  409. // Don't pass on to the next hook if we are swallowing the event.
  410. //
  411. if (!rc)
  412. {
  413. rc = CallNextHookEx(g_imNTData.imhLowLevelKeyboardHook,
  414. nCode, wParam, lParam);
  415. }
  416. DebugExitDWORD(IMLowLevelKeyboardProc, rc);
  417. return(rc);
  418. }
  419. //
  420. // IMInjectMouseEvent()
  421. // NT-specific version to inject mouse events into the local system
  422. //
  423. void WINAPI OSI_InjectMouseEvent
  424. (
  425. DWORD flags,
  426. LONG x,
  427. LONG y,
  428. DWORD mouseData,
  429. DWORD dwExtraInfo
  430. )
  431. {
  432. TRACE_OUT(("Before MOUSE inject: %08lx, %08lx %08lx",
  433. flags, mouseData, dwExtraInfo));
  434. mouse_event(flags, (DWORD)x, (DWORD)y, mouseData, dwExtraInfo);
  435. if ( g_imNTData.imOtherDesktopThread )
  436. {
  437. // Stuff these dword parameters through WORDS
  438. // need to make sure we don't clip anything
  439. ASSERT(!(flags & 0xffff0000));
  440. //ASSERT(!(mouseData & 0xffff0000)); BUGBUG possible loss
  441. ASSERT(!(x & 0xffff0000));
  442. ASSERT(!(y & 0xffff0000));
  443. PostThreadMessage(
  444. g_imNTData.imOtherDesktopThread,
  445. OSI_WM_MOUSEINJECT,
  446. MAKEWPARAM((WORD)flags,(WORD)mouseData),
  447. MAKELPARAM((WORD)y, (WORD)x ));
  448. }
  449. TRACE_OUT(("After MOUSE inject"));
  450. }
  451. //
  452. // OSI_InjectSAS()
  453. // NT-specific version to inject ctrl+alt+del into the local system
  454. //
  455. void WINAPI OSI_InjectCtrlAltDel(void)
  456. {
  457. if ( g_imNTData.imOtherDesktopThread )
  458. {
  459. PostThreadMessage(
  460. g_imNTData.imOtherDesktopThread,
  461. OSI_WM_INJECTSAS,
  462. 0,
  463. 0 );
  464. }
  465. else
  466. {
  467. WARNING_OUT(("Ignoring SAS Injection attempt"));
  468. }
  469. }
  470. //
  471. // OSI_InjectKeyboardEvent()
  472. // NT-specific version to inject keyboard events into the local system
  473. //
  474. void WINAPI OSI_InjectKeyboardEvent
  475. (
  476. DWORD flags,
  477. WORD vkCode,
  478. WORD scanCode,
  479. DWORD dwExtraInfo
  480. )
  481. {
  482. TRACE_OUT(("Before KEY inject: %04lx, {%04x, %04x}, %04lx",
  483. flags, vkCode, scanCode, dwExtraInfo));
  484. keybd_event((BYTE)vkCode, (BYTE)scanCode, flags, dwExtraInfo);
  485. if ( g_imNTData.imOtherDesktopThread )
  486. {
  487. PostThreadMessage(
  488. g_imNTData.imOtherDesktopThread,
  489. OSI_WM_KEYBDINJECT,
  490. (WPARAM)flags,
  491. MAKELPARAM(vkCode, scanCode));
  492. }
  493. TRACE_OUT(("After KEY inject"));
  494. }
  495. //
  496. // OSI_DesktopSwitch()
  497. // NT-specific, called when we think the current desktop has changed.
  498. //
  499. void WINAPI OSI_DesktopSwitch
  500. (
  501. UINT desktopFrom,
  502. UINT desktopTo
  503. )
  504. {
  505. DebugEntry(OSI_DesktopSwitch);
  506. if (g_imNTData.imOtherDesktopThread)
  507. {
  508. PostThreadMessage(
  509. g_imNTData.imOtherDesktopThread,
  510. OSI_WM_DESKTOPSWITCH,
  511. desktopFrom,
  512. desktopTo);
  513. }
  514. DebugExitVOID(OSI_DesktopSwitch);
  515. }