xref: /reactos/win32ss/user/ntuser/input.c (revision 2ea03b5b)
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
IntLastInputTick(BOOL bUpdate)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 screensaver should be started and sends message to SAS window
46  */
47 VOID FASTCALL
DoTheScreenSaver(VOID)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
OpenInputDevice(PHANDLE pHandle,PFILE_OBJECT * ppObject,CONST WCHAR * pszDeviceName)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
RawInputThreadMain(VOID)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 CODE_SEG("INIT")
358 NTSTATUS
359 NTAPI
InitInputImpl(VOID)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
IntBlockInput(PTHREADINFO pti,BOOL BlockIt)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
NtUserBlockInput(BOOL BlockIt)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
IsRemoveAttachThread(PTHREADINFO pti)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 // Win: zzzAttachThreadInput
479 NTSTATUS FASTCALL
UserAttachThreadInput(PTHREADINFO ptiFrom,PTHREADINFO ptiTo,BOOL fAttach)480 UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
481 {
482     MSG msg;
483     PATTACHINFO pai;
484     PCURICON_OBJECT CurIcon;
485 
486     /* Can not be the same thread. */
487     if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
488 
489     /* Do not attach to system threads or between different desktops. */
490     if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE ||
491         ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
492         ptiFrom->rpdesk != ptiTo->rpdesk)
493         return STATUS_ACCESS_DENIED;
494 
495     /* MSDN Note:
496        Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
497      */
498 
499     /* If Attach set, allocate and link. */
500     if (fAttach)
501     {
502         pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
503         if (!pai) return STATUS_NO_MEMORY;
504 
505         pai->paiNext = gpai;
506         pai->pti1 = ptiFrom;
507         pai->pti2 = ptiTo;
508         gpai = pai;
509         paiCount++;
510         ERR("Attach Allocated! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
511 
512         if (ptiTo->MessageQueue != ptiFrom->MessageQueue)
513         {
514 
515            ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
516 
517            if (ptiFrom->MessageQueue == gpqForeground)
518            {
519               ERR("ptiFrom is Foreground\n");
520               ptiTo->MessageQueue->spwndActive  = ptiFrom->MessageQueue->spwndActive;
521               ptiTo->MessageQueue->spwndFocus   = ptiFrom->MessageQueue->spwndFocus;
522               ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture;
523               ptiTo->MessageQueue->QF_flags    ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED);
524               RtlCopyMemory(&ptiTo->MessageQueue->CaretInfo,
525                             &ptiFrom->MessageQueue->CaretInfo,
526                             sizeof(ptiTo->MessageQueue->CaretInfo));
527               IntSetFocusMessageQueue(NULL);
528               IntSetFocusMessageQueue(ptiTo->MessageQueue);
529               gptiForeground = ptiTo;
530            }
531            else
532            {
533               ERR("ptiFrom NOT Foreground\n");
534               if ( ptiTo->MessageQueue->spwndActive == 0 )
535                   ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
536               if ( ptiTo->MessageQueue->spwndFocus == 0 )
537                   ptiTo->MessageQueue->spwndFocus  = ptiFrom->MessageQueue->spwndFocus;
538            }
539 
540            CurIcon = ptiFrom->MessageQueue->CursorObject;
541 
542            MsqDestroyMessageQueue(ptiFrom);
543 
544            if (CurIcon)
545            {
546               // Could be global. Keep it above the water line!
547               UserReferenceObject(CurIcon);
548            }
549 
550            if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon)))
551            {
552               UserDereferenceObject(CurIcon);
553               CurIcon = NULL;
554            }
555 
556            ptiFrom->MessageQueue = ptiTo->MessageQueue;
557 
558            // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID.
559            if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL)
560            {
561               ERR("ptiTo receiving ptiFrom Cursor\n");
562               ptiTo->MessageQueue->CursorObject = CurIcon;
563            }
564 
565            ptiFrom->MessageQueue->cThreads++;
566            ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
567 
568            IntReferenceMessageQueue(ptiTo->MessageQueue);
569         }
570         else
571         {
572            ERR("Attach Threads are already associated!\n");
573         }
574     }
575     else /* If clear, unlink and free it. */
576     {
577         BOOL Hit = FALSE;
578         PATTACHINFO *ppai;
579 
580         if (!gpai) return STATUS_INVALID_PARAMETER;
581 
582         /* Search list and free if found or return false. */
583         ppai = &gpai;
584         while (*ppai != NULL)
585         {
586            if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom )
587            {
588               pai = *ppai;
589               /* Remove it from the list */
590               *ppai = (*ppai)->paiNext;
591               ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
592               paiCount--;
593               Hit = TRUE;
594               break;
595            }
596            ppai = &((*ppai)->paiNext);
597         }
598 
599         if (!Hit) return STATUS_INVALID_PARAMETER;
600 
601         ERR("Attach Free! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
602 
603         if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
604         {
605            PWND spwndActive = ptiTo->MessageQueue->spwndActive;
606            PWND spwndFocus  = ptiTo->MessageQueue->spwndFocus;
607 
608            if (gptiForeground == ptiFrom)
609            {
610               ERR("ptiTo is now pti FG.\n");
611               // MessageQueue foreground is set so switch threads.
612               gptiForeground = ptiTo;
613            }
614            ptiTo->MessageQueue->cThreads--;
615            ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads);
616            ASSERT(ptiTo->MessageQueue->cThreads >= 1);
617 
618            IntDereferenceMessageQueue(ptiTo->MessageQueue);
619 
620            ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
621 
622            if (spwndActive)
623            {
624               if (spwndActive->head.pti == ptiFrom)
625               {
626                  ptiFrom->MessageQueue->spwndActive = spwndActive;
627                  ptiTo->MessageQueue->spwndActive = 0;
628               }
629            }
630            if (spwndFocus)
631            {
632               if (spwndFocus->head.pti == ptiFrom)
633               {
634                  ptiFrom->MessageQueue->spwndFocus = spwndFocus;
635                  ptiTo->MessageQueue->spwndFocus = 0;
636               }
637            }
638            ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
639         }
640         else
641         {
642            ERR("Detaching Threads are not associated!\n");
643         }
644     }
645     /* Note that key state, which can be ascertained by calls to the GetKeyState
646        or GetKeyboardState function, is reset after a call to AttachThreadInput.
647        ATM which one?
648      */
649     RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
650 
651     ptiTo->MessageQueue->msgDblClk.message = 0;
652 
653     /* Generate mouse move message */
654     msg.message = WM_MOUSEMOVE;
655     msg.wParam = UserGetMouseButtonsState();
656     msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
657     msg.pt = gpsi->ptCursor;
658     co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
659 
660     return STATUS_SUCCESS;
661 }
662 
663 BOOL
664 APIENTRY
NtUserAttachThreadInput(IN DWORD idAttach,IN DWORD idAttachTo,IN BOOL fAttach)665 NtUserAttachThreadInput(
666     IN DWORD idAttach,
667     IN DWORD idAttachTo,
668     IN BOOL fAttach)
669 {
670   NTSTATUS Status;
671   PTHREADINFO pti, ptiTo;
672   BOOL Ret = FALSE;
673 
674   UserEnterExclusive();
675   TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
676 
677   pti = IntTID2PTI(UlongToHandle(idAttach));
678   ptiTo = IntTID2PTI(UlongToHandle(idAttachTo));
679 
680   if ( !pti || !ptiTo )
681   {
682      TRACE("AttachThreadInput pti or ptiTo NULL.\n");
683      EngSetLastError(ERROR_INVALID_PARAMETER);
684      goto Exit;
685   }
686 
687   Status = UserAttachThreadInput( pti, ptiTo, fAttach);
688   if (!NT_SUCCESS(Status))
689   {
690      TRACE("AttachThreadInput Error Status 0x%x. \n",Status);
691      EngSetLastError(RtlNtStatusToDosError(Status));
692   }
693   else Ret = TRUE;
694 
695 Exit:
696   TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret);
697   UserLeave();
698   return Ret;
699 }
700 
701 /*
702  * NtUserSendInput
703  *
704  * Generates input events from software
705  */
706 UINT
707 APIENTRY
NtUserSendInput(UINT nInputs,LPINPUT pInput,INT cbSize)708 NtUserSendInput(
709     UINT nInputs,
710     LPINPUT pInput,
711     INT cbSize)
712 {
713     PTHREADINFO pti;
714     UINT uRet = 0;
715 
716     TRACE("Enter NtUserSendInput\n");
717     UserEnterExclusive();
718 
719     pti = PsGetCurrentThreadWin32Thread();
720     ASSERT(pti);
721 
722     if (!pti->rpdesk)
723     {
724         goto cleanup;
725     }
726 
727     if (!nInputs || !pInput || cbSize != sizeof(INPUT))
728     {
729         EngSetLastError(ERROR_INVALID_PARAMETER);
730         goto cleanup;
731     }
732 
733     /*
734      * FIXME: Check access rights of the window station
735      *        e.g. services running in the service window station cannot block input
736      */
737     if (!ThreadHasInputAccess(pti) ||
738         !IntIsActiveDesktop(pti->rpdesk))
739     {
740         EngSetLastError(ERROR_ACCESS_DENIED);
741         goto cleanup;
742     }
743 
744     while (nInputs--)
745     {
746         INPUT SafeInput;
747         NTSTATUS Status;
748 
749         Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
750         if (!NT_SUCCESS(Status))
751         {
752             SetLastNtError(Status);
753             goto cleanup;
754         }
755 
756         switch (SafeInput.type)
757         {
758             case INPUT_MOUSE:
759                 if (UserSendMouseInput(&SafeInput.mi, TRUE))
760                     uRet++;
761                 break;
762             case INPUT_KEYBOARD:
763                 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
764                     uRet++;
765                 break;
766             case INPUT_HARDWARE:
767                 FIXME("INPUT_HARDWARE not supported!\n");
768                 break;
769             default:
770                 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
771                 break;
772         }
773     }
774 
775 cleanup:
776     TRACE("Leave NtUserSendInput, ret=%u\n", uRet);
777     UserLeave();
778     return uRet;
779 }
780 
781 /* EOF */
782