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 PKTIMER MasterTimer
= NULL
;
17 PATTACHINFO gpai
= NULL
;
19 HANDLE ghKeyboardDevice
= NULL
;
21 static DWORD LastInputTick
= 0;
22 static HANDLE ghMouseDevice
;
24 /* FUNCTIONS *****************************************************************/
29 * Updates or gets last input tick count
32 IntLastInputTick(BOOL bUpdate
)
36 LARGE_INTEGER TickCount
;
37 KeQueryTickCount(&TickCount
);
38 LastInputTick
= MsqCalculateMessageTime(&TickCount
);
39 if (gpsi
) gpsi
->dwLastRITEventTickCount
= LastInputTick
;
47 * Check if scrensaver should be started and sends message to SAS window
50 DoTheScreenSaver(VOID
)
52 LARGE_INTEGER TickCount
;
55 if (gspv
.iScrSaverTimeout
> 0) // Zero means Off.
57 KeQueryTickCount(&TickCount
);
58 Test
= MsqCalculateMessageTime(&TickCount
);
59 Test
= Test
- LastInputTick
;
60 TO
= 1000 * gspv
.iScrSaverTimeout
;
63 TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test
, gspv
.iScrSaverTimeout
);
65 if (ppiScrnSaver
) // We are or we are not the screensaver, prevent reentry...
67 if (!(ppiScrnSaver
->W32PF_flags
& W32PF_IDLESCREENSAVER
))
69 ppiScrnSaver
->W32PF_flags
|= W32PF_IDLESCREENSAVER
;
70 ERR("Screensaver is Idle\n");
75 PUSER_MESSAGE_QUEUE ForegroundQueue
= IntGetFocusMessageQueue();
76 if (ForegroundQueue
&& ForegroundQueue
->spwndActive
)
77 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 1); // lParam 1 == Secure
79 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 0);
88 * Opens input device for asynchronous access
92 OpenInputDevice(PHANDLE pHandle
, PFILE_OBJECT
*ppObject
, CONST WCHAR
*pszDeviceName
)
94 UNICODE_STRING DeviceName
;
95 OBJECT_ATTRIBUTES ObjectAttributes
;
99 RtlInitUnicodeString(&DeviceName
, pszDeviceName
);
101 InitializeObjectAttributes(&ObjectAttributes
,
107 Status
= ZwOpenFile(pHandle
,
113 if (NT_SUCCESS(Status
) && ppObject
)
115 Status
= ObReferenceObjectByHandle(*pHandle
, SYNCHRONIZE
, NULL
, KernelMode
, (PVOID
*)ppObject
, NULL
);
116 ASSERT(NT_SUCCESS(Status
));
125 * Reads data from input devices and supports win32 timers
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
;
140 ByteOffset
.QuadPart
= (LONGLONG
)0;
141 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
143 ptiRawInput
= GetW32ThreadInfo();
144 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
145 ptiRawInput
->pClientInfo
->dwTIFlags
= ptiRawInput
->TIF_flags
;
147 TRACE("Raw Input Thread %p\n", ptiRawInput
);
149 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
150 LOW_REALTIME_PRIORITY
+ 3);
152 UserEnterExclusive();
160 /* Check if mouse device already exists */
161 Status
= OpenInputDevice(&ghMouseDevice
, &pMouDevice
, L
"\\Device\\PointerClass0" );
162 if (NT_SUCCESS(Status
))
165 TRACE("Mouse connected!\n");
168 if (!ghKeyboardDevice
)
170 /* Check if keyboard device already exists */
171 Status
= OpenInputDevice(&ghKeyboardDevice
, &pKbdDevice
, L
"\\Device\\KeyboardClass0");
172 if (NT_SUCCESS(Status
))
175 TRACE("Keyboard connected!\n");
176 // Get and load keyboard attributes.
177 UserInitKeyboard(ghKeyboardDevice
);
178 UserEnterExclusive();
179 // Register the Window hotkey.
180 UserRegisterHotKey(PWND_BOTTOM
, IDHK_WINKEY
, MOD_WIN
, 0);
181 // Register the debug hotkeys.
187 /* Reset WaitHandles array */
189 WaitObjects
[cWaitObjects
++] = MasterTimer
;
193 /* Try to read from mouse if previous reading is not pending */
194 if (MouStatus
!= STATUS_PENDING
)
196 MouStatus
= ZwReadFile(ghMouseDevice
,
202 sizeof(MOUSE_INPUT_DATA
),
207 if (MouStatus
== STATUS_PENDING
)
208 WaitObjects
[cWaitObjects
++] = &pMouDevice
->Event
;
211 if (ghKeyboardDevice
)
213 /* Try to read from keyboard if previous reading is not pending */
214 if (KbdStatus
!= STATUS_PENDING
)
216 KbdStatus
= ZwReadFile(ghKeyboardDevice
,
222 sizeof(KEYBOARD_INPUT_DATA
),
227 if (KbdStatus
== STATUS_PENDING
)
228 WaitObjects
[cWaitObjects
++] = &pKbdDevice
->Event
;
231 /* If all objects are pending, wait for them */
232 if (cWaitObjects
== cMaxWaitObjects
)
234 Status
= KeWaitForMultipleObjects(cWaitObjects
,
243 if ((Status
>= STATUS_WAIT_0
) &&
244 (Status
< (STATUS_WAIT_0
+ (LONG
)cWaitObjects
)))
246 /* Some device has finished reading */
247 pSignaledObject
= WaitObjects
[Status
- STATUS_WAIT_0
];
249 /* Check if it is mouse or keyboard and update status */
250 if (pSignaledObject
== &pMouDevice
->Event
)
251 MouStatus
= MouIosb
.Status
;
252 else if (pSignaledObject
== &pKbdDevice
->Event
)
253 KbdStatus
= KbdIosb
.Status
;
254 else if (pSignaledObject
== MasterTimer
)
262 /* Have we successed reading from mouse? */
263 if (NT_SUCCESS(MouStatus
) && MouStatus
!= STATUS_PENDING
)
265 TRACE("MouseEvent\n");
267 /* Set LastInputTick */
268 IntLastInputTick(TRUE
);
271 UserEnterExclusive();
272 UserProcessMouseInput(&MouseInput
);
275 else if (MouStatus
!= STATUS_PENDING
)
276 ERR("Failed to read from mouse: %x.\n", MouStatus
);
278 /* Have we successed reading from keyboard? */
279 if (NT_SUCCESS(KbdStatus
) && KbdStatus
!= STATUS_PENDING
)
281 TRACE("KeyboardEvent: %s %04x\n",
282 (KeyInput
.Flags
& KEY_BREAK
) ? "up" : "down",
285 /* Set LastInputTick */
286 IntLastInputTick(TRUE
);
289 UserEnterExclusive();
290 UserProcessKeyboardInput(&KeyInput
);
293 else if (KbdStatus
!= STATUS_PENDING
)
294 ERR("Failed to read from keyboard: %x.\n", KbdStatus
);
296 ERR("Raw Input Thread Exit!\n");
300 * CreateSystemThreads
302 * Called form dedicated thread in CSRSS. RIT is started in context of this
303 * thread because it needs valid Win32 process with TEB initialized
306 CreateSystemThreads(UINT Type
)
312 case 0: RawInputThreadMain(); break;
313 case 1: DesktopThreadMain(); break;
314 default: ERR("Wrong type: %x\n", Type
);
325 * Inits input implementation
332 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), USERTAG_SYSTEM
);
335 ERR("Failed to allocate memory\n");
337 return STATUS_UNSUCCESSFUL
;
339 KeInitializeTimer(MasterTimer
);
341 return STATUS_SUCCESS
;
345 IntBlockInput(PTHREADINFO pti
, BOOL BlockIt
)
347 PTHREADINFO OldBlock
;
350 if(!pti
->rpdesk
|| ((pti
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
353 * Fail blocking if exiting the thread
360 * FIXME: Check access rights of the window station
361 * e.g. services running in the service window station cannot block input
363 if(!ThreadHasInputAccess(pti
) ||
364 !IntIsActiveDesktop(pti
->rpdesk
))
366 EngSetLastError(ERROR_ACCESS_DENIED
);
371 OldBlock
= pti
->rpdesk
->BlockInputThread
;
376 EngSetLastError(ERROR_ACCESS_DENIED
);
379 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
380 return OldBlock
== NULL
;
383 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
384 return OldBlock
== NULL
;
394 TRACE("Enter NtUserBlockInput\n");
395 UserEnterExclusive();
397 ret
= IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
);
400 TRACE("Leave NtUserBlockInput, ret=%i\n", ret
);
406 IsThreadAttach(PTHREADINFO ptiTo
)
410 if (!gpai
) return NULL
;
415 if (pai
->pti2
== ptiTo
) break;
419 if (!pai
) return NULL
;
426 UserAttachThreadInput(PTHREADINFO ptiFrom
, PTHREADINFO ptiTo
, BOOL fAttach
)
431 /* Can not be the same thread. */
432 if (ptiFrom
== ptiTo
) return STATUS_INVALID_PARAMETER
;
434 /* Do not attach to system threads or between different desktops. */
435 if (ptiFrom
->TIF_flags
& TIF_DONTATTACHQUEUE
||
436 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
437 ptiFrom
->rpdesk
!= ptiTo
->rpdesk
)
438 return STATUS_ACCESS_DENIED
;
441 Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
444 /* If Attach set, allocate and link. */
447 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), USERTAG_ATTACHINFO
);
448 if (!pai
) return STATUS_NO_MEMORY
;
455 ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom
,ptiTo
,paiCount
);
457 if (ptiTo
->MessageQueue
== ptiFrom
->MessageQueue
)
459 ERR("Attach Threads are already associated!\n");
462 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
464 /* Keep the original queue in pqAttach (ie do not trash it in a second attachment) */
465 if (ptiFrom
->pqAttach
== NULL
)
466 ptiFrom
->pqAttach
= ptiFrom
->MessageQueue
;
467 ptiFrom
->MessageQueue
= ptiTo
->MessageQueue
;
469 ptiFrom
->MessageQueue
->cThreads
++;
470 ERR("ptiTo S Share count %lu\n", ptiFrom
->MessageQueue
->cThreads
);
472 // FIXME: conditions?
473 if (ptiFrom
->pqAttach
== gpqForeground
)
475 ERR("ptiFrom is Foreground\n");
476 ptiFrom
->MessageQueue
->spwndActive
= ptiFrom
->pqAttach
->spwndActive
;
477 ptiFrom
->MessageQueue
->spwndFocus
= ptiFrom
->pqAttach
->spwndFocus
;
478 ptiFrom
->MessageQueue
->CursorObject
= ptiFrom
->pqAttach
->CursorObject
;
479 ptiFrom
->MessageQueue
->spwndCapture
= ptiFrom
->pqAttach
->spwndCapture
;
480 ptiFrom
->MessageQueue
->QF_flags
^= ((ptiFrom
->MessageQueue
->QF_flags
^ ptiFrom
->pqAttach
->QF_flags
) & QF_CAPTURELOCKED
);
481 ptiFrom
->MessageQueue
->CaretInfo
= ptiFrom
->pqAttach
->CaretInfo
;
485 ERR("ptiFrom NOT Foreground\n");
487 if (ptiTo
->MessageQueue
== gpqForeground
)
489 ERR("ptiTo is Foreground\n");
493 ERR("ptiTo NOT Foreground\n");
496 else /* If clear, unlink and free it. */
501 if (!gpai
) return STATUS_INVALID_PARAMETER
;
503 /* Search list and free if found or return false. */
505 while (*ppai
!= NULL
)
507 if ( (*ppai
)->pti2
== ptiTo
&& (*ppai
)->pti1
== ptiFrom
)
510 /* Remove it from the list */
511 *ppai
= (*ppai
)->paiNext
;
512 ExFreePoolWithTag(pai
, USERTAG_ATTACHINFO
);
517 ppai
= &((*ppai
)->paiNext
);
520 if (!Hit
) return STATUS_INVALID_PARAMETER
;
522 ASSERT(ptiFrom
->pqAttach
);
524 ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom
,ptiTo
,paiCount
);
526 /* Search list and check if the thread is attached one more time */
530 /* If the thread is attached again , we are done */
531 if (pai
->pti1
== ptiFrom
)
533 ptiFrom
->MessageQueue
->cThreads
--;
534 ERR("ptiTo L Share count %lu\n", ptiFrom
->MessageQueue
->cThreads
);
535 /* Use the message queue of the last attachment */
536 ptiFrom
->MessageQueue
= pai
->pti2
->MessageQueue
;
537 ptiFrom
->MessageQueue
->CursorObject
= NULL
;
538 ptiFrom
->MessageQueue
->spwndActive
= NULL
;
539 ptiFrom
->MessageQueue
->spwndFocus
= NULL
;
540 ptiFrom
->MessageQueue
->spwndCapture
= NULL
;
541 return STATUS_SUCCESS
;
546 ptiFrom
->MessageQueue
->cThreads
--;
547 ERR("ptiTo E Share count %lu\n", ptiFrom
->MessageQueue
->cThreads
);
548 ptiFrom
->MessageQueue
= ptiFrom
->pqAttach
;
549 // FIXME: conditions?
550 ptiFrom
->MessageQueue
->CursorObject
= NULL
;
551 ptiFrom
->MessageQueue
->spwndActive
= NULL
;
552 ptiFrom
->MessageQueue
->spwndFocus
= NULL
;
553 ptiFrom
->MessageQueue
->spwndCapture
= NULL
;
554 ptiFrom
->pqAttach
= NULL
;
555 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
557 /* Note that key state, which can be ascertained by calls to the GetKeyState
558 or GetKeyboardState function, is reset after a call to AttachThreadInput.
561 RtlCopyMemory(ptiTo
->MessageQueue
->afKeyState
, gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
563 /* Generate mouse move message */
564 msg
.message
= WM_MOUSEMOVE
;
565 msg
.wParam
= UserGetMouseButtonsState();
566 msg
.lParam
= MAKELPARAM(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
567 msg
.pt
= gpsi
->ptCursor
;
568 co_MsqInsertMouseMessage(&msg
, 0, 0, TRUE
);
570 return STATUS_SUCCESS
;
576 * Generates input events from software
588 TRACE("Enter NtUserSendInput\n");
589 UserEnterExclusive();
591 pti
= PsGetCurrentThreadWin32Thread();
599 if (!nInputs
|| !pInput
|| cbSize
!= sizeof(INPUT
))
601 EngSetLastError(ERROR_INVALID_PARAMETER
);
606 * FIXME: Check access rights of the window station
607 * e.g. services running in the service window station cannot block input
609 if (!ThreadHasInputAccess(pti
) ||
610 !IntIsActiveDesktop(pti
->rpdesk
))
612 EngSetLastError(ERROR_ACCESS_DENIED
);
621 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
622 if (!NT_SUCCESS(Status
))
624 SetLastNtError(Status
);
628 switch (SafeInput
.type
)
631 if (UserSendMouseInput(&SafeInput
.mi
, TRUE
))
635 if (UserSendKeyboardInput(&SafeInput
.ki
, TRUE
))
639 FIXME("INPUT_HARDWARE not supported!");
642 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
648 TRACE("Leave NtUserSendInput, ret=%u\n", uRet
);