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);
149 KeInitializeEvent(&Event, NotificationEvent, FALSE);
150 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &WaitTimeout);
151 } while (!NT_SUCCESS(Status));*/
153 ptiRawInput
= PsGetCurrentThreadWin32Thread();
154 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
155 TRACE("Raw Input Thread 0x%x\n", ptiRawInput
);
157 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
158 LOW_REALTIME_PRIORITY
+ 3);
160 UserEnterExclusive();
168 /* Check if mouse device already exists */
169 Status
= OpenInputDevice(&ghMouseDevice
, &pMouDevice
, L
"\\Device\\PointerClass0" );
170 if (NT_SUCCESS(Status
))
173 TRACE("Mouse connected!\n");
176 if (!ghKeyboardDevice
)
178 /* Check if keyboard device already exists */
179 Status
= OpenInputDevice(&ghKeyboardDevice
, &pKbdDevice
, L
"\\Device\\KeyboardClass0");
180 if (NT_SUCCESS(Status
))
183 TRACE("Keyboard connected!\n");
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
&& Status
< STATUS_WAIT_0
+ cWaitObjects
)
245 /* Some device has finished reading */
246 pSignaledObject
= WaitObjects
[Status
- STATUS_WAIT_0
];
248 /* Check if it is mouse or keyboard and update status */
249 if (pSignaledObject
== &pMouDevice
->Event
)
250 MouStatus
= MouIosb
.Status
;
251 else if (pSignaledObject
== &pKbdDevice
->Event
)
252 KbdStatus
= KbdIosb
.Status
;
253 else if (pSignaledObject
== MasterTimer
)
255 /* FIXME: where it should go? */
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
, MouIosb
.Information
/ sizeof(MOUSE_INPUT_DATA
));
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 default: ERR("Wrong type: %x\n", Type
);
324 * Inits input implementation
331 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), USERTAG_SYSTEM
);
334 ERR("Failed to allocate memory\n");
336 return STATUS_UNSUCCESSFUL
;
338 KeInitializeTimer(MasterTimer
);
340 /* Initialize the default keyboard layout */
341 if (!UserInitDefaultKeyboardLayout())
343 ERR("Failed to initialize default keyboard layout!\n");
346 return STATUS_SUCCESS
;
352 * Cleans input implementation
355 CleanupInputImp(VOID
)
357 return STATUS_SUCCESS
;
361 IntBlockInput(PTHREADINFO pti
, BOOL BlockIt
)
363 PTHREADINFO OldBlock
;
366 if(!pti
->rpdesk
|| ((pti
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
369 * fail blocking if exiting the thread
376 * FIXME - check access rights of the window station
377 * e.g. services running in the service window station cannot block input
379 if(!ThreadHasInputAccess(pti
) ||
380 !IntIsActiveDesktop(pti
->rpdesk
))
382 EngSetLastError(ERROR_ACCESS_DENIED
);
387 OldBlock
= pti
->rpdesk
->BlockInputThread
;
392 EngSetLastError(ERROR_ACCESS_DENIED
);
395 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
396 return OldBlock
== NULL
;
399 pti
->rpdesk
->BlockInputThread
= (BlockIt
? pti
: NULL
);
400 return OldBlock
== NULL
;
410 TRACE("Enter NtUserBlockInput\n");
411 UserEnterExclusive();
413 ret
= IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
);
416 TRACE("Leave NtUserBlockInput, ret=%i\n", ret
);
422 UserAttachThreadInput(PTHREADINFO pti
, PTHREADINFO ptiTo
, BOOL fAttach
)
426 /* Can not be the same thread.*/
427 if (pti
== ptiTo
) return FALSE
;
429 /* Do not attach to system threads or between different desktops. */
430 if (pti
->TIF_flags
& TIF_DONTATTACHQUEUE
||
431 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
432 pti
->rpdesk
!= ptiTo
->rpdesk
)
435 /* If Attach set, allocate and link. */
438 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), USERTAG_ATTACHINFO
);
439 if (!pai
) return FALSE
;
446 else /* If clear, unlink and free it. */
448 PATTACHINFO paiprev
= NULL
;
450 if (!gpai
) return FALSE
;
454 /* Search list and free if found or return false. */
457 if (pai
->pti2
== ptiTo
&& pai
->pti1
== pti
) break;
462 if (!pai
) return FALSE
;
464 if (paiprev
) paiprev
->paiNext
= pai
->paiNext
;
466 ExFreePoolWithTag(pai
, USERTAG_ATTACHINFO
);
475 * Generates input events from software
487 TRACE("Enter NtUserSendInput\n");
488 UserEnterExclusive();
490 pti
= PsGetCurrentThreadWin32Thread();
498 if (!nInputs
|| !pInput
|| cbSize
!= sizeof(INPUT
))
500 EngSetLastError(ERROR_INVALID_PARAMETER
);
505 * FIXME - check access rights of the window station
506 * e.g. services running in the service window station cannot block input
508 if (!ThreadHasInputAccess(pti
) ||
509 !IntIsActiveDesktop(pti
->rpdesk
))
511 EngSetLastError(ERROR_ACCESS_DENIED
);
520 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
521 if (!NT_SUCCESS(Status
))
523 SetLastNtError(Status
);
527 switch (SafeInput
.type
)
530 if (IntMouseInput(&SafeInput
.mi
, TRUE
))
534 if (UserSendKeyboardInput(&SafeInput
.ki
, TRUE
))
538 FIXME("INPUT_HARDWARE not supported!");
541 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
547 TRACE("Leave NtUserSendInput, ret=%i\n", uRet
);