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