xref: /reactos/win32ss/user/ntuser/input.c (revision 2ea03b5b)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * COPYRIGHT:        See COPYING in the top level directory
3c2c66affSColin Finck  * PROJECT:          ReactOS Win32k subsystem
4c2c66affSColin Finck  * PURPOSE:          General input functions
5c2c66affSColin Finck  * FILE:             win32ss/user/ntuser/input.c
6c2c66affSColin Finck  * PROGRAMERS:       Casper S. Hornstrup (chorns@users.sourceforge.net)
7c2c66affSColin Finck  *                   Rafal Harabien (rafalh@reactos.org)
8c2c66affSColin Finck  */
9c2c66affSColin Finck 
10c2c66affSColin Finck #include <win32k.h>
11c2c66affSColin Finck DBG_DEFAULT_CHANNEL(UserInput);
12c2c66affSColin Finck 
13c2c66affSColin Finck /* GLOBALS *******************************************************************/
14c2c66affSColin Finck 
15c2c66affSColin Finck PTHREADINFO ptiRawInput;
16c2c66affSColin Finck PKTIMER MasterTimer = NULL;
17c2c66affSColin Finck PATTACHINFO gpai = NULL;
18c2c66affSColin Finck INT paiCount = 0;
19c2c66affSColin Finck HANDLE ghKeyboardDevice = NULL;
20c2c66affSColin Finck 
21c2c66affSColin Finck static DWORD LastInputTick = 0;
22c2c66affSColin Finck static HANDLE ghMouseDevice;
23c2c66affSColin Finck 
24c2c66affSColin Finck /* FUNCTIONS *****************************************************************/
25c2c66affSColin Finck 
26c2c66affSColin Finck /*
27c2c66affSColin Finck  * IntLastInputTick
28c2c66affSColin Finck  *
29c2c66affSColin Finck  * Updates or gets last input tick count
30c2c66affSColin Finck  */
31c2c66affSColin Finck static DWORD FASTCALL
IntLastInputTick(BOOL bUpdate)32c2c66affSColin Finck IntLastInputTick(BOOL bUpdate)
33c2c66affSColin Finck {
34c2c66affSColin Finck     if (bUpdate)
35c2c66affSColin Finck     {
3694a42d43SKatayama Hirofumi MZ         LastInputTick = EngGetTickCount32();
37c2c66affSColin Finck         if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
38c2c66affSColin Finck     }
39c2c66affSColin Finck     return LastInputTick;
40c2c66affSColin Finck }
41c2c66affSColin Finck 
42c2c66affSColin Finck /*
43c2c66affSColin Finck  * DoTheScreenSaver
44c2c66affSColin Finck  *
45*2ea03b5bSAndriy Shevchenko  * Check if screensaver should be started and sends message to SAS window
46c2c66affSColin Finck  */
47c2c66affSColin Finck VOID FASTCALL
DoTheScreenSaver(VOID)48c2c66affSColin Finck DoTheScreenSaver(VOID)
49c2c66affSColin Finck {
50c2c66affSColin Finck     DWORD Test, TO;
51c2c66affSColin Finck 
52c2c66affSColin Finck     if (gspv.iScrSaverTimeout > 0) // Zero means Off.
53c2c66affSColin Finck     {
5494a42d43SKatayama Hirofumi MZ         Test = EngGetTickCount32();
55c2c66affSColin Finck         Test = Test - LastInputTick;
56c2c66affSColin Finck         TO = 1000 * gspv.iScrSaverTimeout;
57c2c66affSColin Finck         if (Test > TO)
58c2c66affSColin Finck         {
59c2c66affSColin Finck             TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test, gspv.iScrSaverTimeout);
60c2c66affSColin Finck 
61c2c66affSColin Finck             if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
62c2c66affSColin Finck             {
63c2c66affSColin Finck                 if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER))
64c2c66affSColin Finck                 {
65c2c66affSColin Finck                     ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER;
66c2c66affSColin Finck                     ERR("Screensaver is Idle\n");
67c2c66affSColin Finck                 }
68c2c66affSColin Finck             }
69c2c66affSColin Finck             else
70c2c66affSColin Finck             {
71c2c66affSColin Finck                 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
72c2c66affSColin Finck                 if (ForegroundQueue && ForegroundQueue->spwndActive)
73c2c66affSColin Finck                     UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
74c2c66affSColin Finck                 else
75c2c66affSColin Finck                     UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
76c2c66affSColin Finck             }
77c2c66affSColin Finck         }
78c2c66affSColin Finck     }
79c2c66affSColin Finck }
80c2c66affSColin Finck 
81c2c66affSColin Finck /*
82c2c66affSColin Finck  * OpenInputDevice
83c2c66affSColin Finck  *
84c2c66affSColin Finck  * Opens input device for asynchronous access
85c2c66affSColin Finck  */
86c2c66affSColin Finck static
87c2c66affSColin Finck NTSTATUS NTAPI
OpenInputDevice(PHANDLE pHandle,PFILE_OBJECT * ppObject,CONST WCHAR * pszDeviceName)88c2c66affSColin Finck OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceName)
89c2c66affSColin Finck {
90c2c66affSColin Finck     UNICODE_STRING DeviceName;
91c2c66affSColin Finck     OBJECT_ATTRIBUTES ObjectAttributes;
92c2c66affSColin Finck     NTSTATUS Status;
93c2c66affSColin Finck     IO_STATUS_BLOCK Iosb;
94c2c66affSColin Finck 
95c2c66affSColin Finck     RtlInitUnicodeString(&DeviceName, pszDeviceName);
96c2c66affSColin Finck 
97c2c66affSColin Finck     InitializeObjectAttributes(&ObjectAttributes,
98c2c66affSColin Finck                                &DeviceName,
99c2c66affSColin Finck                                OBJ_KERNEL_HANDLE,
100c2c66affSColin Finck                                NULL,
101c2c66affSColin Finck                                NULL);
102c2c66affSColin Finck 
103c2c66affSColin Finck     Status = ZwOpenFile(pHandle,
104c2c66affSColin Finck                         FILE_ALL_ACCESS,
105c2c66affSColin Finck                         &ObjectAttributes,
106c2c66affSColin Finck                         &Iosb,
107c2c66affSColin Finck                         0,
108c2c66affSColin Finck                         0);
109c2c66affSColin Finck     if (NT_SUCCESS(Status) && ppObject)
110c2c66affSColin Finck     {
111c2c66affSColin Finck         Status = ObReferenceObjectByHandle(*pHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)ppObject, NULL);
112c2c66affSColin Finck         ASSERT(NT_SUCCESS(Status));
113c2c66affSColin Finck     }
114c2c66affSColin Finck 
115c2c66affSColin Finck     return Status;
116c2c66affSColin Finck }
117c2c66affSColin Finck 
118c2c66affSColin Finck /*
119c2c66affSColin Finck  * RawInputThreadMain
120c2c66affSColin Finck  *
121c2c66affSColin Finck  * Reads data from input devices and supports win32 timers
122c2c66affSColin Finck  */
123c2c66affSColin Finck VOID NTAPI
RawInputThreadMain(VOID)124c2c66affSColin Finck RawInputThreadMain(VOID)
125c2c66affSColin Finck {
126c2c66affSColin Finck     NTSTATUS MouStatus = STATUS_UNSUCCESSFUL, KbdStatus = STATUS_UNSUCCESSFUL, Status;
127c2c66affSColin Finck     IO_STATUS_BLOCK MouIosb, KbdIosb;
128c2c66affSColin Finck     PFILE_OBJECT pKbdDevice = NULL, pMouDevice = NULL;
129c2c66affSColin Finck     LARGE_INTEGER ByteOffset;
130c2c66affSColin Finck     //LARGE_INTEGER WaitTimeout;
131c2c66affSColin Finck     PVOID WaitObjects[4], pSignaledObject = NULL;
132c2c66affSColin Finck     KWAIT_BLOCK WaitBlockArray[RTL_NUMBER_OF(WaitObjects)];
133c2c66affSColin Finck     ULONG cWaitObjects = 0, cMaxWaitObjects = 2;
134c2c66affSColin Finck     MOUSE_INPUT_DATA MouseInput;
135c2c66affSColin Finck     KEYBOARD_INPUT_DATA KeyInput;
136c2c66affSColin Finck     PVOID ShutdownEvent;
1371a8d9f12SGiannis Adamopoulos     HWINSTA hWinSta;
138c2c66affSColin Finck 
139c2c66affSColin Finck     ByteOffset.QuadPart = (LONGLONG)0;
140c2c66affSColin Finck     //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
141c2c66affSColin Finck 
142c2c66affSColin Finck     ptiRawInput = GetW32ThreadInfo();
143c2c66affSColin Finck     ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
144c2c66affSColin Finck     ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
145c2c66affSColin Finck 
146c2c66affSColin Finck     TRACE("Raw Input Thread %p\n", ptiRawInput);
147c2c66affSColin Finck 
148c2c66affSColin Finck     KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
149c2c66affSColin Finck                         LOW_REALTIME_PRIORITY + 3);
150c2c66affSColin Finck 
1511a8d9f12SGiannis Adamopoulos     Status = ObOpenObjectByPointer(InputWindowStation,
1521a8d9f12SGiannis Adamopoulos                                    0,
1531a8d9f12SGiannis Adamopoulos                                    NULL,
1541a8d9f12SGiannis Adamopoulos                                    MAXIMUM_ALLOWED,
1551a8d9f12SGiannis Adamopoulos                                    ExWindowStationObjectType,
1561a8d9f12SGiannis Adamopoulos                                    UserMode,
1571a8d9f12SGiannis Adamopoulos                                    (PHANDLE)&hWinSta);
1581a8d9f12SGiannis Adamopoulos     if (NT_SUCCESS(Status))
1591a8d9f12SGiannis Adamopoulos     {
1601a8d9f12SGiannis Adamopoulos         UserSetProcessWindowStation(hWinSta);
1611a8d9f12SGiannis Adamopoulos     }
1621a8d9f12SGiannis Adamopoulos     else
1631a8d9f12SGiannis Adamopoulos     {
1641a8d9f12SGiannis Adamopoulos         ASSERT(FALSE);
1651a8d9f12SGiannis Adamopoulos         /* Failed to open the interactive winsta! What now? */
1661a8d9f12SGiannis Adamopoulos     }
1671a8d9f12SGiannis Adamopoulos 
168c2c66affSColin Finck     UserEnterExclusive();
169c2c66affSColin Finck     StartTheTimers();
170c2c66affSColin Finck     UserLeave();
171c2c66affSColin Finck 
172c2c66affSColin Finck     NT_ASSERT(ghMouseDevice == NULL);
173c2c66affSColin Finck     NT_ASSERT(ghKeyboardDevice == NULL);
174c2c66affSColin Finck 
175c2c66affSColin Finck     PoRequestShutdownEvent(&ShutdownEvent);
176c2c66affSColin Finck     for (;;)
177c2c66affSColin Finck     {
178c2c66affSColin Finck         if (!ghMouseDevice)
179c2c66affSColin Finck         {
180c2c66affSColin Finck             /* Check if mouse device already exists */
181c2c66affSColin Finck             Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" );
182c2c66affSColin Finck             if (NT_SUCCESS(Status))
183c2c66affSColin Finck             {
184c2c66affSColin Finck                 ++cMaxWaitObjects;
185c2c66affSColin Finck                 TRACE("Mouse connected!\n");
186c2c66affSColin Finck             }
187c2c66affSColin Finck         }
188c2c66affSColin Finck         if (!ghKeyboardDevice)
189c2c66affSColin Finck         {
190c2c66affSColin Finck             /* Check if keyboard device already exists */
191c2c66affSColin Finck             Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0");
192c2c66affSColin Finck             if (NT_SUCCESS(Status))
193c2c66affSColin Finck             {
194c2c66affSColin Finck                 ++cMaxWaitObjects;
195c2c66affSColin Finck                 TRACE("Keyboard connected!\n");
196c2c66affSColin Finck                 // Get and load keyboard attributes.
197c2c66affSColin Finck                 UserInitKeyboard(ghKeyboardDevice);
198c2c66affSColin Finck                 UserEnterExclusive();
199c2c66affSColin Finck                 // Register the Window hotkey.
200c2c66affSColin Finck                 UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0);
2017e396787SDenis Malikov                 // Register the Window Snap hotkey.
2027e396787SDenis Malikov                 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_LEFT, MOD_WIN, VK_LEFT);
2037e396787SDenis Malikov                 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_RIGHT, MOD_WIN, VK_RIGHT);
2047e396787SDenis Malikov                 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_UP, MOD_WIN, VK_UP);
2057e396787SDenis Malikov                 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_DOWN, MOD_WIN, VK_DOWN);
206c2c66affSColin Finck                 // Register the debug hotkeys.
207c2c66affSColin Finck                 StartDebugHotKeys();
208c2c66affSColin Finck                 UserLeave();
209c2c66affSColin Finck             }
210c2c66affSColin Finck         }
211c2c66affSColin Finck 
212c2c66affSColin Finck         /* Reset WaitHandles array */
213c2c66affSColin Finck         cWaitObjects = 0;
214c2c66affSColin Finck         WaitObjects[cWaitObjects++] = ShutdownEvent;
215c2c66affSColin Finck         WaitObjects[cWaitObjects++] = MasterTimer;
216c2c66affSColin Finck 
217c2c66affSColin Finck         if (ghMouseDevice)
218c2c66affSColin Finck         {
219c2c66affSColin Finck             /* Try to read from mouse if previous reading is not pending */
220c2c66affSColin Finck             if (MouStatus != STATUS_PENDING)
221c2c66affSColin Finck             {
222c2c66affSColin Finck                 MouStatus = ZwReadFile(ghMouseDevice,
223c2c66affSColin Finck                                        NULL,
224c2c66affSColin Finck                                        NULL,
225c2c66affSColin Finck                                        NULL,
226c2c66affSColin Finck                                        &MouIosb,
227c2c66affSColin Finck                                        &MouseInput,
228c2c66affSColin Finck                                        sizeof(MOUSE_INPUT_DATA),
229c2c66affSColin Finck                                        &ByteOffset,
230c2c66affSColin Finck                                        NULL);
231c2c66affSColin Finck             }
232c2c66affSColin Finck 
233c2c66affSColin Finck             if (MouStatus == STATUS_PENDING)
234c2c66affSColin Finck                 WaitObjects[cWaitObjects++] = &pMouDevice->Event;
235c2c66affSColin Finck         }
236c2c66affSColin Finck 
237c2c66affSColin Finck         if (ghKeyboardDevice)
238c2c66affSColin Finck         {
239c2c66affSColin Finck             /* Try to read from keyboard if previous reading is not pending */
240c2c66affSColin Finck             if (KbdStatus != STATUS_PENDING)
241c2c66affSColin Finck             {
242c2c66affSColin Finck                 KbdStatus = ZwReadFile(ghKeyboardDevice,
243c2c66affSColin Finck                                        NULL,
244c2c66affSColin Finck                                        NULL,
245c2c66affSColin Finck                                        NULL,
246c2c66affSColin Finck                                        &KbdIosb,
247c2c66affSColin Finck                                        &KeyInput,
248c2c66affSColin Finck                                        sizeof(KEYBOARD_INPUT_DATA),
249c2c66affSColin Finck                                        &ByteOffset,
250c2c66affSColin Finck                                        NULL);
251c2c66affSColin Finck 
252c2c66affSColin Finck             }
253c2c66affSColin Finck             if (KbdStatus == STATUS_PENDING)
254c2c66affSColin Finck                 WaitObjects[cWaitObjects++] = &pKbdDevice->Event;
255c2c66affSColin Finck         }
256c2c66affSColin Finck 
257c2c66affSColin Finck         /* If all objects are pending, wait for them */
258c2c66affSColin Finck         if (cWaitObjects == cMaxWaitObjects)
259c2c66affSColin Finck         {
260c2c66affSColin Finck             Status = KeWaitForMultipleObjects(cWaitObjects,
261c2c66affSColin Finck                                               WaitObjects,
262c2c66affSColin Finck                                               WaitAny,
263c2c66affSColin Finck                                               UserRequest,
264c2c66affSColin Finck                                               KernelMode,
265c2c66affSColin Finck                                               TRUE,
266c2c66affSColin Finck                                               NULL,//&WaitTimeout,
267c2c66affSColin Finck                                               WaitBlockArray);
268c2c66affSColin Finck 
269c2c66affSColin Finck             if ((Status >= STATUS_WAIT_0) &&
270c2c66affSColin Finck                 (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects)))
271c2c66affSColin Finck             {
272c2c66affSColin Finck                 /* Some device has finished reading */
273c2c66affSColin Finck                 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
274c2c66affSColin Finck 
275c2c66affSColin Finck                 /* Check if it is mouse or keyboard and update status */
276c2c66affSColin Finck                 if ((MouStatus == STATUS_PENDING) &&
277c2c66affSColin Finck                     (pSignaledObject == &pMouDevice->Event))
278c2c66affSColin Finck                 {
279c2c66affSColin Finck                     MouStatus = MouIosb.Status;
280c2c66affSColin Finck                 }
281c2c66affSColin Finck                 else if ((KbdStatus == STATUS_PENDING) &&
282c2c66affSColin Finck                          (pSignaledObject == &pKbdDevice->Event))
283c2c66affSColin Finck                 {
284c2c66affSColin Finck                     KbdStatus = KbdIosb.Status;
285c2c66affSColin Finck                 }
286c2c66affSColin Finck                 else if (pSignaledObject == MasterTimer)
287c2c66affSColin Finck                 {
288c2c66affSColin Finck                     ProcessTimers();
289c2c66affSColin Finck                 }
290c2c66affSColin Finck                 else if (pSignaledObject == ShutdownEvent)
291c2c66affSColin Finck                 {
292c2c66affSColin Finck                     break;
293c2c66affSColin Finck                 }
294c2c66affSColin Finck                 else ASSERT(FALSE);
295c2c66affSColin Finck             }
296c2c66affSColin Finck         }
297c2c66affSColin Finck 
298c2c66affSColin Finck         /* Have we successed reading from mouse? */
299c2c66affSColin Finck         if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING)
300c2c66affSColin Finck         {
301c2c66affSColin Finck             TRACE("MouseEvent\n");
302c2c66affSColin Finck 
303c2c66affSColin Finck             /* Set LastInputTick */
304c2c66affSColin Finck             IntLastInputTick(TRUE);
305c2c66affSColin Finck 
306c2c66affSColin Finck             /* Process data */
307c2c66affSColin Finck             UserEnterExclusive();
308c2c66affSColin Finck             UserProcessMouseInput(&MouseInput);
309c2c66affSColin Finck             UserLeave();
310c2c66affSColin Finck         }
311c2c66affSColin Finck         else if (MouStatus != STATUS_PENDING)
312c2c66affSColin Finck             ERR("Failed to read from mouse: %x.\n", MouStatus);
313c2c66affSColin Finck 
314c2c66affSColin Finck         /* Have we successed reading from keyboard? */
315c2c66affSColin Finck         if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING)
316c2c66affSColin Finck         {
317c2c66affSColin Finck             TRACE("KeyboardEvent: %s %04x\n",
318c2c66affSColin Finck                   (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
319c2c66affSColin Finck                   KeyInput.MakeCode);
320c2c66affSColin Finck 
321c2c66affSColin Finck             /* Set LastInputTick */
322c2c66affSColin Finck             IntLastInputTick(TRUE);
323c2c66affSColin Finck 
324c2c66affSColin Finck             /* Process data */
325c2c66affSColin Finck             UserEnterExclusive();
326c2c66affSColin Finck             UserProcessKeyboardInput(&KeyInput);
327c2c66affSColin Finck             UserLeave();
328c2c66affSColin Finck         }
329c2c66affSColin Finck         else if (KbdStatus != STATUS_PENDING)
330c2c66affSColin Finck             ERR("Failed to read from keyboard: %x.\n", KbdStatus);
331c2c66affSColin Finck     }
332c2c66affSColin Finck 
333c2c66affSColin Finck     if (ghMouseDevice)
334c2c66affSColin Finck     {
335c2c66affSColin Finck         (void)ZwCancelIoFile(ghMouseDevice, &MouIosb);
336c2c66affSColin Finck         ObCloseHandle(ghMouseDevice, KernelMode);
337c2c66affSColin Finck         ObDereferenceObject(pMouDevice);
338c2c66affSColin Finck         ghMouseDevice = NULL;
339c2c66affSColin Finck     }
340c2c66affSColin Finck 
341c2c66affSColin Finck     if (ghKeyboardDevice)
342c2c66affSColin Finck     {
343c2c66affSColin Finck         (void)ZwCancelIoFile(ghKeyboardDevice, &KbdIosb);
344c2c66affSColin Finck         ObCloseHandle(ghKeyboardDevice, KernelMode);
345c2c66affSColin Finck         ObDereferenceObject(pKbdDevice);
346c2c66affSColin Finck         ghKeyboardDevice = NULL;
347c2c66affSColin Finck     }
348c2c66affSColin Finck 
349c2c66affSColin Finck     ERR("Raw Input Thread Exit!\n");
350c2c66affSColin Finck }
351c2c66affSColin Finck 
352c2c66affSColin Finck /*
353c2c66affSColin Finck  * InitInputImpl
354c2c66affSColin Finck  *
355c2c66affSColin Finck  * Inits input implementation
356c2c66affSColin Finck  */
3575c7ce447SVictor Perevertkin CODE_SEG("INIT")
358c2c66affSColin Finck NTSTATUS
359c2c66affSColin Finck NTAPI
InitInputImpl(VOID)360c2c66affSColin Finck InitInputImpl(VOID)
361c2c66affSColin Finck {
362c2c66affSColin Finck     MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
363c2c66affSColin Finck     if (!MasterTimer)
364c2c66affSColin Finck     {
365c2c66affSColin Finck         ERR("Failed to allocate memory\n");
366c2c66affSColin Finck         ASSERT(FALSE);
367c2c66affSColin Finck         return STATUS_UNSUCCESSFUL;
368c2c66affSColin Finck     }
369c2c66affSColin Finck     KeInitializeTimer(MasterTimer);
370c2c66affSColin Finck 
371c2c66affSColin Finck     return STATUS_SUCCESS;
372c2c66affSColin Finck }
373c2c66affSColin Finck 
374c2c66affSColin Finck BOOL FASTCALL
IntBlockInput(PTHREADINFO pti,BOOL BlockIt)375c2c66affSColin Finck IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
376c2c66affSColin Finck {
377c2c66affSColin Finck     PTHREADINFO OldBlock;
378c2c66affSColin Finck     ASSERT(pti);
379c2c66affSColin Finck 
380c2c66affSColin Finck     if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
381c2c66affSColin Finck     {
382c2c66affSColin Finck         /*
383c2c66affSColin Finck          * Fail blocking if exiting the thread
384c2c66affSColin Finck          */
385c2c66affSColin Finck 
386c2c66affSColin Finck         return FALSE;
387c2c66affSColin Finck     }
388c2c66affSColin Finck 
389c2c66affSColin Finck     /*
390c2c66affSColin Finck      * FIXME: Check access rights of the window station
391c2c66affSColin Finck      *        e.g. services running in the service window station cannot block input
392c2c66affSColin Finck      */
393c2c66affSColin Finck     if(!ThreadHasInputAccess(pti) ||
394c2c66affSColin Finck        !IntIsActiveDesktop(pti->rpdesk))
395c2c66affSColin Finck     {
396c2c66affSColin Finck         EngSetLastError(ERROR_ACCESS_DENIED);
397c2c66affSColin Finck         return FALSE;
398c2c66affSColin Finck     }
399c2c66affSColin Finck 
400c2c66affSColin Finck     ASSERT(pti->rpdesk);
401c2c66affSColin Finck     OldBlock = pti->rpdesk->BlockInputThread;
402c2c66affSColin Finck     if(OldBlock)
403c2c66affSColin Finck     {
404c2c66affSColin Finck         if(OldBlock != pti)
405c2c66affSColin Finck         {
406c2c66affSColin Finck             EngSetLastError(ERROR_ACCESS_DENIED);
407c2c66affSColin Finck             return FALSE;
408c2c66affSColin Finck         }
409c2c66affSColin Finck         pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
410c2c66affSColin Finck         return OldBlock == NULL;
411c2c66affSColin Finck     }
412c2c66affSColin Finck 
413c2c66affSColin Finck     pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
414c2c66affSColin Finck     return OldBlock == NULL;
415c2c66affSColin Finck }
416c2c66affSColin Finck 
417c2c66affSColin Finck BOOL
418c2c66affSColin Finck APIENTRY
NtUserBlockInput(BOOL BlockIt)419c2c66affSColin Finck NtUserBlockInput(
420c2c66affSColin Finck     BOOL BlockIt)
421c2c66affSColin Finck {
422c2c66affSColin Finck     BOOL ret;
423c2c66affSColin Finck 
424c2c66affSColin Finck     TRACE("Enter NtUserBlockInput\n");
425c2c66affSColin Finck     UserEnterExclusive();
426c2c66affSColin Finck 
427c2c66affSColin Finck     ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
428c2c66affSColin Finck 
429c2c66affSColin Finck     UserLeave();
430c2c66affSColin Finck     TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
431c2c66affSColin Finck 
432c2c66affSColin Finck     return ret;
433c2c66affSColin Finck }
434c2c66affSColin Finck 
435c2c66affSColin Finck BOOL
436c2c66affSColin Finck FASTCALL
IsRemoveAttachThread(PTHREADINFO pti)437c2c66affSColin Finck IsRemoveAttachThread(PTHREADINFO pti)
438c2c66affSColin Finck {
439c2c66affSColin Finck     NTSTATUS Status;
440c2c66affSColin Finck     PATTACHINFO pai;
441c2c66affSColin Finck     BOOL Ret = TRUE;
442c2c66affSColin Finck     PTHREADINFO ptiFrom = NULL, ptiTo = NULL;
443c2c66affSColin Finck 
444c2c66affSColin Finck     do
445c2c66affSColin Finck     {
446c2c66affSColin Finck        if (!gpai) return TRUE;
447c2c66affSColin Finck 
448c2c66affSColin Finck        pai = gpai; // Bottom of the list.
449c2c66affSColin Finck 
450c2c66affSColin Finck        do
451c2c66affSColin Finck        {
452c2c66affSColin Finck           if (pai->pti2 == pti)
453c2c66affSColin Finck           {
454c2c66affSColin Finck              ptiFrom = pai->pti1;
455c2c66affSColin Finck              ptiTo = pti;
456c2c66affSColin Finck              break;
457c2c66affSColin Finck           }
458c2c66affSColin Finck           if (pai->pti1 == pti)
459c2c66affSColin Finck           {
460c2c66affSColin Finck              ptiFrom = pti;
461c2c66affSColin Finck              ptiTo = pai->pti2;
462c2c66affSColin Finck              break;
463c2c66affSColin Finck           }
464c2c66affSColin Finck           pai = pai->paiNext;
465c2c66affSColin Finck 
466c2c66affSColin Finck        } while (pai);
467c2c66affSColin Finck 
468c2c66affSColin Finck        if (!pai && !ptiFrom && !ptiTo) break;
469c2c66affSColin Finck 
470c2c66affSColin Finck        Status = UserAttachThreadInput(ptiFrom, ptiTo, FALSE);
471c2c66affSColin Finck        if (!NT_SUCCESS(Status)) Ret = FALSE;
472c2c66affSColin Finck 
473c2c66affSColin Finck     } while (Ret);
474c2c66affSColin Finck 
475c2c66affSColin Finck     return Ret;
476c2c66affSColin Finck }
477c2c66affSColin Finck 
478567b0700SKatayama Hirofumi MZ // Win: zzzAttachThreadInput
479c2c66affSColin Finck NTSTATUS FASTCALL
UserAttachThreadInput(PTHREADINFO ptiFrom,PTHREADINFO ptiTo,BOOL fAttach)480c2c66affSColin Finck UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
481c2c66affSColin Finck {
482c2c66affSColin Finck     MSG msg;
483c2c66affSColin Finck     PATTACHINFO pai;
484c2c66affSColin Finck     PCURICON_OBJECT CurIcon;
485c2c66affSColin Finck 
486c2c66affSColin Finck     /* Can not be the same thread. */
487c2c66affSColin Finck     if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
488c2c66affSColin Finck 
489c2c66affSColin Finck     /* Do not attach to system threads or between different desktops. */
490c2c66affSColin Finck     if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE ||
491c2c66affSColin Finck         ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
492c2c66affSColin Finck         ptiFrom->rpdesk != ptiTo->rpdesk)
493c2c66affSColin Finck         return STATUS_ACCESS_DENIED;
494c2c66affSColin Finck 
495c2c66affSColin Finck     /* MSDN Note:
496c2c66affSColin Finck        Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
497c2c66affSColin Finck      */
498c2c66affSColin Finck 
499c2c66affSColin Finck     /* If Attach set, allocate and link. */
500c2c66affSColin Finck     if (fAttach)
501c2c66affSColin Finck     {
502c2c66affSColin Finck         pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
503c2c66affSColin Finck         if (!pai) return STATUS_NO_MEMORY;
504c2c66affSColin Finck 
505c2c66affSColin Finck         pai->paiNext = gpai;
506c2c66affSColin Finck         pai->pti1 = ptiFrom;
507c2c66affSColin Finck         pai->pti2 = ptiTo;
508c2c66affSColin Finck         gpai = pai;
509c2c66affSColin Finck         paiCount++;
510c2c66affSColin Finck         ERR("Attach Allocated! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
511c2c66affSColin Finck 
512c2c66affSColin Finck         if (ptiTo->MessageQueue != ptiFrom->MessageQueue)
513c2c66affSColin Finck         {
514c2c66affSColin Finck 
515c2c66affSColin Finck            ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
516c2c66affSColin Finck 
517c2c66affSColin Finck            if (ptiFrom->MessageQueue == gpqForeground)
518c2c66affSColin Finck            {
519c2c66affSColin Finck               ERR("ptiFrom is Foreground\n");
520c2c66affSColin Finck               ptiTo->MessageQueue->spwndActive  = ptiFrom->MessageQueue->spwndActive;
521c2c66affSColin Finck               ptiTo->MessageQueue->spwndFocus   = ptiFrom->MessageQueue->spwndFocus;
522c2c66affSColin Finck               ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture;
523c2c66affSColin Finck               ptiTo->MessageQueue->QF_flags    ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED);
524c2c66affSColin Finck               RtlCopyMemory(&ptiTo->MessageQueue->CaretInfo,
525c2c66affSColin Finck                             &ptiFrom->MessageQueue->CaretInfo,
526c2c66affSColin Finck                             sizeof(ptiTo->MessageQueue->CaretInfo));
527c2c66affSColin Finck               IntSetFocusMessageQueue(NULL);
528c2c66affSColin Finck               IntSetFocusMessageQueue(ptiTo->MessageQueue);
529c2c66affSColin Finck               gptiForeground = ptiTo;
530c2c66affSColin Finck            }
531c2c66affSColin Finck            else
532c2c66affSColin Finck            {
533c2c66affSColin Finck               ERR("ptiFrom NOT Foreground\n");
534c2c66affSColin Finck               if ( ptiTo->MessageQueue->spwndActive == 0 )
535c2c66affSColin Finck                   ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
536c2c66affSColin Finck               if ( ptiTo->MessageQueue->spwndFocus == 0 )
537c2c66affSColin Finck                   ptiTo->MessageQueue->spwndFocus  = ptiFrom->MessageQueue->spwndFocus;
538c2c66affSColin Finck            }
539c2c66affSColin Finck 
540c2c66affSColin Finck            CurIcon = ptiFrom->MessageQueue->CursorObject;
541c2c66affSColin Finck 
542c2c66affSColin Finck            MsqDestroyMessageQueue(ptiFrom);
543c2c66affSColin Finck 
544c2c66affSColin Finck            if (CurIcon)
545c2c66affSColin Finck            {
546c2c66affSColin Finck               // Could be global. Keep it above the water line!
547c2c66affSColin Finck               UserReferenceObject(CurIcon);
548c2c66affSColin Finck            }
549c2c66affSColin Finck 
550c2c66affSColin Finck            if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon)))
551c2c66affSColin Finck            {
552c2c66affSColin Finck               UserDereferenceObject(CurIcon);
553c2c66affSColin Finck               CurIcon = NULL;
554c2c66affSColin Finck            }
555c2c66affSColin Finck 
556c2c66affSColin Finck            ptiFrom->MessageQueue = ptiTo->MessageQueue;
557c2c66affSColin Finck 
558c2c66affSColin Finck            // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID.
559c2c66affSColin Finck            if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL)
560c2c66affSColin Finck            {
561c2c66affSColin Finck               ERR("ptiTo receiving ptiFrom Cursor\n");
562c2c66affSColin Finck               ptiTo->MessageQueue->CursorObject = CurIcon;
563c2c66affSColin Finck            }
564c2c66affSColin Finck 
565c2c66affSColin Finck            ptiFrom->MessageQueue->cThreads++;
566c2c66affSColin Finck            ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
567c2c66affSColin Finck 
568c2c66affSColin Finck            IntReferenceMessageQueue(ptiTo->MessageQueue);
569c2c66affSColin Finck         }
570c2c66affSColin Finck         else
571c2c66affSColin Finck         {
572c2c66affSColin Finck            ERR("Attach Threads are already associated!\n");
573c2c66affSColin Finck         }
574c2c66affSColin Finck     }
575c2c66affSColin Finck     else /* If clear, unlink and free it. */
576c2c66affSColin Finck     {
577c2c66affSColin Finck         BOOL Hit = FALSE;
578c2c66affSColin Finck         PATTACHINFO *ppai;
579c2c66affSColin Finck 
580c2c66affSColin Finck         if (!gpai) return STATUS_INVALID_PARAMETER;
581c2c66affSColin Finck 
582c2c66affSColin Finck         /* Search list and free if found or return false. */
583c2c66affSColin Finck         ppai = &gpai;
584c2c66affSColin Finck         while (*ppai != NULL)
585c2c66affSColin Finck         {
586c2c66affSColin Finck            if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom )
587c2c66affSColin Finck            {
588c2c66affSColin Finck               pai = *ppai;
589c2c66affSColin Finck               /* Remove it from the list */
590c2c66affSColin Finck               *ppai = (*ppai)->paiNext;
591c2c66affSColin Finck               ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
592c2c66affSColin Finck               paiCount--;
593c2c66affSColin Finck               Hit = TRUE;
594c2c66affSColin Finck               break;
595c2c66affSColin Finck            }
596c2c66affSColin Finck            ppai = &((*ppai)->paiNext);
597c2c66affSColin Finck         }
598c2c66affSColin Finck 
599c2c66affSColin Finck         if (!Hit) return STATUS_INVALID_PARAMETER;
600c2c66affSColin Finck 
601c2c66affSColin Finck         ERR("Attach Free! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
602c2c66affSColin Finck 
603c2c66affSColin Finck         if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
604c2c66affSColin Finck         {
605c2c66affSColin Finck            PWND spwndActive = ptiTo->MessageQueue->spwndActive;
606c2c66affSColin Finck            PWND spwndFocus  = ptiTo->MessageQueue->spwndFocus;
607c2c66affSColin Finck 
608c2c66affSColin Finck            if (gptiForeground == ptiFrom)
609c2c66affSColin Finck            {
610c2c66affSColin Finck               ERR("ptiTo is now pti FG.\n");
611c2c66affSColin Finck               // MessageQueue foreground is set so switch threads.
612c2c66affSColin Finck               gptiForeground = ptiTo;
613c2c66affSColin Finck            }
614c2c66affSColin Finck            ptiTo->MessageQueue->cThreads--;
615c2c66affSColin Finck            ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads);
616c2c66affSColin Finck            ASSERT(ptiTo->MessageQueue->cThreads >= 1);
617c2c66affSColin Finck 
618c2c66affSColin Finck            IntDereferenceMessageQueue(ptiTo->MessageQueue);
619c2c66affSColin Finck 
620c2c66affSColin Finck            ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
621c2c66affSColin Finck 
622c2c66affSColin Finck            if (spwndActive)
623c2c66affSColin Finck            {
624c2c66affSColin Finck               if (spwndActive->head.pti == ptiFrom)
625c2c66affSColin Finck               {
626c2c66affSColin Finck                  ptiFrom->MessageQueue->spwndActive = spwndActive;
627c2c66affSColin Finck                  ptiTo->MessageQueue->spwndActive = 0;
628c2c66affSColin Finck               }
629c2c66affSColin Finck            }
630c2c66affSColin Finck            if (spwndFocus)
631c2c66affSColin Finck            {
632c2c66affSColin Finck               if (spwndFocus->head.pti == ptiFrom)
633c2c66affSColin Finck               {
634c2c66affSColin Finck                  ptiFrom->MessageQueue->spwndFocus = spwndFocus;
635c2c66affSColin Finck                  ptiTo->MessageQueue->spwndFocus = 0;
636c2c66affSColin Finck               }
637c2c66affSColin Finck            }
638c2c66affSColin Finck            ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
639c2c66affSColin Finck         }
640c2c66affSColin Finck         else
641c2c66affSColin Finck         {
642c2c66affSColin Finck            ERR("Detaching Threads are not associated!\n");
643c2c66affSColin Finck         }
644c2c66affSColin Finck     }
645c2c66affSColin Finck     /* Note that key state, which can be ascertained by calls to the GetKeyState
646c2c66affSColin Finck        or GetKeyboardState function, is reset after a call to AttachThreadInput.
647c2c66affSColin Finck        ATM which one?
648c2c66affSColin Finck      */
649c2c66affSColin Finck     RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
650c2c66affSColin Finck 
651c2c66affSColin Finck     ptiTo->MessageQueue->msgDblClk.message = 0;
652c2c66affSColin Finck 
653c2c66affSColin Finck     /* Generate mouse move message */
654c2c66affSColin Finck     msg.message = WM_MOUSEMOVE;
655c2c66affSColin Finck     msg.wParam = UserGetMouseButtonsState();
656c2c66affSColin Finck     msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
657c2c66affSColin Finck     msg.pt = gpsi->ptCursor;
658c2c66affSColin Finck     co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
659c2c66affSColin Finck 
660c2c66affSColin Finck     return STATUS_SUCCESS;
661c2c66affSColin Finck }
662c2c66affSColin Finck 
663c2c66affSColin Finck BOOL
664c2c66affSColin Finck APIENTRY
NtUserAttachThreadInput(IN DWORD idAttach,IN DWORD idAttachTo,IN BOOL fAttach)665c2c66affSColin Finck NtUserAttachThreadInput(
666c2c66affSColin Finck     IN DWORD idAttach,
667c2c66affSColin Finck     IN DWORD idAttachTo,
668c2c66affSColin Finck     IN BOOL fAttach)
669c2c66affSColin Finck {
670c2c66affSColin Finck   NTSTATUS Status;
671c2c66affSColin Finck   PTHREADINFO pti, ptiTo;
672c2c66affSColin Finck   BOOL Ret = FALSE;
673c2c66affSColin Finck 
674c2c66affSColin Finck   UserEnterExclusive();
675c2c66affSColin Finck   TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
676c2c66affSColin Finck 
6772d9c88e0STimo Kreuzer   pti = IntTID2PTI(UlongToHandle(idAttach));
6782d9c88e0STimo Kreuzer   ptiTo = IntTID2PTI(UlongToHandle(idAttachTo));
679c2c66affSColin Finck 
680c2c66affSColin Finck   if ( !pti || !ptiTo )
681c2c66affSColin Finck   {
682c2c66affSColin Finck      TRACE("AttachThreadInput pti or ptiTo NULL.\n");
683c2c66affSColin Finck      EngSetLastError(ERROR_INVALID_PARAMETER);
684c2c66affSColin Finck      goto Exit;
685c2c66affSColin Finck   }
686c2c66affSColin Finck 
687c2c66affSColin Finck   Status = UserAttachThreadInput( pti, ptiTo, fAttach);
688c2c66affSColin Finck   if (!NT_SUCCESS(Status))
689c2c66affSColin Finck   {
690c2c66affSColin Finck      TRACE("AttachThreadInput Error Status 0x%x. \n",Status);
691c2c66affSColin Finck      EngSetLastError(RtlNtStatusToDosError(Status));
692c2c66affSColin Finck   }
693c2c66affSColin Finck   else Ret = TRUE;
694c2c66affSColin Finck 
695c2c66affSColin Finck Exit:
696c2c66affSColin Finck   TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret);
697c2c66affSColin Finck   UserLeave();
698c2c66affSColin Finck   return Ret;
699c2c66affSColin Finck }
700c2c66affSColin Finck 
701c2c66affSColin Finck /*
702c2c66affSColin Finck  * NtUserSendInput
703c2c66affSColin Finck  *
704c2c66affSColin Finck  * Generates input events from software
705c2c66affSColin Finck  */
706c2c66affSColin Finck UINT
707c2c66affSColin Finck APIENTRY
NtUserSendInput(UINT nInputs,LPINPUT pInput,INT cbSize)708c2c66affSColin Finck NtUserSendInput(
709c2c66affSColin Finck     UINT nInputs,
710c2c66affSColin Finck     LPINPUT pInput,
711c2c66affSColin Finck     INT cbSize)
712c2c66affSColin Finck {
713c2c66affSColin Finck     PTHREADINFO pti;
714c2c66affSColin Finck     UINT uRet = 0;
715c2c66affSColin Finck 
716c2c66affSColin Finck     TRACE("Enter NtUserSendInput\n");
717c2c66affSColin Finck     UserEnterExclusive();
718c2c66affSColin Finck 
719c2c66affSColin Finck     pti = PsGetCurrentThreadWin32Thread();
720c2c66affSColin Finck     ASSERT(pti);
721c2c66affSColin Finck 
722c2c66affSColin Finck     if (!pti->rpdesk)
723c2c66affSColin Finck     {
724c2c66affSColin Finck         goto cleanup;
725c2c66affSColin Finck     }
726c2c66affSColin Finck 
727c2c66affSColin Finck     if (!nInputs || !pInput || cbSize != sizeof(INPUT))
728c2c66affSColin Finck     {
729c2c66affSColin Finck         EngSetLastError(ERROR_INVALID_PARAMETER);
730c2c66affSColin Finck         goto cleanup;
731c2c66affSColin Finck     }
732c2c66affSColin Finck 
733c2c66affSColin Finck     /*
734c2c66affSColin Finck      * FIXME: Check access rights of the window station
735c2c66affSColin Finck      *        e.g. services running in the service window station cannot block input
736c2c66affSColin Finck      */
737c2c66affSColin Finck     if (!ThreadHasInputAccess(pti) ||
738c2c66affSColin Finck         !IntIsActiveDesktop(pti->rpdesk))
739c2c66affSColin Finck     {
740c2c66affSColin Finck         EngSetLastError(ERROR_ACCESS_DENIED);
741c2c66affSColin Finck         goto cleanup;
742c2c66affSColin Finck     }
743c2c66affSColin Finck 
744c2c66affSColin Finck     while (nInputs--)
745c2c66affSColin Finck     {
746c2c66affSColin Finck         INPUT SafeInput;
747c2c66affSColin Finck         NTSTATUS Status;
748c2c66affSColin Finck 
749c2c66affSColin Finck         Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
750c2c66affSColin Finck         if (!NT_SUCCESS(Status))
751c2c66affSColin Finck         {
752c2c66affSColin Finck             SetLastNtError(Status);
753c2c66affSColin Finck             goto cleanup;
754c2c66affSColin Finck         }
755c2c66affSColin Finck 
756c2c66affSColin Finck         switch (SafeInput.type)
757c2c66affSColin Finck         {
758c2c66affSColin Finck             case INPUT_MOUSE:
759c2c66affSColin Finck                 if (UserSendMouseInput(&SafeInput.mi, TRUE))
760c2c66affSColin Finck                     uRet++;
761c2c66affSColin Finck                 break;
762c2c66affSColin Finck             case INPUT_KEYBOARD:
763c2c66affSColin Finck                 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
764c2c66affSColin Finck                     uRet++;
765c2c66affSColin Finck                 break;
766c2c66affSColin Finck             case INPUT_HARDWARE:
76703422451SSerge Gautherie                 FIXME("INPUT_HARDWARE not supported!\n");
768c2c66affSColin Finck                 break;
769c2c66affSColin Finck             default:
770c2c66affSColin Finck                 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
771c2c66affSColin Finck                 break;
772c2c66affSColin Finck         }
773c2c66affSColin Finck     }
774c2c66affSColin Finck 
775c2c66affSColin Finck cleanup:
776c2c66affSColin Finck     TRACE("Leave NtUserSendInput, ret=%u\n", uRet);
777c2c66affSColin Finck     UserLeave();
778c2c66affSColin Finck     return uRet;
779c2c66affSColin Finck }
780c2c66affSColin Finck 
781c2c66affSColin Finck /* EOF */
782