2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
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 extern NTSTATUS
Win32kInitWin32Thread(PETHREAD Thread
);
14 extern PPROCESSINFO ppiScrnSaver
;
16 /* GLOBALS *******************************************************************/
18 PTHREADINFO ptiRawInput
;
19 PTHREADINFO ptiKeyboard
;
21 PKTIMER MasterTimer
= NULL
;
22 PATTACHINFO gpai
= NULL
;
23 HANDLE ghKeyboardDevice
;
25 static DWORD LastInputTick
= 0;
26 static HANDLE ghMouseDevice
;
28 /* FUNCTIONS *****************************************************************/
33 * Updates or gets last input tick count
36 IntLastInputTick(BOOL bUpdate
)
40 LARGE_INTEGER TickCount
;
41 KeQueryTickCount(&TickCount
);
42 LastInputTick
= MsqCalculateMessageTime(&TickCount
);
43 if (gpsi
) gpsi
->dwLastRITEventTickCount
= LastInputTick
;
51 * Check if scrensaver should be started and sends message to SAS window
54 DoTheScreenSaver(VOID
)
56 LARGE_INTEGER TickCount
;
59 if (gspv
.iScrSaverTimeout
> 0) // Zero means Off.
61 KeQueryTickCount(&TickCount
);
62 Test
= MsqCalculateMessageTime(&TickCount
);
63 Test
= Test
- LastInputTick
;
64 TO
= 1000 * gspv
.iScrSaverTimeout
;
67 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test
, gspv
.iScrSaverTimeout
);
69 if (ppiScrnSaver
) // We are or we are not the screensaver, prevent reentry...
71 if (!(ppiScrnSaver
->W32PF_flags
& W32PF_IDLESCREENSAVER
))
73 ppiScrnSaver
->W32PF_flags
|= W32PF_IDLESCREENSAVER
;
74 ERR("Screensaver is Idle\n");
79 PUSER_MESSAGE_QUEUE ForegroundQueue
= IntGetFocusMessageQueue();
80 if (ForegroundQueue
&& ForegroundQueue
->ActiveWindow
)
81 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 1); // lParam 1 == Secure
83 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, LN_START_SCREENSAVE
, 0);
92 * Opens input device for asynchronous access
96 OpenInputDevice(PHANDLE pHandle
, PFILE_OBJECT
*ppObject
, CONST WCHAR
*pszDeviceName
)
98 UNICODE_STRING DeviceName
;
99 OBJECT_ATTRIBUTES ObjectAttributes
;
101 IO_STATUS_BLOCK Iosb
;
103 RtlInitUnicodeString(&DeviceName
, pszDeviceName
);
105 InitializeObjectAttributes(&ObjectAttributes
,
111 Status
= ZwOpenFile(pHandle
,
117 if (NT_SUCCESS(Status
) && ppObject
)
119 Status
= ObReferenceObjectByHandle(*pHandle
, SYNCHRONIZE
, NULL
, KernelMode
, (PVOID
*)ppObject
, NULL
);
120 ASSERT(NT_SUCCESS(Status
));
129 * Reads data from input devices and supports win32 timers
134 NTSTATUS MouStatus
= STATUS_UNSUCCESSFUL
, KbdStatus
= STATUS_UNSUCCESSFUL
, Status
;
135 IO_STATUS_BLOCK MouIosb
, KbdIosb
;
136 PFILE_OBJECT pKbdDevice
, pMouDevice
;
137 LARGE_INTEGER WaitTimeout
, ByteOffset
;
138 PVOID WaitObjects
[3], pSignaledObject
= NULL
;
139 ULONG cWaitObjects
= 0, cMaxWaitObjects
= 1;
140 MOUSE_INPUT_DATA MouseInput
;
141 KEYBOARD_INPUT_DATA KeyInput
;
143 ByteOffset
.QuadPart
= (LONGLONG
)0;
144 WaitTimeout
.QuadPart
= (LONGLONG
)(-10000000);
146 ptiRawInput
= PsGetCurrentThreadWin32Thread();
147 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
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
&& Status
< STATUS_WAIT_0
+ cWaitObjects
)
238 /* Some device has finished reading */
239 pSignaledObject
= WaitObjects
[Status
- STATUS_WAIT_0
];
241 /* Check if it is mouse or keyboard and update status */
242 if (pSignaledObject
== &pMouDevice
->Event
)
243 MouStatus
= MouIosb
.Status
;
244 else if (pSignaledObject
== &pKbdDevice
->Event
)
245 KbdStatus
= KbdIosb
.Status
;
246 else if (pSignaledObject
== MasterTimer
)
248 /* FIXME: where it should go? */
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
, MouIosb
.Information
/ sizeof(MOUSE_INPUT_DATA
));
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 /* Initialize the default keyboard layout */
334 if (!UserInitDefaultKeyboardLayout())
336 ERR("Failed to initialize default keyboard layout!\n");
339 return STATUS_SUCCESS
;
345 * Cleans input implementation
348 CleanupInputImp(VOID
)
350 return STATUS_SUCCESS
;
354 IntBlockInput(PTHREADINFO pti
, BOOL BlockIt
)
356 PTHREADINFO OldBlock
;
359 if(!pti
->rpdesk
|| ((pti
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
362 * fail blocking if exiting the thread
369 * FIXME - check access rights of the window station
370 * e.g. services running in the service window station cannot block input
372 if(!ThreadHasInputAccess(pti
) ||
373 !IntIsActiveDesktop(pti
->rpdesk
))
375 EngSetLastError(ERROR_ACCESS_DENIED
);
380 OldBlock
= pti
->rpdesk
->BlockInputThread
;
385 EngSetLastError(ERROR_ACCESS_DENIED
);
388 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
389 return OldBlock
== NULL
;
392 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
393 return OldBlock
== NULL
;
403 TRACE("Enter NtUserBlockInput\n");
404 UserEnterExclusive();
406 ret
= IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
);
409 TRACE("Leave NtUserBlockInput, ret=%i\n", ret
);
415 UserAttachThreadInput(PTHREADINFO pti
, PTHREADINFO ptiTo
, BOOL fAttach
)
419 /* Can not be the same thread.*/
420 if (pti
== ptiTo
) return FALSE
;
422 /* Do not attach to system threads or between different desktops. */
423 if (pti
->TIF_flags
& TIF_DONTATTACHQUEUE
||
424 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
425 pti
->rpdesk
!= ptiTo
->rpdesk
)
428 /* If Attach set, allocate and link. */
431 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), USERTAG_ATTACHINFO
);
432 if (!pai
) return FALSE
;
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
== pti
) break;
455 if (!pai
) return FALSE
;
457 if (paiprev
) paiprev
->paiNext
= pai
->paiNext
;
459 ExFreePoolWithTag(pai
, USERTAG_ATTACHINFO
);
468 * Generates input events from software
480 TRACE("Enter NtUserSendInput\n");
481 UserEnterExclusive();
483 pti
= PsGetCurrentThreadWin32Thread();
491 if (!nInputs
|| !pInput
|| cbSize
!= sizeof(INPUT
))
493 EngSetLastError(ERROR_INVALID_PARAMETER
);
498 * FIXME - check access rights of the window station
499 * e.g. services running in the service window station cannot block input
501 if (!ThreadHasInputAccess(pti
) ||
502 !IntIsActiveDesktop(pti
->rpdesk
))
504 EngSetLastError(ERROR_ACCESS_DENIED
);
513 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
514 if (!NT_SUCCESS(Status
))
516 SetLastNtError(Status
);
520 switch (SafeInput
.type
)
523 if (IntMouseInput(&SafeInput
.mi
, TRUE
))
527 if (UserSendKeyboardInput(&SafeInput
.ki
, TRUE
))
531 FIXME("INPUT_HARDWARE not supported!");
534 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
540 TRACE("Leave NtUserSendInput, ret=%i\n", uRet
);