3 * Copyright (C) 1998, 1999, 2000, 2001 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 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/input.c
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * 09-05-2001 CSH Created
29 /* INCLUDES ******************************************************************/
33 /* GLOBALS *******************************************************************/
36 typedef struct __TRACKINGLIST
{
38 POINT pos
; /* center of hover rectangle */
39 INT iHoverTime
; /* elapsed time the cursor has been inside of the hover rect */
42 static _TRACKINGLIST TrackingList
[10];
43 static int iTrackMax
= 0;
44 static UINT_PTR timer
;
45 static const INT iTimerInterval
= 50; /* msec for timer interval */
48 /* FUNCTIONS *****************************************************************/
61 return NtUserDragDetect(hWnd
, pt
.x
, pt
.y
);
66 ULONG dx
= NtUserGetSystemMetrics(SM_CXDRAG
);
67 ULONG dy
= NtUserGetSystemMetrics(SM_CYDRAG
);
69 rect
.left
= pt
.x
- dx
;
70 rect
.right
= pt
.x
+ dx
;
72 rect
.bottom
= pt
.y
+ dy
;
78 while (PeekMessageW(&msg
, 0, WM_MOUSEFIRST
, WM_MOUSELAST
, PM_REMOVE
))
80 if (msg
.message
== WM_LBUTTONUP
)
85 if (msg
.message
== WM_MOUSEMOVE
)
87 tmp
.x
= LOWORD(msg
.lParam
);
88 tmp
.y
= HIWORD(msg
.lParam
);
89 if (!PtInRect(&rect
, tmp
))
107 ActivateKeyboardLayout(HKL hkl
,
119 BlockInput(BOOL fBlockIt
)
121 return NtUserBlockInput(fBlockIt
);
129 EnableWindow(HWND hWnd
,
132 LONG Style
= NtUserGetWindowLong(hWnd
, GWL_STYLE
, FALSE
);
133 Style
= bEnable
? Style
& ~WS_DISABLED
: Style
| WS_DISABLED
;
134 NtUserSetWindowLong(hWnd
, GWL_STYLE
, Style
, FALSE
);
136 SendMessageA(hWnd
, WM_ENABLE
, (LPARAM
) IsWindowEnabled(hWnd
), 0);
138 // Return nonzero if it was disabled, or zero if it wasn't:
139 return IsWindowEnabled(hWnd
);
147 GetAsyncKeyState(int vKey
)
149 return (SHORT
) NtUserGetAsyncKeyState((DWORD
) vKey
);
158 GetDoubleClickTime(VOID
)
160 return NtUserGetDoubleClickTime();
168 GetKeyboardLayout(DWORD idThread
)
170 return (HKL
)NtUserCallOneParam((DWORD
) idThread
, ONEPARAM_ROUTINE_GETKEYBOARDLAYOUT
);
188 GetKeyNameTextA(LONG lParam
,
192 LPWSTR intermediateString
=
193 HeapAlloc(GetProcessHeap(),0,nSize
* sizeof(WCHAR
));
196 BOOL defChar
= FALSE
;
198 if( !intermediateString
) return 0;
199 ret
= GetKeyNameTextW(lParam
,intermediateString
,nSize
);
200 if( ret
== 0 ) { lpString
[0] = 0; return 0; }
202 wstrLen
= wcslen( intermediateString
);
203 ret
= WideCharToMultiByte(CP_ACP
, 0,
204 intermediateString
, wstrLen
,
205 lpString
, nSize
, ".", &defChar
);
207 HeapFree(GetProcessHeap(),0,intermediateString
);
216 GetKeyNameTextW(LONG lParam
,
220 return NtUserGetKeyNameText( lParam
, lpString
, nSize
);
228 GetKeyState(int nVirtKey
)
230 return (SHORT
) NtUserGetKeyState((DWORD
) nVirtKey
);
238 GetKeyboardLayoutList(int nBuff
,
250 GetKeyboardLayoutNameA(LPSTR pwszKLID
)
252 WCHAR buf
[KL_NAMELENGTH
];
254 if (GetKeyboardLayoutNameW(buf
))
255 return WideCharToMultiByte( CP_ACP
, 0, buf
, -1, pwszKLID
, KL_NAMELENGTH
, NULL
, NULL
) != 0;
264 GetKeyboardLayoutNameW(LPWSTR pwszKLID
)
275 GetKeyboardState(PBYTE lpKeyState
)
278 return (BOOL
) NtUserGetKeyboardState((LPBYTE
) lpKeyState
);
286 GetKeyboardType(int nTypeFlag
)
288 return (int)NtUserCallOneParam((DWORD
) nTypeFlag
, ONEPARAM_ROUTINE_GETKEYBOARDTYPE
);
296 GetLastInputInfo(PLASTINPUTINFO plii
)
307 LoadKeyboardLayoutA(LPCSTR pwszKLID
,
311 UNICODE_STRING pwszKLIDW
;
313 if (pwszKLID
) RtlCreateUnicodeStringFromAsciiz(&pwszKLIDW
, pwszKLID
);
314 else pwszKLIDW
.Buffer
= NULL
;
316 ret
= LoadKeyboardLayoutW(pwszKLIDW
.Buffer
, Flags
);
317 RtlFreeUnicodeString(&pwszKLIDW
);
327 LoadKeyboardLayoutW(LPCWSTR pwszKLID
,
339 MapVirtualKeyA(UINT uCode
,
342 return MapVirtualKeyExA( uCode
, uMapType
, GetKeyboardLayout( 0 ) );
350 MapVirtualKeyExA(UINT uCode
,
354 return MapVirtualKeyExW( uCode
, uMapType
, dwhkl
);
362 MapVirtualKeyExW(UINT uCode
,
366 return NtUserMapVirtualKeyEx( uCode
, uMapType
, 0, dwhkl
);
374 MapVirtualKeyW(UINT uCode
,
377 return MapVirtualKeyExW( uCode
, uMapType
, GetKeyboardLayout( 0 ) );
385 OemKeyScan(WORD wOemChar
)
391 MultiByteToWideChar(CP_OEMCP
, 0, (PCSTR
)&wOemChar
, 1, &p
, 1);
393 Scan
= MapVirtualKeyW((Vk
& 0x00ff), 0);
396 Page 450-1, MS W2k SuperBible by SAMS. Return, low word has the
397 scan code and high word has the shift state.
399 return ((Vk
& 0xff00) << 8) | Scan
;
407 RegisterHotKey(HWND hWnd
,
412 return (BOOL
)NtUserRegisterHotKey(hWnd
,
423 SetDoubleClickTime(UINT uInterval
)
425 return (BOOL
)NtUserSystemParametersInfo(SPI_SETDOUBLECLICKTIME
,
438 return NtUserSetFocus(hWnd
);
446 SetKeyboardState(LPBYTE lpKeyState
)
448 return (BOOL
) NtUserSetKeyboardState((LPBYTE
)lpKeyState
);
460 return NtUserSwapMouseButton(fSwap
);
468 ToAscii(UINT uVirtKey
,
470 CONST PBYTE lpKeyState
,
474 return ToAsciiEx(uVirtKey
, uScanCode
, lpKeyState
, lpChar
, uFlags
, 0);
482 ToAsciiEx(UINT uVirtKey
,
484 CONST PBYTE lpKeyState
,
492 Ret
= ToUnicodeEx(uVirtKey
, uScanCode
, lpKeyState
, UniChars
, 2, uFlags
, dwhkl
);
493 CharCount
= (Ret
< 0 ? 1 : Ret
);
494 WideCharToMultiByte(CP_ACP
, 0, UniChars
, CharCount
, (LPSTR
) lpChar
, 2, NULL
, NULL
);
504 ToUnicode(UINT wVirtKey
,
506 CONST PBYTE lpKeyState
,
511 return ToUnicodeEx( wVirtKey
, wScanCode
, lpKeyState
, pwszBuff
, cchBuff
,
520 ToUnicodeEx(UINT wVirtKey
,
522 CONST PBYTE lpKeyState
,
528 return NtUserToUnicodeEx( wVirtKey
, wScanCode
, lpKeyState
, pwszBuff
, cchBuff
,
537 UnloadKeyboardLayout(HKL hkl
)
548 UnregisterHotKey(HWND hWnd
,
551 return (BOOL
)NtUserUnregisterHotKey(hWnd
, id
);
563 if (IsDBCSLeadByte(ch
)) return -1;
565 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wChar
, 1);
566 return VkKeyScanW(wChar
);
574 VkKeyScanExA(CHAR ch
,
579 if (IsDBCSLeadByte(ch
)) return -1;
581 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wChar
, 1);
582 return VkKeyScanExW(wChar
, dwhkl
);
590 VkKeyScanExW(WCHAR ch
,
593 return (SHORT
) NtUserVkKeyScanEx((DWORD
) ch
,(DWORD
) dwhkl
,(DWORD
)NULL
);
603 return VkKeyScanExW(ch
, GetKeyboardLayout(0));
617 return NtUserSendInput(nInputs
, pInputs
, cbSize
);
621 * Private call for CSRSS
625 PrivateCsrssRegisterPrimitive(VOID
)
627 NtUserCallNoParam(NOPARAM_ROUTINE_REGISTER_PRIMITIVE
);
631 * Another private call for CSRSS
635 PrivateCsrssAcquireOrReleaseInputOwnership(BOOL Release
)
637 NtUserAcquireOrReleaseInputOwnership(Release
);
649 ULONG_PTR dwExtraInfo
)
655 Input
.type
= INPUT_KEYBOARD
;
657 Input
.ki
.wScan
= bScan
;
658 Input
.ki
.dwFlags
= dwFlags
;
660 Input
.ki
.dwExtraInfo
= dwExtraInfo
;
662 NtUserSendInput(1, &Input
, sizeof(INPUT
));
676 ULONG_PTR dwExtraInfo
)
680 Input
.type
= INPUT_MOUSE
;
683 Input
.mi
.mouseData
= dwData
;
684 Input
.mi
.dwFlags
= dwFlags
;
686 Input
.mi
.dwExtraInfo
= dwExtraInfo
;
688 NtUserSendInput(1, &Input
, sizeof(INPUT
));
692 /***********************************************************************
695 static WORD
get_key_state(void)
699 if (GetSystemMetrics( SM_SWAPBUTTON
))
701 if (GetAsyncKeyState(VK_RBUTTON
) & 0x80) ret
|= MK_LBUTTON
;
702 if (GetAsyncKeyState(VK_LBUTTON
) & 0x80) ret
|= MK_RBUTTON
;
706 if (GetAsyncKeyState(VK_LBUTTON
) & 0x80) ret
|= MK_LBUTTON
;
707 if (GetAsyncKeyState(VK_RBUTTON
) & 0x80) ret
|= MK_RBUTTON
;
709 if (GetAsyncKeyState(VK_MBUTTON
) & 0x80) ret
|= MK_MBUTTON
;
710 if (GetAsyncKeyState(VK_SHIFT
) & 0x80) ret
|= MK_SHIFT
;
711 if (GetAsyncKeyState(VK_CONTROL
) & 0x80) ret
|= MK_CONTROL
;
712 if (GetAsyncKeyState(VK_XBUTTON1
) & 0x80) ret
|= MK_XBUTTON1
;
713 if (GetAsyncKeyState(VK_XBUTTON2
) & 0x80) ret
|= MK_XBUTTON2
;
718 static void CALLBACK
TrackMouseEventProc(HWND hwndUnused
, UINT uMsg
, UINT_PTR idEvent
,
726 INT hoverwidth
= 0, hoverheight
= 0;
730 hwnd
= WindowFromPoint(pos
);
732 SystemParametersInfoA(SPI_GETMOUSEHOVERWIDTH
, 0, &hoverwidth
, 0);
733 SystemParametersInfoA(SPI_GETMOUSEHOVERHEIGHT
, 0, &hoverheight
, 0);
735 /* loop through tracking events we are processing */
736 while (i
< iTrackMax
) {
737 if (TrackingList
[i
].tme
.dwFlags
& TME_NONCLIENT
) {
744 /* see if this tracking event is looking for TME_LEAVE and that the */
745 /* mouse has left the window */
746 if (TrackingList
[i
].tme
.dwFlags
& TME_LEAVE
) {
747 if (TrackingList
[i
].tme
.hwndTrack
!= hwnd
) {
749 PostMessageA(TrackingList
[i
].tme
.hwndTrack
, WM_NCMOUSELEAVE
, 0, 0);
752 PostMessageA(TrackingList
[i
].tme
.hwndTrack
, WM_MOUSELEAVE
, 0, 0);
755 /* remove the TME_LEAVE flag */
756 TrackingList
[i
].tme
.dwFlags
^= TME_LEAVE
;
759 GetClientRect(hwnd
, &client
);
760 MapWindowPoints(hwnd
, NULL
, (LPPOINT
)&client
, 2);
761 if(PtInRect(&client
, pos
)) {
763 PostMessageA(TrackingList
[i
].tme
.hwndTrack
, WM_NCMOUSELEAVE
, 0, 0);
764 /* remove the TME_LEAVE flag */
765 TrackingList
[i
].tme
.dwFlags
^= TME_LEAVE
;
770 PostMessageA(TrackingList
[i
].tme
.hwndTrack
, WM_MOUSELEAVE
, 0, 0);
771 /* remove the TME_LEAVE flag */
772 TrackingList
[i
].tme
.dwFlags
^= TME_LEAVE
;
778 /* see if we are tracking hovering for this hwnd */
779 if(TrackingList
[i
].tme
.dwFlags
& TME_HOVER
) {
780 /* add the timer interval to the hovering time */
781 TrackingList
[i
].iHoverTime
+=iTimerInterval
;
783 /* has the cursor moved outside the rectangle centered around pos? */
784 if((abs(pos
.x
- TrackingList
[i
].pos
.x
) > (hoverwidth
/ 2.0))
785 || (abs(pos
.y
- TrackingList
[i
].pos
.y
) > (hoverheight
/ 2.0)))
787 /* record this new position as the current position and reset */
788 /* the iHoverTime variable to 0 */
789 TrackingList
[i
].pos
= pos
;
790 TrackingList
[i
].iHoverTime
= 0;
793 /* has the mouse hovered long enough? */
794 if(TrackingList
[i
].iHoverTime
<= TrackingList
[i
].tme
.dwHoverTime
)
798 ScreenToClient(hwnd
, &posClient
);
800 PostMessageW(TrackingList
[i
].tme
.hwndTrack
, WM_NCMOUSEHOVER
,
801 get_key_state(), MAKELPARAM( posClient
.x
, posClient
.y
));
804 PostMessageW(TrackingList
[i
].tme
.hwndTrack
, WM_MOUSEHOVER
,
805 get_key_state(), MAKELPARAM( posClient
.x
, posClient
.y
));
808 /* stop tracking mouse hover */
809 TrackingList
[i
].tme
.dwFlags
^= TME_HOVER
;
813 /* see if we are still tracking TME_HOVER or TME_LEAVE for this entry */
814 if((TrackingList
[i
].tme
.dwFlags
& TME_HOVER
) ||
815 (TrackingList
[i
].tme
.dwFlags
& TME_LEAVE
)) {
817 } else { /* remove this entry from the tracking list */
818 TrackingList
[i
] = TrackingList
[--iTrackMax
];
822 /* stop the timer if the tracking list is empty */
830 /***********************************************************************
831 * TrackMouseEvent [USER32]
833 * Requests notification of mouse events
835 * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
836 * to the hwnd specified in the ptme structure. After the event message
837 * is posted to the hwnd, the entry in the queue is removed.
839 * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
840 * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
841 * immediately and the TME_LEAVE flag being ignored.
844 * ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
857 LPTRACKMOUSEEVENT ptme
)
861 BOOL cancel
= 0, hover
= 0, leave
= 0, query
= 0, nonclient
= 0, inclient
= 0;
869 SetRectEmpty(&client
);
871 DbgPrint("%lx, %lx, %p, %lx\n", ptme
->cbSize
, ptme
->dwFlags
, ptme
->hwndTrack
, ptme
->dwHoverTime
);
874 if (ptme
->cbSize
!= sizeof(TRACKMOUSEEVENT
)) {
875 DPRINT("wrong TRACKMOUSEEVENT size from app\n");
876 SetLastError(ERROR_INVALID_PARAMETER
); /* FIXME not sure if this is correct */
880 flags
= ptme
->dwFlags
;
882 /* if HOVER_DEFAULT was specified replace this with the systems current value */
883 if(ptme
->dwHoverTime
== HOVER_DEFAULT
)
884 SystemParametersInfoA(SPI_GETMOUSEHOVERTIME
, 0, &(ptme
->dwHoverTime
), 0);
887 hwnd
= WindowFromPoint(pos
);
889 if ( flags
& TME_CANCEL
) {
890 flags
&= ~ TME_CANCEL
;
894 if ( flags
& TME_HOVER
) {
895 flags
&= ~ TME_HOVER
;
899 if ( flags
& TME_LEAVE
) {
900 flags
&= ~ TME_LEAVE
;
904 if ( flags
& TME_NONCLIENT
) {
905 flags
&= ~ TME_NONCLIENT
;
909 /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */
910 if ( flags
& TME_QUERY
) {
911 flags
&= ~ TME_QUERY
;
915 /* Find the tracking list entry with the matching hwnd */
916 while((i
< iTrackMax
) && (TrackingList
[i
].tme
.hwndTrack
!= ptme
->hwndTrack
)) {
920 /* hwnd found, fill in the ptme struct */
922 *ptme
= TrackingList
[i
].tme
;
926 return TRUE
; /* return here, TME_QUERY is retrieving information */
930 DPRINT("Unknown flag(s) %08lx\n", flags
);
933 /* find a matching hwnd if one exists */
936 while((i
< iTrackMax
) && (TrackingList
[i
].tme
.hwndTrack
!= ptme
->hwndTrack
)) {
941 TrackingList
[i
].tme
.dwFlags
&= ~(ptme
->dwFlags
& ~TME_CANCEL
);
943 /* if we aren't tracking on hover or leave remove this entry */
944 if(!((TrackingList
[i
].tme
.dwFlags
& TME_HOVER
) ||
945 (TrackingList
[i
].tme
.dwFlags
& TME_LEAVE
)))
947 TrackingList
[i
] = TrackingList
[--iTrackMax
];
956 /* see if hwndTrack isn't the current window */
957 if(ptme
->hwndTrack
!= hwnd
) {
960 PostMessageA(ptme
->hwndTrack
, WM_NCMOUSELEAVE
, 0, 0);
963 PostMessageA(ptme
->hwndTrack
, WM_MOUSELEAVE
, 0, 0);
967 GetClientRect(ptme
->hwndTrack
, &client
);
968 MapWindowPoints(ptme
->hwndTrack
, NULL
, (LPPOINT
)&client
, 2);
969 if(PtInRect(&client
, pos
)) {
972 if(nonclient
&& inclient
) {
973 PostMessageA(ptme
->hwndTrack
, WM_NCMOUSELEAVE
, 0, 0);
976 else if(!nonclient
&& !inclient
) {
977 PostMessageA(ptme
->hwndTrack
, WM_MOUSELEAVE
, 0, 0);
981 /* See if this hwnd is already being tracked and update the tracking flags */
982 for(i
= 0; i
< iTrackMax
; i
++) {
983 if(TrackingList
[i
].tme
.hwndTrack
== ptme
->hwndTrack
) {
984 TrackingList
[i
].tme
.dwFlags
= 0;
987 TrackingList
[i
].tme
.dwFlags
|= TME_HOVER
;
988 TrackingList
[i
].tme
.dwHoverTime
= ptme
->dwHoverTime
;
992 TrackingList
[i
].tme
.dwFlags
|= TME_LEAVE
;
995 TrackingList
[i
].tme
.dwFlags
|= TME_NONCLIENT
;
997 /* reset iHoverTime as per winapi specs */
998 TrackingList
[i
].iHoverTime
= 0;
1004 /* if the tracking list is full return FALSE */
1005 if (iTrackMax
== sizeof (TrackingList
) / sizeof(*TrackingList
)) {
1009 /* Adding new mouse event to the tracking list */
1010 TrackingList
[iTrackMax
].tme
= *ptme
;
1012 /* Initialize HoverInfo variables even if not hover tracking */
1013 TrackingList
[iTrackMax
].iHoverTime
= 0;
1014 TrackingList
[iTrackMax
].pos
= pos
;
1019 timer
= SetTimer(0, 0, iTimerInterval
, TrackMouseEventProc
);