e25a94a2a261ad4f1d51e3d6a83d451c25c43413
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / input.c
1 /*
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)
8 */
9
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserInput);
12
13 extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
14 extern PPROCESSINFO ppiScrnSaver;
15
16 /* GLOBALS *******************************************************************/
17
18 PTHREADINFO ptiRawInput;
19 PTHREADINFO ptiKeyboard;
20 PTHREADINFO ptiMouse;
21 PKTIMER MasterTimer = NULL;
22 PATTACHINFO gpai = NULL;
23 HANDLE ghKeyboardDevice;
24
25 static DWORD LastInputTick = 0;
26 static HANDLE ghMouseDevice;
27
28 /* FUNCTIONS *****************************************************************/
29
30 /*
31 * IntLastInputTick
32 *
33 * Updates or gets last input tick count
34 */
35 static DWORD FASTCALL
36 IntLastInputTick(BOOL bUpdate)
37 {
38 if (bUpdate)
39 {
40 LARGE_INTEGER TickCount;
41 KeQueryTickCount(&TickCount);
42 LastInputTick = MsqCalculateMessageTime(&TickCount);
43 if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
44 }
45 return LastInputTick;
46 }
47
48 /*
49 * DoTheScreenSaver
50 *
51 * Check if scrensaver should be started and sends message to SAS window
52 */
53 VOID FASTCALL
54 DoTheScreenSaver(VOID)
55 {
56 LARGE_INTEGER TickCount;
57 DWORD Test, TO;
58
59 if (gspv.iScrSaverTimeout > 0) // Zero means Off.
60 {
61 KeQueryTickCount(&TickCount);
62 Test = MsqCalculateMessageTime(&TickCount);
63 Test = Test - LastInputTick;
64 TO = 1000 * gspv.iScrSaverTimeout;
65 if (Test > TO)
66 {
67 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test, gspv.iScrSaverTimeout);
68
69 if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
70 {
71 if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER))
72 {
73 ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER;
74 ERR("Screensaver is Idle\n");
75 }
76 }
77 else
78 {
79 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
80 if (ForegroundQueue && ForegroundQueue->ActiveWindow)
81 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
82 else
83 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
84 }
85 }
86 }
87 }
88
89 /*
90 * OpenInputDevice
91 *
92 * Opens input device for asynchronous access
93 */
94 static
95 NTAPI NTSTATUS
96 OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceName)
97 {
98 UNICODE_STRING DeviceName;
99 OBJECT_ATTRIBUTES ObjectAttributes;
100 NTSTATUS Status;
101 IO_STATUS_BLOCK Iosb;
102
103 RtlInitUnicodeString(&DeviceName, pszDeviceName);
104
105 InitializeObjectAttributes(&ObjectAttributes,
106 &DeviceName,
107 0,
108 NULL,
109 NULL);
110
111 Status = ZwOpenFile(pHandle,
112 FILE_ALL_ACCESS,
113 &ObjectAttributes,
114 &Iosb,
115 0,
116 0);
117 if (NT_SUCCESS(Status) && ppObject)
118 {
119 Status = ObReferenceObjectByHandle(*pHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)ppObject, NULL);
120 ASSERT(NT_SUCCESS(Status));
121 }
122
123 return Status;
124 }
125
126 /*
127 * RawInputThreadMain
128 *
129 * Reads data from input devices and supports win32 timers
130 */
131 VOID NTAPI
132 RawInputThreadMain()
133 {
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;
142
143 ByteOffset.QuadPart = (LONGLONG)0;
144 WaitTimeout.QuadPart = (LONGLONG)(-10000000);
145
146 /*do
147 {
148 KEVENT Event;
149 KeInitializeEvent(&Event, NotificationEvent, FALSE);
150 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &WaitTimeout);
151 } while (!NT_SUCCESS(Status));*/
152
153 ptiRawInput = PsGetCurrentThreadWin32Thread();
154 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
155 TRACE("Raw Input Thread 0x%x\n", ptiRawInput);
156
157 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
158 LOW_REALTIME_PRIORITY + 3);
159
160 UserEnterExclusive();
161 StartTheTimers();
162 UserLeave();
163
164 for(;;)
165 {
166 if (!ghMouseDevice)
167 {
168 /* Check if mouse device already exists */
169 Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" );
170 if (NT_SUCCESS(Status))
171 {
172 ++cMaxWaitObjects;
173 TRACE("Mouse connected!\n");
174 }
175 }
176 if (!ghKeyboardDevice)
177 {
178 /* Check if keyboard device already exists */
179 Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0");
180 if (NT_SUCCESS(Status))
181 {
182 ++cMaxWaitObjects;
183 TRACE("Keyboard connected!\n");
184 }
185 }
186
187 /* Reset WaitHandles array */
188 cWaitObjects = 0;
189 WaitObjects[cWaitObjects++] = MasterTimer;
190
191 if (ghMouseDevice)
192 {
193 /* Try to read from mouse if previous reading is not pending */
194 if (MouStatus != STATUS_PENDING)
195 {
196 MouStatus = ZwReadFile(ghMouseDevice,
197 NULL,
198 NULL,
199 NULL,
200 &MouIosb,
201 &MouseInput,
202 sizeof(MOUSE_INPUT_DATA),
203 &ByteOffset,
204 NULL);
205 }
206
207 if (MouStatus == STATUS_PENDING)
208 WaitObjects[cWaitObjects++] = &pMouDevice->Event;
209 }
210
211 if (ghKeyboardDevice)
212 {
213 /* Try to read from keyboard if previous reading is not pending */
214 if (KbdStatus != STATUS_PENDING)
215 {
216 KbdStatus = ZwReadFile(ghKeyboardDevice,
217 NULL,
218 NULL,
219 NULL,
220 &KbdIosb,
221 &KeyInput,
222 sizeof(KEYBOARD_INPUT_DATA),
223 &ByteOffset,
224 NULL);
225
226 }
227 if (KbdStatus == STATUS_PENDING)
228 WaitObjects[cWaitObjects++] = &pKbdDevice->Event;
229 }
230
231 /* If all objects are pending, wait for them */
232 if (cWaitObjects == cMaxWaitObjects)
233 {
234 Status = KeWaitForMultipleObjects(cWaitObjects,
235 WaitObjects,
236 WaitAny,
237 UserRequest,
238 KernelMode,
239 TRUE,
240 NULL,//&WaitTimeout,
241 NULL);
242
243 if (Status >= STATUS_WAIT_0 && Status < STATUS_WAIT_0 + cWaitObjects)
244 {
245 /* Some device has finished reading */
246 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
247
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)
254 {
255 /* FIXME: where it should go? */
256 ProcessTimers();
257 }
258 else ASSERT(FALSE);
259 }
260 }
261
262 /* Have we successed reading from mouse? */
263 if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING)
264 {
265 TRACE("MouseEvent\n");
266
267 /* Set LastInputTick */
268 IntLastInputTick(TRUE);
269
270 /* Process data */
271 UserEnterExclusive();
272 UserProcessMouseInput(&MouseInput, MouIosb.Information / sizeof(MOUSE_INPUT_DATA));
273 UserLeave();
274 }
275 else if (MouStatus != STATUS_PENDING)
276 ERR("Failed to read from mouse: %x.\n", MouStatus);
277
278 /* Have we successed reading from keyboard? */
279 if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING)
280 {
281 TRACE("KeyboardEvent: %s %04x\n",
282 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
283 KeyInput.MakeCode);
284
285 /* Set LastInputTick */
286 IntLastInputTick(TRUE);
287
288 /* Process data */
289 UserEnterExclusive();
290 UserProcessKeyboardInput(&KeyInput);
291 UserLeave();
292 }
293 else if (KbdStatus != STATUS_PENDING)
294 ERR("Failed to read from keyboard: %x.\n", KbdStatus);
295 }
296 ERR("Raw Input Thread Exit!\n");
297 }
298
299 /*
300 * CreateSystemThreads
301 *
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
304 */
305 DWORD NTAPI
306 CreateSystemThreads(UINT Type)
307 {
308 UserLeave();
309
310 switch (Type)
311 {
312 case 0: RawInputThreadMain(); break;
313 default: ERR("Wrong type: %x\n", Type);
314 }
315
316 UserEnterShared();
317
318 return 0;
319 }
320
321 /*
322 * InitInputImpl
323 *
324 * Inits input implementation
325 */
326 INIT_FUNCTION
327 NTSTATUS
328 NTAPI
329 InitInputImpl(VOID)
330 {
331 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
332 if (!MasterTimer)
333 {
334 ERR("Failed to allocate memory\n");
335 ASSERT(FALSE);
336 return STATUS_UNSUCCESSFUL;
337 }
338 KeInitializeTimer(MasterTimer);
339
340 /* Initialize the default keyboard layout */
341 if (!UserInitDefaultKeyboardLayout())
342 {
343 ERR("Failed to initialize default keyboard layout!\n");
344 }
345
346 return STATUS_SUCCESS;
347 }
348
349 /*
350 * CleanupInputImp
351 *
352 * Cleans input implementation
353 */
354 NTSTATUS FASTCALL
355 CleanupInputImp(VOID)
356 {
357 return STATUS_SUCCESS;
358 }
359
360 BOOL FASTCALL
361 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
362 {
363 PTHREADINFO OldBlock;
364 ASSERT(pti);
365
366 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
367 {
368 /*
369 * fail blocking if exiting the thread
370 */
371
372 return FALSE;
373 }
374
375 /*
376 * FIXME - check access rights of the window station
377 * e.g. services running in the service window station cannot block input
378 */
379 if(!ThreadHasInputAccess(pti) ||
380 !IntIsActiveDesktop(pti->rpdesk))
381 {
382 EngSetLastError(ERROR_ACCESS_DENIED);
383 return FALSE;
384 }
385
386 ASSERT(pti->rpdesk);
387 OldBlock = pti->rpdesk->BlockInputThread;
388 if(OldBlock)
389 {
390 if(OldBlock != pti)
391 {
392 EngSetLastError(ERROR_ACCESS_DENIED);
393 return FALSE;
394 }
395 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
396 return OldBlock == NULL;
397 }
398
399 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
400 return OldBlock == NULL;
401 }
402
403 BOOL
404 APIENTRY
405 NtUserBlockInput(
406 BOOL BlockIt)
407 {
408 BOOL ret;
409
410 TRACE("Enter NtUserBlockInput\n");
411 UserEnterExclusive();
412
413 ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
414
415 UserLeave();
416 TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
417
418 return ret;
419 }
420
421 BOOL FASTCALL
422 UserAttachThreadInput(PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
423 {
424 PATTACHINFO pai;
425
426 /* Can not be the same thread.*/
427 if (pti == ptiTo) return FALSE;
428
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)
433 return FALSE;
434
435 /* If Attach set, allocate and link. */
436 if (fAttach)
437 {
438 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
439 if (!pai) return FALSE;
440
441 pai->paiNext = gpai;
442 pai->pti1 = pti;
443 pai->pti2 = ptiTo;
444 gpai = pai;
445 }
446 else /* If clear, unlink and free it. */
447 {
448 PATTACHINFO paiprev = NULL;
449
450 if (!gpai) return FALSE;
451
452 pai = gpai;
453
454 /* Search list and free if found or return false. */
455 do
456 {
457 if (pai->pti2 == ptiTo && pai->pti1 == pti) break;
458 paiprev = pai;
459 pai = pai->paiNext;
460 } while (pai);
461
462 if (!pai) return FALSE;
463
464 if (paiprev) paiprev->paiNext = pai->paiNext;
465
466 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
467 }
468
469 return TRUE;
470 }
471
472 /*
473 * NtUserSendInput
474 *
475 * Generates input events from software
476 */
477 UINT
478 APIENTRY
479 NtUserSendInput(
480 UINT nInputs,
481 LPINPUT pInput,
482 INT cbSize)
483 {
484 PTHREADINFO pti;
485 UINT uRet = 0;
486
487 TRACE("Enter NtUserSendInput\n");
488 UserEnterExclusive();
489
490 pti = PsGetCurrentThreadWin32Thread();
491 ASSERT(pti);
492
493 if (!pti->rpdesk)
494 {
495 goto cleanup;
496 }
497
498 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
499 {
500 EngSetLastError(ERROR_INVALID_PARAMETER);
501 goto cleanup;
502 }
503
504 /*
505 * FIXME - check access rights of the window station
506 * e.g. services running in the service window station cannot block input
507 */
508 if (!ThreadHasInputAccess(pti) ||
509 !IntIsActiveDesktop(pti->rpdesk))
510 {
511 EngSetLastError(ERROR_ACCESS_DENIED);
512 goto cleanup;
513 }
514
515 while (nInputs--)
516 {
517 INPUT SafeInput;
518 NTSTATUS Status;
519
520 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
521 if (!NT_SUCCESS(Status))
522 {
523 SetLastNtError(Status);
524 goto cleanup;
525 }
526
527 switch (SafeInput.type)
528 {
529 case INPUT_MOUSE:
530 if (IntMouseInput(&SafeInput.mi, TRUE))
531 uRet++;
532 break;
533 case INPUT_KEYBOARD:
534 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
535 uRet++;
536 break;
537 case INPUT_HARDWARE:
538 FIXME("INPUT_HARDWARE not supported!");
539 break;
540 default:
541 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
542 break;
543 }
544 }
545
546 cleanup:
547 TRACE("Leave NtUserSendInput, ret=%i\n", uRet);
548 UserLeave();
549 return uRet;
550 }
551
552 /* EOF */