- Fix MSVC build based on ThFabba suggestion
[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 NTSTATUS NTAPI
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 ptiRawInput = PsGetCurrentThreadWin32Thread();
147 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
148 TRACE("Raw Input Thread 0x%x\n", ptiRawInput);
149
150 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
151 LOW_REALTIME_PRIORITY + 3);
152
153 UserEnterExclusive();
154 StartTheTimers();
155 UserLeave();
156
157 for(;;)
158 {
159 if (!ghMouseDevice)
160 {
161 /* Check if mouse device already exists */
162 Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" );
163 if (NT_SUCCESS(Status))
164 {
165 ++cMaxWaitObjects;
166 TRACE("Mouse connected!\n");
167 }
168 }
169 if (!ghKeyboardDevice)
170 {
171 /* Check if keyboard device already exists */
172 Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0");
173 if (NT_SUCCESS(Status))
174 {
175 ++cMaxWaitObjects;
176 TRACE("Keyboard connected!\n");
177 }
178 }
179
180 /* Reset WaitHandles array */
181 cWaitObjects = 0;
182 WaitObjects[cWaitObjects++] = MasterTimer;
183
184 if (ghMouseDevice)
185 {
186 /* Try to read from mouse if previous reading is not pending */
187 if (MouStatus != STATUS_PENDING)
188 {
189 MouStatus = ZwReadFile(ghMouseDevice,
190 NULL,
191 NULL,
192 NULL,
193 &MouIosb,
194 &MouseInput,
195 sizeof(MOUSE_INPUT_DATA),
196 &ByteOffset,
197 NULL);
198 }
199
200 if (MouStatus == STATUS_PENDING)
201 WaitObjects[cWaitObjects++] = &pMouDevice->Event;
202 }
203
204 if (ghKeyboardDevice)
205 {
206 /* Try to read from keyboard if previous reading is not pending */
207 if (KbdStatus != STATUS_PENDING)
208 {
209 KbdStatus = ZwReadFile(ghKeyboardDevice,
210 NULL,
211 NULL,
212 NULL,
213 &KbdIosb,
214 &KeyInput,
215 sizeof(KEYBOARD_INPUT_DATA),
216 &ByteOffset,
217 NULL);
218
219 }
220 if (KbdStatus == STATUS_PENDING)
221 WaitObjects[cWaitObjects++] = &pKbdDevice->Event;
222 }
223
224 /* If all objects are pending, wait for them */
225 if (cWaitObjects == cMaxWaitObjects)
226 {
227 Status = KeWaitForMultipleObjects(cWaitObjects,
228 WaitObjects,
229 WaitAny,
230 UserRequest,
231 KernelMode,
232 TRUE,
233 NULL,//&WaitTimeout,
234 NULL);
235
236 if (Status >= STATUS_WAIT_0 && Status < STATUS_WAIT_0 + cWaitObjects)
237 {
238 /* Some device has finished reading */
239 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
240
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)
247 {
248 /* FIXME: where it should go? */
249 ProcessTimers();
250 }
251 else ASSERT(FALSE);
252 }
253 }
254
255 /* Have we successed reading from mouse? */
256 if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING)
257 {
258 TRACE("MouseEvent\n");
259
260 /* Set LastInputTick */
261 IntLastInputTick(TRUE);
262
263 /* Process data */
264 UserEnterExclusive();
265 UserProcessMouseInput(&MouseInput, MouIosb.Information / sizeof(MOUSE_INPUT_DATA));
266 UserLeave();
267 }
268 else if (MouStatus != STATUS_PENDING)
269 ERR("Failed to read from mouse: %x.\n", MouStatus);
270
271 /* Have we successed reading from keyboard? */
272 if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING)
273 {
274 TRACE("KeyboardEvent: %s %04x\n",
275 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
276 KeyInput.MakeCode);
277
278 /* Set LastInputTick */
279 IntLastInputTick(TRUE);
280
281 /* Process data */
282 UserEnterExclusive();
283 UserProcessKeyboardInput(&KeyInput);
284 UserLeave();
285 }
286 else if (KbdStatus != STATUS_PENDING)
287 ERR("Failed to read from keyboard: %x.\n", KbdStatus);
288 }
289 ERR("Raw Input Thread Exit!\n");
290 }
291
292 /*
293 * CreateSystemThreads
294 *
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
297 */
298 DWORD NTAPI
299 CreateSystemThreads(UINT Type)
300 {
301 UserLeave();
302
303 switch (Type)
304 {
305 case 0: RawInputThreadMain(); break;
306 default: ERR("Wrong type: %x\n", Type);
307 }
308
309 UserEnterShared();
310
311 return 0;
312 }
313
314 /*
315 * InitInputImpl
316 *
317 * Inits input implementation
318 */
319 INIT_FUNCTION
320 NTSTATUS
321 NTAPI
322 InitInputImpl(VOID)
323 {
324 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
325 if (!MasterTimer)
326 {
327 ERR("Failed to allocate memory\n");
328 ASSERT(FALSE);
329 return STATUS_UNSUCCESSFUL;
330 }
331 KeInitializeTimer(MasterTimer);
332
333 /* Initialize the default keyboard layout */
334 if (!UserInitDefaultKeyboardLayout())
335 {
336 ERR("Failed to initialize default keyboard layout!\n");
337 }
338
339 return STATUS_SUCCESS;
340 }
341
342 /*
343 * CleanupInputImp
344 *
345 * Cleans input implementation
346 */
347 NTSTATUS FASTCALL
348 CleanupInputImp(VOID)
349 {
350 return STATUS_SUCCESS;
351 }
352
353 BOOL FASTCALL
354 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
355 {
356 PTHREADINFO OldBlock;
357 ASSERT(pti);
358
359 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
360 {
361 /*
362 * fail blocking if exiting the thread
363 */
364
365 return FALSE;
366 }
367
368 /*
369 * FIXME - check access rights of the window station
370 * e.g. services running in the service window station cannot block input
371 */
372 if(!ThreadHasInputAccess(pti) ||
373 !IntIsActiveDesktop(pti->rpdesk))
374 {
375 EngSetLastError(ERROR_ACCESS_DENIED);
376 return FALSE;
377 }
378
379 ASSERT(pti->rpdesk);
380 OldBlock = pti->rpdesk->BlockInputThread;
381 if(OldBlock)
382 {
383 if(OldBlock != pti)
384 {
385 EngSetLastError(ERROR_ACCESS_DENIED);
386 return FALSE;
387 }
388 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
389 return OldBlock == NULL;
390 }
391
392 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
393 return OldBlock == NULL;
394 }
395
396 BOOL
397 APIENTRY
398 NtUserBlockInput(
399 BOOL BlockIt)
400 {
401 BOOL ret;
402
403 TRACE("Enter NtUserBlockInput\n");
404 UserEnterExclusive();
405
406 ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
407
408 UserLeave();
409 TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
410
411 return ret;
412 }
413
414 BOOL FASTCALL
415 UserAttachThreadInput(PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
416 {
417 PATTACHINFO pai;
418
419 /* Can not be the same thread.*/
420 if (pti == ptiTo) return FALSE;
421
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)
426 return FALSE;
427
428 /* If Attach set, allocate and link. */
429 if (fAttach)
430 {
431 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
432 if (!pai) return FALSE;
433
434 pai->paiNext = gpai;
435 pai->pti1 = pti;
436 pai->pti2 = ptiTo;
437 gpai = pai;
438 }
439 else /* If clear, unlink and free it. */
440 {
441 PATTACHINFO paiprev = NULL;
442
443 if (!gpai) return FALSE;
444
445 pai = gpai;
446
447 /* Search list and free if found or return false. */
448 do
449 {
450 if (pai->pti2 == ptiTo && pai->pti1 == pti) break;
451 paiprev = pai;
452 pai = pai->paiNext;
453 } while (pai);
454
455 if (!pai) return FALSE;
456
457 if (paiprev) paiprev->paiNext = pai->paiNext;
458
459 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
460 }
461
462 return TRUE;
463 }
464
465 /*
466 * NtUserSendInput
467 *
468 * Generates input events from software
469 */
470 UINT
471 APIENTRY
472 NtUserSendInput(
473 UINT nInputs,
474 LPINPUT pInput,
475 INT cbSize)
476 {
477 PTHREADINFO pti;
478 UINT uRet = 0;
479
480 TRACE("Enter NtUserSendInput\n");
481 UserEnterExclusive();
482
483 pti = PsGetCurrentThreadWin32Thread();
484 ASSERT(pti);
485
486 if (!pti->rpdesk)
487 {
488 goto cleanup;
489 }
490
491 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
492 {
493 EngSetLastError(ERROR_INVALID_PARAMETER);
494 goto cleanup;
495 }
496
497 /*
498 * FIXME - check access rights of the window station
499 * e.g. services running in the service window station cannot block input
500 */
501 if (!ThreadHasInputAccess(pti) ||
502 !IntIsActiveDesktop(pti->rpdesk))
503 {
504 EngSetLastError(ERROR_ACCESS_DENIED);
505 goto cleanup;
506 }
507
508 while (nInputs--)
509 {
510 INPUT SafeInput;
511 NTSTATUS Status;
512
513 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
514 if (!NT_SUCCESS(Status))
515 {
516 SetLastNtError(Status);
517 goto cleanup;
518 }
519
520 switch (SafeInput.type)
521 {
522 case INPUT_MOUSE:
523 if (IntMouseInput(&SafeInput.mi, TRUE))
524 uRet++;
525 break;
526 case INPUT_KEYBOARD:
527 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
528 uRet++;
529 break;
530 case INPUT_HARDWARE:
531 FIXME("INPUT_HARDWARE not supported!");
532 break;
533 default:
534 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
535 break;
536 }
537 }
538
539 cleanup:
540 TRACE("Leave NtUserSendInput, ret=%i\n", uRet);
541 UserLeave();
542 return uRet;
543 }
544
545 /* EOF */