2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: General input functions
5 * FILE: subsystems/win32/win32k/ntuser/input.c
6 * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Rafal Harabien (rafalh@reactos.org)
11 DBG_DEFAULT_CHANNEL(UserInput
);
13 /* GLOBALS *******************************************************************/
15 PTHREADINFO ptiRawInput
;
16 PTHREADINFO ptiKeyboard
;
18 PKTIMER MasterTimer
= NULL
;
19 PATTACHINFO gpai
= NULL
;
21 HANDLE ghKeyboardDevice
;
23 static DWORD LastInputTick
= 0;
24 static HANDLE ghMouseDevice
;
26 /* FUNCTIONS *****************************************************************/
31 * Updates or gets last input tick count
34 IntLastInputTick(BOOL bUpdate
)
38 LARGE_INTEGER TickCount
;
39 KeQueryTickCount(&TickCount
);
40 LastInputTick
= MsqCalculateMessageTime(&TickCount
);
41 if (gpsi
) gpsi
->dwLastRITEventTickCount
= LastInputTick
;
49 * Check if scrensaver should be started and sends message to SAS window
52 DoTheScreenSaver(VOID
)
54 LARGE_INTEGER TickCount
;
57 if (gspv
.iScrSaverTimeout
> 0) // Zero means Off.
59 KeQueryTickCount(&TickCount
);
60 Test
= MsqCalculateMessageTime(&TickCount
);
61 Test
= Test
- LastInputTick
;
62 TO
= 1000 * gspv
.iScrSaverTimeout
;
65 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test
, gspv
.iScrSaverTimeout
);
67 if (ppiScrnSaver
) // We are or we are not the screensaver, prevent reentry...
69 if (!(ppiScrnSaver
->W32PF_flags
& W32PF_IDLESCREENSAVER
))
71 ppiScrnSaver
->W32PF_flags
|= W32PF_IDLESCREENSAVER
;
72 ERR("Screensaver is Idle\n");
77 PUSER_MESSAGE_QUEUE ForegroundQueue
= IntGetFocusMessageQueue();
78 if (ForegroundQueue
&& ForegroundQueue
->spwndActive
)
79 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 1); // lParam 1 == Secure
81 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 0);
90 * Opens input device for asynchronous access
94 OpenInputDevice(PHANDLE pHandle
, PFILE_OBJECT
*ppObject
, CONST WCHAR
*pszDeviceName
)
96 UNICODE_STRING DeviceName
;
97 OBJECT_ATTRIBUTES ObjectAttributes
;
101 RtlInitUnicodeString(&DeviceName
, pszDeviceName
);
103 InitializeObjectAttributes(&ObjectAttributes
,
109 Status
= ZwOpenFile(pHandle
,
115 if (NT_SUCCESS(Status
) && ppObject
)
117 Status
= ObReferenceObjectByHandle(*pHandle
, SYNCHRONIZE
, NULL
, KernelMode
, (PVOID
*)ppObject
, NULL
);
118 ASSERT(NT_SUCCESS(Status
));
127 * Reads data from input devices and supports win32 timers
132 NTSTATUS MouStatus
= STATUS_UNSUCCESSFUL
, KbdStatus
= STATUS_UNSUCCESSFUL
, Status
;
133 IO_STATUS_BLOCK MouIosb
, KbdIosb
;
134 PFILE_OBJECT pKbdDevice
, pMouDevice
;
135 LARGE_INTEGER ByteOffset
;
136 //LARGE_INTEGER WaitTimeout;
137 PVOID WaitObjects
[3], pSignaledObject
= NULL
;
138 ULONG cWaitObjects
= 0, cMaxWaitObjects
= 1;
139 MOUSE_INPUT_DATA MouseInput
;
140 KEYBOARD_INPUT_DATA KeyInput
;
142 ByteOffset
.QuadPart
= (LONGLONG
)0;
143 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
145 ptiRawInput
= GetW32ThreadInfo();
146 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
147 ptiRawInput
->pClientInfo
->dwTIFlags
= ptiRawInput
->TIF_flags
;
149 TRACE("Raw Input Thread 0x%x\n", ptiRawInput
);
151 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
152 LOW_REALTIME_PRIORITY
+ 3);
154 UserEnterExclusive();
162 /* Check if mouse device already exists */
163 Status
= OpenInputDevice(&ghMouseDevice
, &pMouDevice
, L
"\\Device\\PointerClass0" );
164 if (NT_SUCCESS(Status
))
167 TRACE("Mouse connected!\n");
170 if (!ghKeyboardDevice
)
172 /* Check if keyboard device already exists */
173 Status
= OpenInputDevice(&ghKeyboardDevice
, &pKbdDevice
, L
"\\Device\\KeyboardClass0");
174 if (NT_SUCCESS(Status
))
177 TRACE("Keyboard connected!\n");
181 /* Reset WaitHandles array */
183 WaitObjects
[cWaitObjects
++] = MasterTimer
;
187 /* Try to read from mouse if previous reading is not pending */
188 if (MouStatus
!= STATUS_PENDING
)
190 MouStatus
= ZwReadFile(ghMouseDevice
,
196 sizeof(MOUSE_INPUT_DATA
),
201 if (MouStatus
== STATUS_PENDING
)
202 WaitObjects
[cWaitObjects
++] = &pMouDevice
->Event
;
205 if (ghKeyboardDevice
)
207 /* Try to read from keyboard if previous reading is not pending */
208 if (KbdStatus
!= STATUS_PENDING
)
210 KbdStatus
= ZwReadFile(ghKeyboardDevice
,
216 sizeof(KEYBOARD_INPUT_DATA
),
221 if (KbdStatus
== STATUS_PENDING
)
222 WaitObjects
[cWaitObjects
++] = &pKbdDevice
->Event
;
225 /* If all objects are pending, wait for them */
226 if (cWaitObjects
== cMaxWaitObjects
)
228 Status
= KeWaitForMultipleObjects(cWaitObjects
,
237 if ((Status
>= STATUS_WAIT_0
) &&
238 (Status
< (STATUS_WAIT_0
+ (LONG
)cWaitObjects
)))
240 /* Some device has finished reading */
241 pSignaledObject
= WaitObjects
[Status
- STATUS_WAIT_0
];
243 /* Check if it is mouse or keyboard and update status */
244 if (pSignaledObject
== &pMouDevice
->Event
)
245 MouStatus
= MouIosb
.Status
;
246 else if (pSignaledObject
== &pKbdDevice
->Event
)
247 KbdStatus
= KbdIosb
.Status
;
248 else if (pSignaledObject
== MasterTimer
)
256 /* Have we successed reading from mouse? */
257 if (NT_SUCCESS(MouStatus
) && MouStatus
!= STATUS_PENDING
)
259 TRACE("MouseEvent\n");
261 /* Set LastInputTick */
262 IntLastInputTick(TRUE
);
265 UserEnterExclusive();
266 UserProcessMouseInput(&MouseInput
);
269 else if (MouStatus
!= STATUS_PENDING
)
270 ERR("Failed to read from mouse: %x.\n", MouStatus
);
272 /* Have we successed reading from keyboard? */
273 if (NT_SUCCESS(KbdStatus
) && KbdStatus
!= STATUS_PENDING
)
275 TRACE("KeyboardEvent: %s %04x\n",
276 (KeyInput
.Flags
& KEY_BREAK
) ? "up" : "down",
279 /* Set LastInputTick */
280 IntLastInputTick(TRUE
);
283 UserEnterExclusive();
284 UserProcessKeyboardInput(&KeyInput
);
287 else if (KbdStatus
!= STATUS_PENDING
)
288 ERR("Failed to read from keyboard: %x.\n", KbdStatus
);
290 ERR("Raw Input Thread Exit!\n");
294 * CreateSystemThreads
296 * Called form dedicated thread in CSRSS. RIT is started in context of this
297 * thread because it needs valid Win32 process with TEB initialized
300 CreateSystemThreads(UINT Type
)
306 case 0: RawInputThreadMain(); break;
307 case 1: DesktopThreadMain(); break;
308 default: ERR("Wrong type: %x\n", Type
);
319 * Inits input implementation
326 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), USERTAG_SYSTEM
);
329 ERR("Failed to allocate memory\n");
331 return STATUS_UNSUCCESSFUL
;
333 KeInitializeTimer(MasterTimer
);
335 return STATUS_SUCCESS
;
339 IntBlockInput(PTHREADINFO pti
, BOOL BlockIt
)
341 PTHREADINFO OldBlock
;
344 if(!pti
->rpdesk
|| ((pti
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
347 * Fail blocking if exiting the thread
354 * FIXME: Check access rights of the window station
355 * e.g. services running in the service window station cannot block input
357 if(!ThreadHasInputAccess(pti
) ||
358 !IntIsActiveDesktop(pti
->rpdesk
))
360 EngSetLastError(ERROR_ACCESS_DENIED
);
365 OldBlock
= pti
->rpdesk
->BlockInputThread
;
370 EngSetLastError(ERROR_ACCESS_DENIED
);
373 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
374 return OldBlock
== NULL
;
377 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
378 return OldBlock
== NULL
;
388 TRACE("Enter NtUserBlockInput\n");
389 UserEnterExclusive();
391 ret
= IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
);
394 TRACE("Leave NtUserBlockInput, ret=%i\n", ret
);
400 IsThreadAttach(PTHREADINFO ptiTo
)
404 if (!gpai
) return NULL
;
409 if (pai
->pti2
== ptiTo
) break;
413 if (!pai
) return NULL
;
420 UserAttachThreadInput(PTHREADINFO ptiFrom
, PTHREADINFO ptiTo
, BOOL fAttach
)
425 /* Can not be the same thread. */
426 if (ptiFrom
== ptiTo
) return STATUS_INVALID_PARAMETER
;
428 /* Do not attach to system threads or between different desktops. */
429 if (ptiFrom
->TIF_flags
& TIF_DONTATTACHQUEUE
||
430 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
431 ptiFrom
->rpdesk
!= ptiTo
->rpdesk
)
432 return STATUS_ACCESS_DENIED
;
435 Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
438 /* If Attach set, allocate and link. */
441 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), USERTAG_ATTACHINFO
);
442 if (!pai
) return STATUS_NO_MEMORY
;
449 ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom
,ptiTo
,paiCount
);
451 if (ptiTo
->MessageQueue
== ptiFrom
->MessageQueue
)
453 ERR("Attach Threads are already associated!\n");
456 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
458 /* Keep the original queue in pqAttach (ie do not trash it in a second attachment) */
459 if (ptiFrom
->pqAttach
== NULL
)
460 ptiFrom
->pqAttach
= ptiFrom
->MessageQueue
;
461 ptiFrom
->MessageQueue
= ptiTo
->MessageQueue
;
463 ptiFrom
->MessageQueue
->cThreads
++;
464 ERR("ptiTo S Share count %d\n", ptiFrom
->MessageQueue
->cThreads
);
466 // FIXME: conditions?
467 if (ptiFrom
->pqAttach
== gpqForeground
)
469 ERR("ptiFrom is Foreground\n");
470 ptiFrom
->MessageQueue
->spwndActive
= ptiFrom
->pqAttach
->spwndActive
;
471 ptiFrom
->MessageQueue
->spwndFocus
= ptiFrom
->pqAttach
->spwndFocus
;
472 ptiFrom
->MessageQueue
->CursorObject
= ptiFrom
->pqAttach
->CursorObject
;
473 ptiFrom
->MessageQueue
->spwndCapture
= ptiFrom
->pqAttach
->spwndCapture
;
474 ptiFrom
->MessageQueue
->QF_flags
^= ((ptiFrom
->MessageQueue
->QF_flags
^ ptiFrom
->pqAttach
->QF_flags
) & QF_CAPTURELOCKED
);
475 ptiFrom
->MessageQueue
->CaretInfo
= ptiFrom
->pqAttach
->CaretInfo
;
479 ERR("ptiFrom NOT Foreground\n");
481 if (ptiTo
->MessageQueue
== gpqForeground
)
483 ERR("ptiTo is Foreground\n");
487 ERR("ptiTo NOT Foreground\n");
490 else /* If clear, unlink and free it. */
495 if (!gpai
) return STATUS_INVALID_PARAMETER
;
497 /* Search list and free if found or return false. */
499 while (*ppai
!= NULL
)
501 if ( (*ppai
)->pti2
== ptiTo
&& (*ppai
)->pti1
== ptiFrom
)
504 /* Remove it from the list */
505 *ppai
= (*ppai
)->paiNext
;
506 ExFreePoolWithTag(pai
, USERTAG_ATTACHINFO
);
511 ppai
= &((*ppai
)->paiNext
);
514 if (!Hit
) return STATUS_INVALID_PARAMETER
;
516 ASSERT(ptiFrom
->pqAttach
);
518 ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom
,ptiTo
,paiCount
);
520 /* Search list and check if the thread is attached one more time */
524 /* If the thread is attached again , we are done */
525 if (pai
->pti1
== ptiFrom
)
527 ptiFrom
->MessageQueue
->cThreads
--;
528 ERR("ptiTo L Share count %d\n", ptiFrom
->MessageQueue
->cThreads
);
529 /* Use the message queue of the last attachment */
530 ptiFrom
->MessageQueue
= pai
->pti2
->MessageQueue
;
531 ptiFrom
->MessageQueue
->CursorObject
= NULL
;
532 ptiFrom
->MessageQueue
->spwndActive
= NULL
;
533 ptiFrom
->MessageQueue
->spwndFocus
= NULL
;
534 ptiFrom
->MessageQueue
->spwndCapture
= NULL
;
535 return STATUS_SUCCESS
;
540 ptiFrom
->MessageQueue
->cThreads
--;
541 ERR("ptiTo E Share count %d\n", ptiFrom
->MessageQueue
->cThreads
);
542 ptiFrom
->MessageQueue
= ptiFrom
->pqAttach
;
543 // FIXME: conditions?
544 ptiFrom
->MessageQueue
->CursorObject
= NULL
;
545 ptiFrom
->MessageQueue
->spwndActive
= NULL
;
546 ptiFrom
->MessageQueue
->spwndFocus
= NULL
;
547 ptiFrom
->MessageQueue
->spwndCapture
= NULL
;
548 ptiFrom
->pqAttach
= NULL
;
549 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
551 /* Note that key state, which can be ascertained by calls to the GetKeyState
552 or GetKeyboardState function, is reset after a call to AttachThreadInput.
555 RtlCopyMemory(ptiTo
->MessageQueue
->afKeyState
, gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
557 /* Generate mouse move message */
558 msg
.message
= WM_MOUSEMOVE
;
559 msg
.wParam
= UserGetMouseButtonsState();
560 msg
.lParam
= MAKELPARAM(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
561 msg
.pt
= gpsi
->ptCursor
;
562 co_MsqInsertMouseMessage(&msg
, 0, 0, TRUE
);
564 return STATUS_SUCCESS
;
570 * Generates input events from software
582 TRACE("Enter NtUserSendInput\n");
583 UserEnterExclusive();
585 pti
= PsGetCurrentThreadWin32Thread();
593 if (!nInputs
|| !pInput
|| cbSize
!= sizeof(INPUT
))
595 EngSetLastError(ERROR_INVALID_PARAMETER
);
600 * FIXME: Check access rights of the window station
601 * e.g. services running in the service window station cannot block input
603 if (!ThreadHasInputAccess(pti
) ||
604 !IntIsActiveDesktop(pti
->rpdesk
))
606 EngSetLastError(ERROR_ACCESS_DENIED
);
615 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
616 if (!NT_SUCCESS(Status
))
618 SetLastNtError(Status
);
622 switch (SafeInput
.type
)
625 if (UserSendMouseInput(&SafeInput
.mi
, TRUE
))
629 if (UserSendKeyboardInput(&SafeInput
.ki
, TRUE
))
633 FIXME("INPUT_HARDWARE not supported!");
636 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
642 TRACE("Leave NtUserSendInput, ret=%i\n", uRet
);