2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window classes
24 * FILE: subsys/win32k/ntuser/class.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
27 * 06-06-2001 CSH Created
30 /* INCLUDES ******************************************************************/
33 #include <rosrtl/string.h>
35 /* GLOBALS *******************************************************************/
37 static HANDLE MouseDeviceHandle
;
38 static HANDLE MouseThreadHandle
;
39 static CLIENT_ID MouseThreadId
;
40 static HANDLE KeyboardThreadHandle
;
41 static CLIENT_ID KeyboardThreadId
;
42 static HANDLE KeyboardDeviceHandle
;
43 static KEVENT InputThreadsStart
;
44 static BOOLEAN InputThreadsRunning
= FALSE
;
45 PUSER_MESSAGE_QUEUE pmPrimitiveMessageQueue
= 0;
47 /* FUNCTIONS *****************************************************************/
49 #define ClearMouseInput(mi) \
55 #define SendMouseEvent(mi) \
56 if(mi.dx != 0 || mi.dy != 0) \
57 mi.dwFlags |= MOUSEEVENTF_MOVE; \
63 ProcessMouseInputData(PMOUSE_INPUT_DATA Data
, ULONG InputCount
)
65 PMOUSE_INPUT_DATA mid
;
72 for(i
= 0; i
< InputCount
; i
++)
80 if(mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_DOWN
)
82 mi
.dwFlags
|= MOUSEEVENTF_LEFTDOWN
;
85 if(mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_UP
)
87 mi
.dwFlags
|= MOUSEEVENTF_LEFTUP
;
90 if(mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_DOWN
)
92 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEDOWN
;
95 if(mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_UP
)
97 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEUP
;
100 if(mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_DOWN
)
102 mi
.dwFlags
|= MOUSEEVENTF_RIGHTDOWN
;
105 if(mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_UP
)
107 mi
.dwFlags
|= MOUSEEVENTF_RIGHTUP
;
110 if(mid
->ButtonFlags
& MOUSE_BUTTON_4_DOWN
)
112 mi
.mouseData
|= XBUTTON1
;
113 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
116 if(mid
->ButtonFlags
& MOUSE_BUTTON_4_UP
)
118 mi
.mouseData
|= XBUTTON1
;
119 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
122 if(mid
->ButtonFlags
& MOUSE_BUTTON_5_DOWN
)
124 mi
.mouseData
|= XBUTTON2
;
125 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
128 if(mid
->ButtonFlags
& MOUSE_BUTTON_5_UP
)
130 mi
.mouseData
|= XBUTTON2
;
131 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
134 if(mid
->ButtonFlags
& MOUSE_WHEEL
)
136 mi
.mouseData
= mid
->ButtonData
;
137 mi
.dwFlags
|= MOUSEEVENTF_WHEEL
;
147 MouseThreadMain(PVOID StartContext
)
149 UNICODE_STRING MouseDeviceName
;
150 OBJECT_ATTRIBUTES MouseObjectAttributes
;
151 IO_STATUS_BLOCK Iosb
;
154 RtlRosInitUnicodeStringFromLiteral(&MouseDeviceName
, L
"\\??\\Mouse"); /* FIXME - does win use the same? */
155 InitializeObjectAttributes(&MouseObjectAttributes
,
160 Status
= NtOpenFile(&MouseDeviceHandle
,
162 &MouseObjectAttributes
,
165 FILE_SYNCHRONOUS_IO_ALERT
);
166 if(!NT_SUCCESS(Status
))
168 DPRINT1("Win32K: Failed to open mouse.\n");
175 * Wait to start input.
177 DPRINT("Mouse Input Thread Waiting for start event\n");
178 Status
= KeWaitForSingleObject(&InputThreadsStart
,
183 DPRINT("Mouse Input Thread Starting...\n");
186 * Receive and process keyboard input.
188 while(InputThreadsRunning
)
190 MOUSE_INPUT_DATA MouseInput
;
191 Status
= NtReadFile(MouseDeviceHandle
,
197 sizeof(MOUSE_INPUT_DATA
),
200 if(Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
204 if(Status
== STATUS_PENDING
)
206 NtWaitForSingleObject(MouseDeviceHandle
, FALSE
, NULL
);
207 Status
= Iosb
.Status
;
209 if(!NT_SUCCESS(Status
))
211 DPRINT1("Win32K: Failed to read from mouse.\n");
214 DPRINT("MouseEvent\n");
216 ProcessMouseInputData(&MouseInput
, Iosb
.Information
/ sizeof(MOUSE_INPUT_DATA
));
218 DPRINT("Mouse Input Thread Stopped...\n");
223 KeyboardThreadMain(PVOID StartContext
)
225 UNICODE_STRING KeyboardDeviceName
;
226 OBJECT_ATTRIBUTES KeyboardObjectAttributes
;
227 IO_STATUS_BLOCK Iosb
;
230 PUSER_MESSAGE_QUEUE FocusQueue
;
231 struct _ETHREAD
*FocusThread
;
233 RtlRosInitUnicodeStringFromLiteral(&KeyboardDeviceName
, L
"\\??\\Keyboard");
234 InitializeObjectAttributes(&KeyboardObjectAttributes
,
239 Status
= NtOpenFile(&KeyboardDeviceHandle
,
241 &KeyboardObjectAttributes
,
244 FILE_SYNCHRONOUS_IO_ALERT
);
245 if (!NT_SUCCESS(Status
))
247 DPRINT1("Win32K: Failed to open keyboard.\n");
254 * Wait to start input.
256 DPRINT( "Keyboard Input Thread Waiting for start event\n" );
257 Status
= KeWaitForSingleObject(&InputThreadsStart
,
262 DPRINT( "Keyboard Input Thread Starting...\n" );
265 * Receive and process keyboard input.
267 while (InputThreadsRunning
)
269 KEY_EVENT_RECORD KeyEvent
;
272 struct _ETHREAD
*Thread
;
276 Status
= NtReadFile (KeyboardDeviceHandle
,
282 sizeof(KEY_EVENT_RECORD
),
285 DPRINT( "KeyRaw: %s %04x\n",
286 KeyEvent
.bKeyDown
? "down" : "up",
287 KeyEvent
.wVirtualScanCode
);
289 if (Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
293 if (!NT_SUCCESS(Status
))
295 DPRINT1("Win32K: Failed to read from keyboard.\n");
299 DPRINT( "Key: %s\n", KeyEvent
.bKeyDown
? "down" : "up" );
302 if (KeyEvent
.dwControlKeyState
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
))
303 fsModifiers
|= MOD_ALT
;
305 if (KeyEvent
.dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
))
306 fsModifiers
|= MOD_CONTROL
;
308 if (KeyEvent
.dwControlKeyState
& SHIFT_PRESSED
)
309 fsModifiers
|= MOD_SHIFT
;
311 /* FIXME: Support MOD_WIN */
313 lParam
= KeyEvent
.wRepeatCount
|
314 ((KeyEvent
.wVirtualScanCode
<< 16) & 0x00FF0000) | 0x40000000;
316 /* Bit 24 indicates if this is an extended key */
317 if (KeyEvent
.dwControlKeyState
& ENHANCED_KEY
)
322 if (fsModifiers
& MOD_ALT
)
324 /* Context mode. 1 if ALT if pressed while the key is pressed */
328 if (! KeyEvent
.bKeyDown
)
330 /* Transition state. 1 for KEY_UP etc, 0 for KEY_DOWN */
334 if (GetHotKey(InputWindowStation
,
336 KeyEvent
.wVirtualKeyCode
,
341 if (KeyEvent
.bKeyDown
)
343 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd
, id
);
344 MsqPostHotKeyMessage (Thread
,
347 MAKELPARAM((WORD
)fsModifiers
,
353 /* Find the target thread whose locale is in effect */
354 if (!IntGetScreenDC())
356 FocusQueue
= W32kGetPrimitiveMessageQueue();
360 FocusQueue
= IntGetFocusMessageQueue();
363 if (!FocusQueue
) continue;
365 if(KeyEvent
.bKeyDown
&& (fsModifiers
& MOD_ALT
))
366 msg
.message
= WM_SYSKEYDOWN
;
367 else if(KeyEvent
.bKeyDown
)
368 msg
.message
= WM_KEYDOWN
;
369 else if(fsModifiers
& MOD_ALT
)
370 msg
.message
= WM_SYSKEYUP
;
372 msg
.message
= WM_KEYUP
;
374 msg
.wParam
= KeyEvent
.wVirtualKeyCode
;
376 msg
.hwnd
= FocusQueue
->FocusWindow
;
378 FocusThread
= FocusQueue
->Thread
;
380 if (FocusThread
&& FocusThread
->Tcb
.Win32Thread
&&
381 FocusThread
->Tcb
.Win32Thread
->KeyboardLayout
)
383 W32kKeyProcessMessage(&msg
,
384 FocusThread
->Tcb
.Win32Thread
->KeyboardLayout
);
390 * Post a keyboard message.
392 MsqPostKeyboardMessage(msg
.message
,msg
.wParam
,msg
.lParam
);
394 DPRINT( "KeyboardInput Thread Stopped...\n" );
400 NtUserAcquireOrReleaseInputOwnership(BOOLEAN Release
)
402 if (Release
&& InputThreadsRunning
&& !pmPrimitiveMessageQueue
)
404 DPRINT( "Releasing input: PM = %08x\n", pmPrimitiveMessageQueue
);
405 KeClearEvent(&InputThreadsStart
);
406 InputThreadsRunning
= FALSE
;
408 NtAlertThread(KeyboardThreadHandle
);
410 else if (!Release
&& !InputThreadsRunning
)
412 InputThreadsRunning
= TRUE
;
413 KeSetEvent(&InputThreadsStart
, IO_NO_INCREMENT
, FALSE
);
416 return(STATUS_SUCCESS
);
424 KeInitializeEvent(&InputThreadsStart
, NotificationEvent
, FALSE
);
426 Status
= PsCreateSystemThread(&KeyboardThreadHandle
,
433 if (!NT_SUCCESS(Status
))
435 DPRINT1("Win32K: Failed to create keyboard thread.\n");
438 /* Initialize the default keyboard layout */
439 (VOID
)W32kGetDefaultKeyLayout();
442 Status
= PsCreateSystemThread(&MouseThreadHandle
,
449 if (!NT_SUCCESS(Status
))
451 DPRINT1("Win32K: Failed to create mouse thread.\n");
454 return STATUS_SUCCESS
;
458 CleanupInputImp(VOID
)
460 return(STATUS_SUCCESS
);
475 IntBlockInput(PW32THREAD W32Thread
, BOOL BlockIt
)
480 if(!W32Thread
->Desktop
|| (W32Thread
->IsExiting
&& BlockIt
))
483 * fail blocking if exiting the thread
490 * FIXME - check access rights of the window station
491 * e.g. services running in the service window station cannot block input
493 if(!ThreadHasInputAccess(W32Thread
) ||
494 !IntIsActiveDesktop(W32Thread
->Desktop
))
496 SetLastWin32Error(ERROR_ACCESS_DENIED
);
500 ASSERT(W32Thread
->Desktop
);
501 OldBlock
= W32Thread
->Desktop
->BlockInputThread
;
504 if(OldBlock
!= W32Thread
)
506 SetLastWin32Error(ERROR_ACCESS_DENIED
);
509 W32Thread
->Desktop
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
510 return OldBlock
== NULL
;
513 W32Thread
->Desktop
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
514 return OldBlock
== NULL
;
522 return IntBlockInput(PsGetWin32Thread(), BlockIt
);
526 IntSwapMouseButton(PWINSTATION_OBJECT WinStaObject
, BOOL Swap
)
528 PSYSTEM_CURSORINFO CurInfo
;
531 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
532 res
= CurInfo
->SwapButtons
;
533 CurInfo
->SwapButtons
= Swap
;
538 IntMouseInput(MOUSEINPUT
*mi
)
540 const UINT SwapBtnMsg
[2][2] = {{WM_LBUTTONDOWN
, WM_RBUTTONDOWN
},
541 {WM_LBUTTONUP
, WM_RBUTTONUP
}};
542 const WPARAM SwapBtn
[2] = {MK_LBUTTON
, MK_RBUTTON
};
543 POINT MousePos
, OrgPos
;
544 PSYSTEM_CURSORINFO CurInfo
;
545 PWINSTATION_OBJECT WinSta
;
546 BOOL DoMove
, SwapButtons
;
549 BITMAPOBJ
*BitmapObj
;
552 PWINDOW_OBJECT DesktopWindow
;
558 /* FIXME - get the screen dc from the window station or desktop */
559 if(!(hDC
= IntGetScreenDC()))
567 WinSta
= PsGetWin32Process()->WindowStation
;
569 /* FIXME - ugly hack but as long as we're using this dumb callback from the
570 mouse class driver, we can't access the window station from the calling
572 WinSta
= InputWindowStation
;
576 CurInfo
= IntGetSysCursorInfo(WinSta
);
580 LARGE_INTEGER LargeTickCount
;
581 KeQueryTickCount(&LargeTickCount
);
582 mi
->time
= LargeTickCount
.u
.LowPart
;
585 SwapButtons
= CurInfo
->SwapButtons
;
588 ExAcquireFastMutex(&CurInfo
->CursorMutex
);
589 IntGetCursorLocation(WinSta
, &MousePos
);
590 OrgPos
.x
= MousePos
.x
;
591 OrgPos
.y
= MousePos
.y
;
593 if(mi
->dwFlags
& MOUSEEVENTF_MOVE
)
595 if(mi
->dwFlags
& MOUSEEVENTF_ABSOLUTE
)
602 MousePos
.x
+= mi
->dx
;
603 MousePos
.y
+= mi
->dy
;
606 Status
= ObmReferenceObjectByHandle(WinSta
->HandleTable
,
607 WinSta
->ActiveDesktop
->DesktopWindow
, otWindow
, (PVOID
*)&DesktopWindow
);
608 if (NT_SUCCESS(Status
))
610 if(MousePos
.x
>= DesktopWindow
->ClientRect
.right
)
611 MousePos
.x
= DesktopWindow
->ClientRect
.right
- 1;
612 if(MousePos
.y
>= DesktopWindow
->ClientRect
.bottom
)
613 MousePos
.y
= DesktopWindow
->ClientRect
.bottom
- 1;
615 ObmDereferenceObject(DesktopWindow
);
622 if(CurInfo
->CursorClipInfo
.IsClipped
)
624 /* The mouse cursor needs to be clipped */
626 if(MousePos
.x
>= (LONG
)CurInfo
->CursorClipInfo
.Right
)
627 MousePos
.x
= (LONG
)CurInfo
->CursorClipInfo
.Right
;
628 if(MousePos
.x
< (LONG
)CurInfo
->CursorClipInfo
.Left
)
629 MousePos
.x
= (LONG
)CurInfo
->CursorClipInfo
.Left
;
630 if(MousePos
.y
>= (LONG
)CurInfo
->CursorClipInfo
.Bottom
)
631 MousePos
.y
= (LONG
)CurInfo
->CursorClipInfo
.Bottom
;
632 if(MousePos
.y
< (LONG
)CurInfo
->CursorClipInfo
.Top
)
633 MousePos
.y
= (LONG
)CurInfo
->CursorClipInfo
.Top
;
636 DoMove
= (MousePos
.x
!= OrgPos
.x
|| MousePos
.y
!= OrgPos
.y
);
639 ExReleaseFastMutex(&CurInfo
->CursorMutex
);
646 hBitmap
= dc
->w
.hBitmap
;
649 BitmapObj
= BITMAPOBJ_LockBitmap(hBitmap
);
652 SurfObj
= &BitmapObj
->SurfObj
;
654 if (GDIDEV(SurfObj
)->Pointer
.MovePointer
)
656 GDIDEV(SurfObj
)->Pointer
.MovePointer(SurfObj
, MousePos
.x
, MousePos
.y
, &(GDIDEV(SurfObj
)->Pointer
.Exclude
));
658 EngMovePointer(SurfObj
, MousePos
.x
, MousePos
.y
, &(GDIDEV(SurfObj
)->Pointer
.Exclude
));
660 /* Only now, update the info in the GDIDEVICE, so EngMovePointer can
661 * use the old values to move the pointer image */
662 GDIDEV(SurfObj
)->Pointer
.Pos
.x
= MousePos
.x
;
663 GDIDEV(SurfObj
)->Pointer
.Pos
.y
= MousePos
.y
;
665 BITMAPOBJ_UnlockBitmap(hBitmap
);
671 * Insert the messages into the system queue
674 Msg
.wParam
= CurInfo
->ButtonsDown
;
675 Msg
.lParam
= MAKELPARAM(MousePos
.x
, MousePos
.y
);
679 Msg
.message
= WM_MOUSEMOVE
;
680 MsqInsertSystemMessage(&Msg
);
684 if(mi
->dwFlags
& MOUSEEVENTF_LEFTDOWN
)
686 Msg
.message
= SwapBtnMsg
[0][SwapButtons
];
687 CurInfo
->ButtonsDown
|= SwapBtn
[SwapButtons
];
688 MsqInsertSystemMessage(&Msg
);
690 else if(mi
->dwFlags
& MOUSEEVENTF_LEFTUP
)
692 Msg
.message
= SwapBtnMsg
[1][SwapButtons
];
693 CurInfo
->ButtonsDown
&= ~SwapBtn
[SwapButtons
];
694 MsqInsertSystemMessage(&Msg
);
696 if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEDOWN
)
698 Msg
.message
= WM_MBUTTONDOWN
;
699 CurInfo
->ButtonsDown
|= MK_MBUTTON
;
700 MsqInsertSystemMessage(&Msg
);
702 else if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEUP
)
704 Msg
.message
= WM_MBUTTONUP
;
705 CurInfo
->ButtonsDown
&= ~MK_MBUTTON
;
706 MsqInsertSystemMessage(&Msg
);
708 if(mi
->dwFlags
& MOUSEEVENTF_RIGHTDOWN
)
710 Msg
.message
= SwapBtnMsg
[0][!SwapButtons
];
711 CurInfo
->ButtonsDown
|= SwapBtn
[!SwapButtons
];
712 MsqInsertSystemMessage(&Msg
);
714 else if(mi
->dwFlags
& MOUSEEVENTF_RIGHTUP
)
716 Msg
.message
= SwapBtnMsg
[1][!SwapButtons
];
717 CurInfo
->ButtonsDown
&= ~SwapBtn
[!SwapButtons
];
718 MsqInsertSystemMessage(&Msg
);
721 if((mi
->dwFlags
& (MOUSEEVENTF_XDOWN
| MOUSEEVENTF_XUP
)) &&
722 (mi
->dwFlags
& MOUSEEVENTF_WHEEL
))
724 /* fail because both types of events use the mouseData field */
728 if(mi
->dwFlags
& MOUSEEVENTF_XDOWN
)
730 Msg
.message
= WM_XBUTTONDOWN
;
731 if(mi
->mouseData
& XBUTTON1
)
733 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
734 CurInfo
->ButtonsDown
|= XBUTTON1
;
735 MsqInsertSystemMessage(&Msg
);
737 if(mi
->mouseData
& XBUTTON2
)
739 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
740 CurInfo
->ButtonsDown
|= XBUTTON2
;
741 MsqInsertSystemMessage(&Msg
);
744 else if(mi
->dwFlags
& MOUSEEVENTF_XUP
)
746 Msg
.message
= WM_XBUTTONUP
;
747 if(mi
->mouseData
& XBUTTON1
)
749 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
750 CurInfo
->ButtonsDown
&= ~XBUTTON1
;
751 MsqInsertSystemMessage(&Msg
);
753 if(mi
->mouseData
& XBUTTON2
)
755 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
756 CurInfo
->ButtonsDown
&= ~XBUTTON2
;
757 MsqInsertSystemMessage(&Msg
);
760 if(mi
->dwFlags
& MOUSEEVENTF_WHEEL
)
762 Msg
.message
= WM_MOUSEWHEEL
;
763 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, mi
->mouseData
);
764 MsqInsertSystemMessage(&Msg
);
771 IntKeyboardInput(KEYBDINPUT
*ki
)
783 PW32THREAD W32Thread
;
786 W32Thread
= PsGetWin32Thread();
789 if(!W32Thread
->Desktop
)
794 if(!nInputs
|| !pInput
|| (cbSize
!= sizeof(INPUT
)))
796 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
801 * FIXME - check access rights of the window station
802 * e.g. services running in the service window station cannot block input
804 if(!ThreadHasInputAccess(W32Thread
) ||
805 !IntIsActiveDesktop(W32Thread
->Desktop
))
807 SetLastWin32Error(ERROR_ACCESS_DENIED
);
817 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
818 if(!NT_SUCCESS(Status
))
820 SetLastNtError(Status
);
824 switch(SafeInput
.type
)
827 if(IntMouseInput(&SafeInput
.mi
))
833 if(IntKeyboardInput(&SafeInput
.ki
))
842 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);