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
;
20 HANDLE ghKeyboardDevice
;
22 static DWORD LastInputTick
= 0;
23 static HANDLE ghMouseDevice
;
25 /* FUNCTIONS *****************************************************************/
30 * Updates or gets last input tick count
33 IntLastInputTick(BOOL bUpdate
)
37 LARGE_INTEGER TickCount
;
38 KeQueryTickCount(&TickCount
);
39 LastInputTick
= MsqCalculateMessageTime(&TickCount
);
40 if (gpsi
) gpsi
->dwLastRITEventTickCount
= LastInputTick
;
48 * Check if scrensaver should be started and sends message to SAS window
51 DoTheScreenSaver(VOID
)
53 LARGE_INTEGER TickCount
;
56 if (gspv
.iScrSaverTimeout
> 0) // Zero means Off.
58 KeQueryTickCount(&TickCount
);
59 Test
= MsqCalculateMessageTime(&TickCount
);
60 Test
= Test
- LastInputTick
;
61 TO
= 1000 * gspv
.iScrSaverTimeout
;
64 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test
, gspv
.iScrSaverTimeout
);
66 if (ppiScrnSaver
) // We are or we are not the screensaver, prevent reentry...
68 if (!(ppiScrnSaver
->W32PF_flags
& W32PF_IDLESCREENSAVER
))
70 ppiScrnSaver
->W32PF_flags
|= W32PF_IDLESCREENSAVER
;
71 ERR("Screensaver is Idle\n");
76 PUSER_MESSAGE_QUEUE ForegroundQueue
= IntGetFocusMessageQueue();
77 if (ForegroundQueue
&& ForegroundQueue
->spwndActive
)
78 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 1); // lParam 1 == Secure
80 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 0);
89 * Opens input device for asynchronous access
93 OpenInputDevice(PHANDLE pHandle
, PFILE_OBJECT
*ppObject
, CONST WCHAR
*pszDeviceName
)
95 UNICODE_STRING DeviceName
;
96 OBJECT_ATTRIBUTES ObjectAttributes
;
100 RtlInitUnicodeString(&DeviceName
, pszDeviceName
);
102 InitializeObjectAttributes(&ObjectAttributes
,
108 Status
= ZwOpenFile(pHandle
,
114 if (NT_SUCCESS(Status
) && ppObject
)
116 Status
= ObReferenceObjectByHandle(*pHandle
, SYNCHRONIZE
, NULL
, KernelMode
, (PVOID
*)ppObject
, NULL
);
117 ASSERT(NT_SUCCESS(Status
));
126 * Reads data from input devices and supports win32 timers
131 NTSTATUS MouStatus
= STATUS_UNSUCCESSFUL
, KbdStatus
= STATUS_UNSUCCESSFUL
, Status
;
132 IO_STATUS_BLOCK MouIosb
, KbdIosb
;
133 PFILE_OBJECT pKbdDevice
, pMouDevice
;
134 LARGE_INTEGER ByteOffset
;
135 //LARGE_INTEGER WaitTimeout;
136 PVOID WaitObjects
[3], pSignaledObject
= NULL
;
137 ULONG cWaitObjects
= 0, cMaxWaitObjects
= 1;
138 MOUSE_INPUT_DATA MouseInput
;
139 KEYBOARD_INPUT_DATA KeyInput
;
141 ByteOffset
.QuadPart
= (LONGLONG
)0;
142 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
144 ptiRawInput
= GetW32ThreadInfo();
145 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
146 ptiRawInput
->pClientInfo
->dwTIFlags
= ptiRawInput
->TIF_flags
;
148 TRACE("Raw Input Thread 0x%x\n", ptiRawInput
);
150 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
151 LOW_REALTIME_PRIORITY
+ 3);
153 UserEnterExclusive();
161 /* Check if mouse device already exists */
162 Status
= OpenInputDevice(&ghMouseDevice
, &pMouDevice
, L
"\\Device\\PointerClass0" );
163 if (NT_SUCCESS(Status
))
166 TRACE("Mouse connected!\n");
169 if (!ghKeyboardDevice
)
171 /* Check if keyboard device already exists */
172 Status
= OpenInputDevice(&ghKeyboardDevice
, &pKbdDevice
, L
"\\Device\\KeyboardClass0");
173 if (NT_SUCCESS(Status
))
176 TRACE("Keyboard connected!\n");
180 /* Reset WaitHandles array */
182 WaitObjects
[cWaitObjects
++] = MasterTimer
;
186 /* Try to read from mouse if previous reading is not pending */
187 if (MouStatus
!= STATUS_PENDING
)
189 MouStatus
= ZwReadFile(ghMouseDevice
,
195 sizeof(MOUSE_INPUT_DATA
),
200 if (MouStatus
== STATUS_PENDING
)
201 WaitObjects
[cWaitObjects
++] = &pMouDevice
->Event
;
204 if (ghKeyboardDevice
)
206 /* Try to read from keyboard if previous reading is not pending */
207 if (KbdStatus
!= STATUS_PENDING
)
209 KbdStatus
= ZwReadFile(ghKeyboardDevice
,
215 sizeof(KEYBOARD_INPUT_DATA
),
220 if (KbdStatus
== STATUS_PENDING
)
221 WaitObjects
[cWaitObjects
++] = &pKbdDevice
->Event
;
224 /* If all objects are pending, wait for them */
225 if (cWaitObjects
== cMaxWaitObjects
)
227 Status
= KeWaitForMultipleObjects(cWaitObjects
,
236 if ((Status
>= STATUS_WAIT_0
) &&
237 (Status
< (STATUS_WAIT_0
+ (LONG
)cWaitObjects
)))
239 /* Some device has finished reading */
240 pSignaledObject
= WaitObjects
[Status
- STATUS_WAIT_0
];
242 /* Check if it is mouse or keyboard and update status */
243 if (pSignaledObject
== &pMouDevice
->Event
)
244 MouStatus
= MouIosb
.Status
;
245 else if (pSignaledObject
== &pKbdDevice
->Event
)
246 KbdStatus
= KbdIosb
.Status
;
247 else if (pSignaledObject
== MasterTimer
)
255 /* Have we successed reading from mouse? */
256 if (NT_SUCCESS(MouStatus
) && MouStatus
!= STATUS_PENDING
)
258 TRACE("MouseEvent\n");
260 /* Set LastInputTick */
261 IntLastInputTick(TRUE
);
264 UserEnterExclusive();
265 UserProcessMouseInput(&MouseInput
);
268 else if (MouStatus
!= STATUS_PENDING
)
269 ERR("Failed to read from mouse: %x.\n", MouStatus
);
271 /* Have we successed reading from keyboard? */
272 if (NT_SUCCESS(KbdStatus
) && KbdStatus
!= STATUS_PENDING
)
274 TRACE("KeyboardEvent: %s %04x\n",
275 (KeyInput
.Flags
& KEY_BREAK
) ? "up" : "down",
278 /* Set LastInputTick */
279 IntLastInputTick(TRUE
);
282 UserEnterExclusive();
283 UserProcessKeyboardInput(&KeyInput
);
286 else if (KbdStatus
!= STATUS_PENDING
)
287 ERR("Failed to read from keyboard: %x.\n", KbdStatus
);
289 ERR("Raw Input Thread Exit!\n");
293 * CreateSystemThreads
295 * Called form dedicated thread in CSRSS. RIT is started in context of this
296 * thread because it needs valid Win32 process with TEB initialized
299 CreateSystemThreads(UINT Type
)
305 case 0: RawInputThreadMain(); break;
306 default: ERR("Wrong type: %x\n", Type
);
317 * Inits input implementation
324 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), USERTAG_SYSTEM
);
327 ERR("Failed to allocate memory\n");
329 return STATUS_UNSUCCESSFUL
;
331 KeInitializeTimer(MasterTimer
);
333 return STATUS_SUCCESS
;
337 IntBlockInput(PTHREADINFO pti
, BOOL BlockIt
)
339 PTHREADINFO OldBlock
;
342 if(!pti
->rpdesk
|| ((pti
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
345 * Fail blocking if exiting the thread
352 * FIXME: Check access rights of the window station
353 * e.g. services running in the service window station cannot block input
355 if(!ThreadHasInputAccess(pti
) ||
356 !IntIsActiveDesktop(pti
->rpdesk
))
358 EngSetLastError(ERROR_ACCESS_DENIED
);
363 OldBlock
= pti
->rpdesk
->BlockInputThread
;
368 EngSetLastError(ERROR_ACCESS_DENIED
);
371 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
372 return OldBlock
== NULL
;
375 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
376 return OldBlock
== NULL
;
386 TRACE("Enter NtUserBlockInput\n");
387 UserEnterExclusive();
389 ret
= IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
);
392 TRACE("Leave NtUserBlockInput, ret=%i\n", ret
);
398 UserAttachThreadInput(PTHREADINFO ptiFrom
, PTHREADINFO ptiTo
, BOOL fAttach
)
403 /* Can not be the same thread. */
404 if (ptiFrom
== ptiTo
) return FALSE
;
406 /* Do not attach to system threads or between different desktops. */
407 if (ptiFrom
->TIF_flags
& TIF_DONTATTACHQUEUE
||
408 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
409 ptiFrom
->rpdesk
!= ptiTo
->rpdesk
)
412 /* If Attach set, allocate and link. */
415 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), USERTAG_ATTACHINFO
);
416 if (!pai
) return FALSE
;
422 TRACE("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p\n",ptiFrom
,ptiTo
);
424 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
425 ptiFrom
->pqAttach
= ptiFrom
->MessageQueue
;
426 ptiFrom
->MessageQueue
= ptiTo
->MessageQueue
;
427 // FIXME: conditions?
428 if (ptiFrom
->pqAttach
== gpqForeground
)
430 ptiFrom
->MessageQueue
->spwndActive
= ptiFrom
->pqAttach
->spwndActive
;
431 ptiFrom
->MessageQueue
->spwndFocus
= ptiFrom
->pqAttach
->spwndFocus
;
432 ptiFrom
->MessageQueue
->CursorObject
= ptiFrom
->pqAttach
->CursorObject
;
433 ptiFrom
->MessageQueue
->CaptureWindow
= ptiFrom
->pqAttach
->CaptureWindow
;
434 ptiFrom
->MessageQueue
->spwndCapture
= ptiFrom
->pqAttach
->spwndCapture
;
435 ptiFrom
->MessageQueue
->QF_flags
^= ((ptiFrom
->MessageQueue
->QF_flags
^ ptiFrom
->pqAttach
->QF_flags
) & QF_CAPTURELOCKED
);
436 ptiFrom
->MessageQueue
->CaretInfo
= ptiFrom
->pqAttach
->CaretInfo
;
439 else /* If clear, unlink and free it. */
441 PATTACHINFO paiprev
= NULL
;
443 if (!gpai
) return FALSE
;
447 /* Search list and free if found or return false. */
450 if (pai
->pti2
== ptiTo
&& pai
->pti1
== ptiFrom
) break;
455 if (!pai
) return FALSE
;
457 if (paiprev
) paiprev
->paiNext
= pai
->paiNext
;
459 ExFreePoolWithTag(pai
, USERTAG_ATTACHINFO
);
460 TRACE("Attach Free! ptiFrom 0x%p ptiTo 0x%p\n",ptiFrom
,ptiTo
);
462 ptiFrom
->MessageQueue
= ptiFrom
->pqAttach
;
463 // FIXME: conditions?
464 ptiFrom
->MessageQueue
->CursorObject
= NULL
;
465 ptiFrom
->MessageQueue
->spwndActive
= NULL
;
466 ptiFrom
->MessageQueue
->spwndFocus
= NULL
;
467 ptiFrom
->pqAttach
= NULL
;
468 ptiTo
->MessageQueue
->iCursorLevel
-= ptiFrom
->iCursorLevel
;
470 /* Note that key state, which can be ascertained by calls to the GetKeyState
471 or GetKeyboardState function, is reset after a call to AttachThreadInput.
474 RtlCopyMemory(ptiTo
->MessageQueue
->afKeyState
, gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
476 /* Generate mouse move message */
477 msg
.message
= WM_MOUSEMOVE
;
478 msg
.wParam
= UserGetMouseButtonsState();
479 msg
.lParam
= MAKELPARAM(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
480 msg
.pt
= gpsi
->ptCursor
;
481 co_MsqInsertMouseMessage(&msg
, 0, 0, TRUE
);
489 * Generates input events from software
501 TRACE("Enter NtUserSendInput\n");
502 UserEnterExclusive();
504 pti
= PsGetCurrentThreadWin32Thread();
512 if (!nInputs
|| !pInput
|| cbSize
!= sizeof(INPUT
))
514 EngSetLastError(ERROR_INVALID_PARAMETER
);
519 * FIXME: Check access rights of the window station
520 * e.g. services running in the service window station cannot block input
522 if (!ThreadHasInputAccess(pti
) ||
523 !IntIsActiveDesktop(pti
->rpdesk
))
525 EngSetLastError(ERROR_ACCESS_DENIED
);
534 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
535 if (!NT_SUCCESS(Status
))
537 SetLastNtError(Status
);
541 switch (SafeInput
.type
)
544 if (UserSendMouseInput(&SafeInput
.mi
, TRUE
))
548 if (UserSendKeyboardInput(&SafeInput
.ki
, TRUE
))
552 FIXME("INPUT_HARDWARE not supported!");
555 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
561 TRACE("Leave NtUserSendInput, ret=%i\n", uRet
);