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