[Win32k]
[reactos.git] / reactos / 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 0,
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()
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[3], pSignaledObject = NULL;
136 ULONG cWaitObjects = 0, cMaxWaitObjects = 1;
137 MOUSE_INPUT_DATA MouseInput;
138 KEYBOARD_INPUT_DATA KeyInput;
139
140 ByteOffset.QuadPart = (LONGLONG)0;
141 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
142
143 ptiRawInput = GetW32ThreadInfo();
144 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
145 ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
146
147 TRACE("Raw Input Thread %p\n", ptiRawInput);
148
149 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
150 LOW_REALTIME_PRIORITY + 3);
151
152 UserEnterExclusive();
153 StartTheTimers();
154 UserLeave();
155
156 NT_ASSERT(ghMouseDevice == NULL);
157 NT_ASSERT(ghKeyboardDevice == NULL);
158
159 for (;;)
160 {
161 if (!ghMouseDevice)
162 {
163 /* Check if mouse device already exists */
164 Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" );
165 if (NT_SUCCESS(Status))
166 {
167 ++cMaxWaitObjects;
168 TRACE("Mouse connected!\n");
169 }
170 }
171 if (!ghKeyboardDevice)
172 {
173 /* Check if keyboard device already exists */
174 Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0");
175 if (NT_SUCCESS(Status))
176 {
177 ++cMaxWaitObjects;
178 TRACE("Keyboard connected!\n");
179 // Get and load keyboard attributes.
180 UserInitKeyboard(ghKeyboardDevice);
181 UserEnterExclusive();
182 // Register the Window hotkey.
183 UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0);
184 // Register the debug hotkeys.
185 StartDebugHotKeys();
186 UserLeave();
187 }
188 }
189
190 /* Reset WaitHandles array */
191 cWaitObjects = 0;
192 WaitObjects[cWaitObjects++] = MasterTimer;
193
194 if (ghMouseDevice)
195 {
196 /* Try to read from mouse if previous reading is not pending */
197 if (MouStatus != STATUS_PENDING)
198 {
199 MouStatus = ZwReadFile(ghMouseDevice,
200 NULL,
201 NULL,
202 NULL,
203 &MouIosb,
204 &MouseInput,
205 sizeof(MOUSE_INPUT_DATA),
206 &ByteOffset,
207 NULL);
208 }
209
210 if (MouStatus == STATUS_PENDING)
211 WaitObjects[cWaitObjects++] = &pMouDevice->Event;
212 }
213
214 if (ghKeyboardDevice)
215 {
216 /* Try to read from keyboard if previous reading is not pending */
217 if (KbdStatus != STATUS_PENDING)
218 {
219 KbdStatus = ZwReadFile(ghKeyboardDevice,
220 NULL,
221 NULL,
222 NULL,
223 &KbdIosb,
224 &KeyInput,
225 sizeof(KEYBOARD_INPUT_DATA),
226 &ByteOffset,
227 NULL);
228
229 }
230 if (KbdStatus == STATUS_PENDING)
231 WaitObjects[cWaitObjects++] = &pKbdDevice->Event;
232 }
233
234 /* If all objects are pending, wait for them */
235 if (cWaitObjects == cMaxWaitObjects)
236 {
237 Status = KeWaitForMultipleObjects(cWaitObjects,
238 WaitObjects,
239 WaitAny,
240 UserRequest,
241 KernelMode,
242 TRUE,
243 NULL,//&WaitTimeout,
244 NULL);
245
246 if ((Status >= STATUS_WAIT_0) &&
247 (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects)))
248 {
249 /* Some device has finished reading */
250 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
251
252 /* Check if it is mouse or keyboard and update status */
253 if ((MouStatus == STATUS_PENDING) &&
254 (pSignaledObject == &pMouDevice->Event))
255 {
256 MouStatus = MouIosb.Status;
257 }
258 else if ((KbdStatus == STATUS_PENDING) &&
259 (pSignaledObject == &pKbdDevice->Event))
260 {
261 KbdStatus = KbdIosb.Status;
262 }
263 else if (pSignaledObject == MasterTimer)
264 {
265 ProcessTimers();
266 }
267 else ASSERT(FALSE);
268 }
269 }
270
271 /* Have we successed reading from mouse? */
272 if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING)
273 {
274 TRACE("MouseEvent\n");
275
276 /* Set LastInputTick */
277 IntLastInputTick(TRUE);
278
279 /* Process data */
280 UserEnterExclusive();
281 UserProcessMouseInput(&MouseInput);
282 UserLeave();
283 }
284 else if (MouStatus != STATUS_PENDING)
285 ERR("Failed to read from mouse: %x.\n", MouStatus);
286
287 /* Have we successed reading from keyboard? */
288 if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING)
289 {
290 TRACE("KeyboardEvent: %s %04x\n",
291 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
292 KeyInput.MakeCode);
293
294 /* Set LastInputTick */
295 IntLastInputTick(TRUE);
296
297 /* Process data */
298 UserEnterExclusive();
299 UserProcessKeyboardInput(&KeyInput);
300 UserLeave();
301 }
302 else if (KbdStatus != STATUS_PENDING)
303 ERR("Failed to read from keyboard: %x.\n", KbdStatus);
304 }
305 ERR("Raw Input Thread Exit!\n");
306 }
307
308 /*
309 * CreateSystemThreads
310 *
311 * Called form dedicated thread in CSRSS. RIT is started in context of this
312 * thread because it needs valid Win32 process with TEB initialized.
313 */
314 DWORD NTAPI
315 CreateSystemThreads(UINT Type)
316 {
317 UserLeave();
318
319 switch (Type)
320 {
321 case 0: RawInputThreadMain(); break;
322 case 1: DesktopThreadMain(); break;
323 default: ERR("Wrong type: %x\n", Type);
324 }
325
326 UserEnterShared();
327
328 return 0;
329 }
330
331 /*
332 * InitInputImpl
333 *
334 * Inits input implementation
335 */
336 INIT_FUNCTION
337 NTSTATUS
338 NTAPI
339 InitInputImpl(VOID)
340 {
341 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
342 if (!MasterTimer)
343 {
344 ERR("Failed to allocate memory\n");
345 ASSERT(FALSE);
346 return STATUS_UNSUCCESSFUL;
347 }
348 KeInitializeTimer(MasterTimer);
349
350 return STATUS_SUCCESS;
351 }
352
353 BOOL FASTCALL
354 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
355 {
356 PTHREADINFO OldBlock;
357 ASSERT(pti);
358
359 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
360 {
361 /*
362 * Fail blocking if exiting the thread
363 */
364
365 return FALSE;
366 }
367
368 /*
369 * FIXME: Check access rights of the window station
370 * e.g. services running in the service window station cannot block input
371 */
372 if(!ThreadHasInputAccess(pti) ||
373 !IntIsActiveDesktop(pti->rpdesk))
374 {
375 EngSetLastError(ERROR_ACCESS_DENIED);
376 return FALSE;
377 }
378
379 ASSERT(pti->rpdesk);
380 OldBlock = pti->rpdesk->BlockInputThread;
381 if(OldBlock)
382 {
383 if(OldBlock != pti)
384 {
385 EngSetLastError(ERROR_ACCESS_DENIED);
386 return FALSE;
387 }
388 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
389 return OldBlock == NULL;
390 }
391
392 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
393 return OldBlock == NULL;
394 }
395
396 BOOL
397 APIENTRY
398 NtUserBlockInput(
399 BOOL BlockIt)
400 {
401 BOOL ret;
402
403 TRACE("Enter NtUserBlockInput\n");
404 UserEnterExclusive();
405
406 ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
407
408 UserLeave();
409 TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
410
411 return ret;
412 }
413
414 BOOL
415 FASTCALL
416 IsRemoveAttachThread(PTHREADINFO pti)
417 {
418 NTSTATUS Status;
419 PATTACHINFO pai;
420 BOOL Ret = TRUE;
421 PTHREADINFO ptiFrom = NULL, ptiTo = NULL;
422
423 do
424 {
425 if (!gpai) return TRUE;
426
427 pai = gpai; // Bottom of the list.
428
429 do
430 {
431 if (pai->pti2 == pti)
432 {
433 ptiFrom = pai->pti1;
434 ptiTo = pti;
435 break;
436 }
437 if (pai->pti1 == pti)
438 {
439 ptiFrom = pti;
440 ptiTo = pai->pti2;
441 break;
442 }
443 pai = pai->paiNext;
444
445 } while (pai);
446
447 if (!pai && !ptiFrom && !ptiTo) break;
448
449 Status = UserAttachThreadInput(ptiFrom, ptiTo, FALSE);
450 if (!NT_SUCCESS(Status)) Ret = FALSE;
451
452 } while (Ret);
453
454 return Ret;
455 }
456
457 NTSTATUS FASTCALL
458 UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
459 {
460 MSG msg;
461 PATTACHINFO pai;
462 PCURICON_OBJECT CurIcon;
463
464 /* Can not be the same thread. */
465 if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
466
467 /* Do not attach to system threads or between different desktops. */
468 if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE ||
469 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
470 ptiFrom->rpdesk != ptiTo->rpdesk)
471 return STATUS_ACCESS_DENIED;
472
473 /* MSDN Note:
474 Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
475 */
476
477 /* If Attach set, allocate and link. */
478 if (fAttach)
479 {
480 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
481 if (!pai) return STATUS_NO_MEMORY;
482
483 pai->paiNext = gpai;
484 pai->pti1 = ptiFrom;
485 pai->pti2 = ptiTo;
486 gpai = pai;
487 paiCount++;
488 ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
489
490 if (ptiTo->MessageQueue != ptiFrom->MessageQueue)
491 {
492
493 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
494
495 if (ptiFrom->MessageQueue == gpqForeground)
496 {
497 ERR("ptiFrom is Foreground\n");
498 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
499 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus;
500 ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture;
501 ptiTo->MessageQueue->QF_flags ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED);
502 ptiTo->MessageQueue->CaretInfo = ptiFrom->MessageQueue->CaretInfo;
503 IntSetFocusMessageQueue(NULL);
504 IntSetFocusMessageQueue(ptiTo->MessageQueue);
505 gptiForeground = ptiTo;
506 }
507 else
508 {
509 ERR("ptiFrom NOT Foreground\n");
510 if ( ptiTo->MessageQueue->spwndActive == 0 )
511 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
512 if ( ptiTo->MessageQueue->spwndFocus == 0 )
513 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus;
514 }
515
516 CurIcon = ptiFrom->MessageQueue->CursorObject;
517
518 MsqDestroyMessageQueue(ptiFrom);
519
520 if (CurIcon)
521 {
522 // Could be global. Keep it above the water line!
523 UserReferenceObject(CurIcon);
524 }
525
526 if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon)))
527 {
528 UserDereferenceObject(CurIcon);
529 CurIcon = NULL;
530 }
531
532 ptiFrom->MessageQueue = ptiTo->MessageQueue;
533
534 // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID.
535 if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL)
536 {
537 ERR("ptiTo receiving ptiFrom Cursor\n");
538 ptiTo->MessageQueue->CursorObject = CurIcon;
539 }
540
541 ptiFrom->MessageQueue->cThreads++;
542 ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
543
544 IntReferenceMessageQueue(ptiTo->MessageQueue);
545 }
546 else
547 {
548 ERR("Attach Threads are already associated!\n");
549 }
550 }
551 else /* If clear, unlink and free it. */
552 {
553 BOOL Hit = FALSE;
554 PATTACHINFO *ppai;
555
556 if (!gpai) return STATUS_INVALID_PARAMETER;
557
558 /* Search list and free if found or return false. */
559 ppai = &gpai;
560 while (*ppai != NULL)
561 {
562 if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom )
563 {
564 pai = *ppai;
565 /* Remove it from the list */
566 *ppai = (*ppai)->paiNext;
567 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
568 paiCount--;
569 Hit = TRUE;
570 break;
571 }
572 ppai = &((*ppai)->paiNext);
573 }
574
575 if (!Hit) return STATUS_INVALID_PARAMETER;
576
577 ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
578
579 if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
580 {
581 PWND spwndActive = ptiTo->MessageQueue->spwndActive;
582 PWND spwndFocus = ptiTo->MessageQueue->spwndFocus;
583
584 if (gptiForeground == ptiFrom)
585 {
586 ERR("ptiTo is now pti FG.\n");
587 // MessageQueue foreground is set so switch threads.
588 gptiForeground = ptiTo;
589 }
590 ptiTo->MessageQueue->cThreads--;
591 ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads);
592 ASSERT(ptiTo->MessageQueue->cThreads >= 1);
593
594 IntDereferenceMessageQueue(ptiTo->MessageQueue);
595
596 ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
597
598 if (spwndActive)
599 {
600 if (spwndActive->head.pti == ptiFrom)
601 {
602 ptiFrom->MessageQueue->spwndActive = spwndActive;
603 ptiTo->MessageQueue->spwndActive = 0;
604 }
605 }
606 if (spwndFocus)
607 {
608 if (spwndFocus->head.pti == ptiFrom)
609 {
610 ptiFrom->MessageQueue->spwndFocus = spwndFocus;
611 ptiTo->MessageQueue->spwndFocus = 0;
612 }
613 }
614 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
615 }
616 else
617 {
618 ERR("Detaching Threads are not associated!\n");
619 }
620 }
621 /* Note that key state, which can be ascertained by calls to the GetKeyState
622 or GetKeyboardState function, is reset after a call to AttachThreadInput.
623 ATM which one?
624 */
625 RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
626
627 ptiTo->MessageQueue->msgDblClk.message = 0;
628
629 /* Generate mouse move message */
630 msg.message = WM_MOUSEMOVE;
631 msg.wParam = UserGetMouseButtonsState();
632 msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
633 msg.pt = gpsi->ptCursor;
634 co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
635
636 return STATUS_SUCCESS;
637 }
638
639 BOOL
640 APIENTRY
641 NtUserAttachThreadInput(
642 IN DWORD idAttach,
643 IN DWORD idAttachTo,
644 IN BOOL fAttach)
645 {
646 NTSTATUS Status;
647 PTHREADINFO pti, ptiTo;
648 BOOL Ret = FALSE;
649
650 UserEnterExclusive();
651 TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
652
653 pti = IntTID2PTI((HANDLE)idAttach);
654 ptiTo = IntTID2PTI((HANDLE)idAttachTo);
655
656 if ( !pti || !ptiTo )
657 {
658 TRACE("AttachThreadInput pti or ptiTo NULL.\n");
659 EngSetLastError(ERROR_INVALID_PARAMETER);
660 goto Exit;
661 }
662
663 Status = UserAttachThreadInput( pti, ptiTo, fAttach);
664 if (!NT_SUCCESS(Status))
665 {
666 TRACE("AttachThreadInput Error Status 0x%x. \n",Status);
667 EngSetLastError(RtlNtStatusToDosError(Status));
668 }
669 else Ret = TRUE;
670
671 Exit:
672 TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret);
673 UserLeave();
674 return Ret;
675 }
676
677 /*
678 * NtUserSendInput
679 *
680 * Generates input events from software
681 */
682 UINT
683 APIENTRY
684 NtUserSendInput(
685 UINT nInputs,
686 LPINPUT pInput,
687 INT cbSize)
688 {
689 PTHREADINFO pti;
690 UINT uRet = 0;
691
692 TRACE("Enter NtUserSendInput\n");
693 UserEnterExclusive();
694
695 pti = PsGetCurrentThreadWin32Thread();
696 ASSERT(pti);
697
698 if (!pti->rpdesk)
699 {
700 goto cleanup;
701 }
702
703 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
704 {
705 EngSetLastError(ERROR_INVALID_PARAMETER);
706 goto cleanup;
707 }
708
709 /*
710 * FIXME: Check access rights of the window station
711 * e.g. services running in the service window station cannot block input
712 */
713 if (!ThreadHasInputAccess(pti) ||
714 !IntIsActiveDesktop(pti->rpdesk))
715 {
716 EngSetLastError(ERROR_ACCESS_DENIED);
717 goto cleanup;
718 }
719
720 while (nInputs--)
721 {
722 INPUT SafeInput;
723 NTSTATUS Status;
724
725 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
726 if (!NT_SUCCESS(Status))
727 {
728 SetLastNtError(Status);
729 goto cleanup;
730 }
731
732 switch (SafeInput.type)
733 {
734 case INPUT_MOUSE:
735 if (UserSendMouseInput(&SafeInput.mi, TRUE))
736 uRet++;
737 break;
738 case INPUT_KEYBOARD:
739 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
740 uRet++;
741 break;
742 case INPUT_HARDWARE:
743 FIXME("INPUT_HARDWARE not supported!");
744 break;
745 default:
746 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
747 break;
748 }
749 }
750
751 cleanup:
752 TRACE("Leave NtUserSendInput, ret=%u\n", uRet);
753 UserLeave();
754 return uRet;
755 }
756
757 /* EOF */