83d1463305fb1ebe080a3ab91c0d21b6295443f3
[reactos.git] / reactos / win32ss / user / ntuser / input.c
1 /*
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)
8 */
9
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserInput);
12
13 /* GLOBALS *******************************************************************/
14
15 PTHREADINFO ptiRawInput;
16 PTHREADINFO ptiKeyboard;
17 PTHREADINFO ptiMouse;
18 PKTIMER MasterTimer = NULL;
19 PATTACHINFO gpai = NULL;
20 HANDLE ghKeyboardDevice;
21
22 static DWORD LastInputTick = 0;
23 static HANDLE ghMouseDevice;
24
25 /* FUNCTIONS *****************************************************************/
26
27 /*
28 * IntLastInputTick
29 *
30 * Updates or gets last input tick count
31 */
32 static DWORD FASTCALL
33 IntLastInputTick(BOOL bUpdate)
34 {
35 if (bUpdate)
36 {
37 LARGE_INTEGER TickCount;
38 KeQueryTickCount(&TickCount);
39 LastInputTick = MsqCalculateMessageTime(&TickCount);
40 if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
41 }
42 return LastInputTick;
43 }
44
45 /*
46 * DoTheScreenSaver
47 *
48 * Check if scrensaver should be started and sends message to SAS window
49 */
50 VOID FASTCALL
51 DoTheScreenSaver(VOID)
52 {
53 LARGE_INTEGER TickCount;
54 DWORD Test, TO;
55
56 if (gspv.iScrSaverTimeout > 0) // Zero means Off.
57 {
58 KeQueryTickCount(&TickCount);
59 Test = MsqCalculateMessageTime(&TickCount);
60 Test = Test - LastInputTick;
61 TO = 1000 * gspv.iScrSaverTimeout;
62 if (Test > TO)
63 {
64 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test, gspv.iScrSaverTimeout);
65
66 if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
67 {
68 if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER))
69 {
70 ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER;
71 ERR("Screensaver is Idle\n");
72 }
73 }
74 else
75 {
76 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
77 if (ForegroundQueue && ForegroundQueue->spwndActive)
78 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
79 else
80 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
81 }
82 }
83 }
84 }
85
86 /*
87 * OpenInputDevice
88 *
89 * Opens input device for asynchronous access
90 */
91 static
92 NTSTATUS NTAPI
93 OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceName)
94 {
95 UNICODE_STRING DeviceName;
96 OBJECT_ATTRIBUTES ObjectAttributes;
97 NTSTATUS Status;
98 IO_STATUS_BLOCK Iosb;
99
100 RtlInitUnicodeString(&DeviceName, pszDeviceName);
101
102 InitializeObjectAttributes(&ObjectAttributes,
103 &DeviceName,
104 0,
105 NULL,
106 NULL);
107
108 Status = ZwOpenFile(pHandle,
109 FILE_ALL_ACCESS,
110 &ObjectAttributes,
111 &Iosb,
112 0,
113 0);
114 if (NT_SUCCESS(Status) && ppObject)
115 {
116 Status = ObReferenceObjectByHandle(*pHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)ppObject, NULL);
117 ASSERT(NT_SUCCESS(Status));
118 }
119
120 return Status;
121 }
122
123 /*
124 * RawInputThreadMain
125 *
126 * Reads data from input devices and supports win32 timers
127 */
128 VOID NTAPI
129 RawInputThreadMain()
130 {
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;
140
141 ByteOffset.QuadPart = (LONGLONG)0;
142 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
143
144 ptiRawInput = GetW32ThreadInfo();
145 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
146 ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
147
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) &&
237 (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects)))
238 {
239 /* Some device has finished reading */
240 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
241
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)
248 {
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);
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 return STATUS_SUCCESS;
334 }
335
336 BOOL FASTCALL
337 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
338 {
339 PTHREADINFO OldBlock;
340 ASSERT(pti);
341
342 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
343 {
344 /*
345 * Fail blocking if exiting the thread
346 */
347
348 return FALSE;
349 }
350
351 /*
352 * FIXME: Check access rights of the window station
353 * e.g. services running in the service window station cannot block input
354 */
355 if(!ThreadHasInputAccess(pti) ||
356 !IntIsActiveDesktop(pti->rpdesk))
357 {
358 EngSetLastError(ERROR_ACCESS_DENIED);
359 return FALSE;
360 }
361
362 ASSERT(pti->rpdesk);
363 OldBlock = pti->rpdesk->BlockInputThread;
364 if(OldBlock)
365 {
366 if(OldBlock != pti)
367 {
368 EngSetLastError(ERROR_ACCESS_DENIED);
369 return FALSE;
370 }
371 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
372 return OldBlock == NULL;
373 }
374
375 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
376 return OldBlock == NULL;
377 }
378
379 BOOL
380 APIENTRY
381 NtUserBlockInput(
382 BOOL BlockIt)
383 {
384 BOOL ret;
385
386 TRACE("Enter NtUserBlockInput\n");
387 UserEnterExclusive();
388
389 ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
390
391 UserLeave();
392 TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
393
394 return ret;
395 }
396
397 BOOL FASTCALL
398 UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
399 {
400 MSG msg;
401 PATTACHINFO pai;
402
403 /* Can not be the same thread. */
404 if (ptiFrom == ptiTo) return FALSE;
405
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)
410 return FALSE;
411
412 /* If Attach set, allocate and link. */
413 if (fAttach)
414 {
415 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
416 if (!pai) return FALSE;
417
418 pai->paiNext = gpai;
419 pai->pti1 = ptiFrom;
420 pai->pti2 = ptiTo;
421 gpai = pai;
422 TRACE("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p\n",ptiFrom,ptiTo);
423
424 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
425 ptiFrom->pqAttach = ptiFrom->MessageQueue;
426 ptiFrom->MessageQueue = ptiTo->MessageQueue;
427 // FIXME: conditions?
428 if (ptiFrom->pqAttach == gpqForeground)
429 {
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;
437 }
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 == ptiFrom) 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 TRACE("Attach Free! ptiFrom 0x%p ptiTo 0x%p\n",ptiFrom,ptiTo);
461
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;
469 }
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.
472 ATM which one?
473 */
474 RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
475
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);
482
483 return TRUE;
484 }
485
486 /*
487 * NtUserSendInput
488 *
489 * Generates input events from software
490 */
491 UINT
492 APIENTRY
493 NtUserSendInput(
494 UINT nInputs,
495 LPINPUT pInput,
496 INT cbSize)
497 {
498 PTHREADINFO pti;
499 UINT uRet = 0;
500
501 TRACE("Enter NtUserSendInput\n");
502 UserEnterExclusive();
503
504 pti = PsGetCurrentThreadWin32Thread();
505 ASSERT(pti);
506
507 if (!pti->rpdesk)
508 {
509 goto cleanup;
510 }
511
512 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
513 {
514 EngSetLastError(ERROR_INVALID_PARAMETER);
515 goto cleanup;
516 }
517
518 /*
519 * FIXME: Check access rights of the window station
520 * e.g. services running in the service window station cannot block input
521 */
522 if (!ThreadHasInputAccess(pti) ||
523 !IntIsActiveDesktop(pti->rpdesk))
524 {
525 EngSetLastError(ERROR_ACCESS_DENIED);
526 goto cleanup;
527 }
528
529 while (nInputs--)
530 {
531 INPUT SafeInput;
532 NTSTATUS Status;
533
534 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
535 if (!NT_SUCCESS(Status))
536 {
537 SetLastNtError(Status);
538 goto cleanup;
539 }
540
541 switch (SafeInput.type)
542 {
543 case INPUT_MOUSE:
544 if (UserSendMouseInput(&SafeInput.mi, TRUE))
545 uRet++;
546 break;
547 case INPUT_KEYBOARD:
548 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
549 uRet++;
550 break;
551 case INPUT_HARDWARE:
552 FIXME("INPUT_HARDWARE not supported!");
553 break;
554 default:
555 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
556 break;
557 }
558 }
559
560 cleanup:
561 TRACE("Leave NtUserSendInput, ret=%i\n", uRet);
562 UserLeave();
563 return uRet;
564 }
565
566 /* EOF */