winebus.sys: Rename UDEV bus device variables to be consistent.
[wine.git] / dlls / user32 / input.c
blobb4e3579f5e8e93ca1b08e416b9b1f8342c4796d5
1 /*
2 * USER Input processing
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1996 Albrecht Kleine
6 * Copyright 1997 David Faure
7 * Copyright 1998 Morten Welinder
8 * Copyright 1998 Ulrich Weigand
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <assert.h>
32 #define NONAMELESSUNION
34 #include "ntstatus.h"
35 #define WIN32_NO_STATUS
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "winnls.h"
41 #include "winternl.h"
42 #include "winerror.h"
43 #include "win.h"
44 #include "user_private.h"
45 #include "dbt.h"
46 #include "wine/server.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(win);
50 WINE_DECLARE_DEBUG_CHANNEL(keyboard);
52 INT global_key_state_counter = 0;
54 /***********************************************************************
55 * get_key_state
57 static WORD get_key_state(void)
59 WORD ret = 0;
61 if (GetSystemMetrics( SM_SWAPBUTTON ))
63 if (GetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_LBUTTON;
64 if (GetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_RBUTTON;
66 else
68 if (GetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_LBUTTON;
69 if (GetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_RBUTTON;
71 if (GetAsyncKeyState(VK_MBUTTON) & 0x80) ret |= MK_MBUTTON;
72 if (GetAsyncKeyState(VK_SHIFT) & 0x80) ret |= MK_SHIFT;
73 if (GetAsyncKeyState(VK_CONTROL) & 0x80) ret |= MK_CONTROL;
74 if (GetAsyncKeyState(VK_XBUTTON1) & 0x80) ret |= MK_XBUTTON1;
75 if (GetAsyncKeyState(VK_XBUTTON2) & 0x80) ret |= MK_XBUTTON2;
76 return ret;
80 /***********************************************************************
81 * get_locale_kbd_layout
83 static HKL get_locale_kbd_layout(void)
85 ULONG_PTR layout;
86 LANGID langid;
88 /* FIXME:
90 * layout = main_key_tab[kbd_layout].lcid;
92 * Winword uses return value of GetKeyboardLayout as a codepage
93 * to translate ANSI keyboard messages to unicode. But we have
94 * a problem with it: for instance Polish keyboard layout is
95 * identical to the US one, and therefore instead of the Polish
96 * locale id we return the US one.
99 layout = GetUserDefaultLCID();
102 * Microsoft Office expects this value to be something specific
103 * for Japanese and Korean Windows with an IME the value is 0xe001
104 * We should probably check to see if an IME exists and if so then
105 * set this word properly.
107 langid = PRIMARYLANGID( LANGIDFROMLCID( layout ) );
108 if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
109 layout = MAKELONG( layout, 0xe001 ); /* IME */
110 else
111 layout = MAKELONG( layout, layout );
113 return (HKL)layout;
117 /**********************************************************************
118 * keyboard_init
120 void keyboard_init(void)
122 WCHAR layout[KL_NAMELENGTH];
123 HKEY hkey;
125 if (RegCreateKeyExW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", 0, NULL, 0,
126 KEY_ALL_ACCESS, NULL, &hkey, NULL ))
127 return;
129 if (GetKeyboardLayoutNameW( layout ))
130 RegSetValueExW( hkey, L"1", 0, REG_SZ, (const BYTE *)layout, sizeof(layout) );
132 RegCloseKey( hkey );
136 /**********************************************************************
137 * set_capture_window
139 BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
141 HWND previous = 0;
142 UINT flags = 0;
143 BOOL ret;
145 if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU;
146 if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE;
148 SERVER_START_REQ( set_capture_window )
150 req->handle = wine_server_user_handle( hwnd );
151 req->flags = flags;
152 if ((ret = !wine_server_call_err( req )))
154 previous = wine_server_ptr_handle( reply->previous );
155 hwnd = wine_server_ptr_handle( reply->full_handle );
158 SERVER_END_REQ;
160 if (ret)
162 USER_Driver->pSetCapture( hwnd, gui_flags );
164 if (previous)
165 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
167 if (prev_ret) *prev_ret = previous;
169 return ret;
173 /***********************************************************************
174 * __wine_send_input (USER32.@)
176 * Internal SendInput function to allow the graphics driver to inject real events.
178 BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput )
180 NTSTATUS status = send_hardware_message( hwnd, input, rawinput, 0 );
181 if (status) SetLastError( RtlNtStatusToDosError(status) );
182 return !status;
186 /***********************************************************************
187 * update_mouse_coords
189 * Helper for SendInput.
191 static void update_mouse_coords( INPUT *input )
193 if (!(input->u.mi.dwFlags & MOUSEEVENTF_MOVE)) return;
195 if (input->u.mi.dwFlags & MOUSEEVENTF_ABSOLUTE)
197 DPI_AWARENESS_CONTEXT context = SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
198 RECT rc;
200 if (input->u.mi.dwFlags & MOUSEEVENTF_VIRTUALDESK)
201 rc = get_virtual_screen_rect();
202 else
203 rc = get_primary_monitor_rect();
205 input->u.mi.dx = rc.left + ((input->u.mi.dx * (rc.right - rc.left)) >> 16);
206 input->u.mi.dy = rc.top + ((input->u.mi.dy * (rc.bottom - rc.top)) >> 16);
207 SetThreadDpiAwarenessContext( context );
209 else
211 int accel[3];
213 /* dx and dy can be negative numbers for relative movements */
214 SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
216 if (!accel[2]) return;
218 if (abs(input->u.mi.dx) > accel[0])
220 input->u.mi.dx *= 2;
221 if ((abs(input->u.mi.dx) > accel[1]) && (accel[2] == 2)) input->u.mi.dx *= 2;
223 if (abs(input->u.mi.dy) > accel[0])
225 input->u.mi.dy *= 2;
226 if ((abs(input->u.mi.dy) > accel[1]) && (accel[2] == 2)) input->u.mi.dy *= 2;
231 /***********************************************************************
232 * SendInput (USER32.@)
234 UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size )
236 UINT i;
237 NTSTATUS status = STATUS_SUCCESS;
239 if (size != sizeof(INPUT))
241 SetLastError( ERROR_INVALID_PARAMETER );
242 return 0;
245 if (!count)
247 SetLastError( ERROR_INVALID_PARAMETER );
248 return 0;
251 if (!inputs)
253 SetLastError( ERROR_NOACCESS );
254 return 0;
257 for (i = 0; i < count; i++)
259 INPUT input = inputs[i];
260 switch (input.type)
262 case INPUT_MOUSE:
263 /* we need to update the coordinates to what the server expects */
264 update_mouse_coords( &input );
265 /* fallthrough */
266 case INPUT_KEYBOARD:
267 status = send_hardware_message( 0, &input, NULL, SEND_HWMSG_INJECTED );
268 break;
269 case INPUT_HARDWARE:
270 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
271 return 0;
274 if (status)
276 SetLastError( RtlNtStatusToDosError(status) );
277 break;
281 return i;
285 /***********************************************************************
286 * keybd_event (USER32.@)
288 void WINAPI keybd_event( BYTE bVk, BYTE bScan,
289 DWORD dwFlags, ULONG_PTR dwExtraInfo )
291 INPUT input;
293 input.type = INPUT_KEYBOARD;
294 input.u.ki.wVk = bVk;
295 input.u.ki.wScan = bScan;
296 input.u.ki.dwFlags = dwFlags;
297 input.u.ki.time = 0;
298 input.u.ki.dwExtraInfo = dwExtraInfo;
299 SendInput( 1, &input, sizeof(input) );
303 /***********************************************************************
304 * mouse_event (USER32.@)
306 void WINAPI mouse_event( DWORD dwFlags, DWORD dx, DWORD dy,
307 DWORD dwData, ULONG_PTR dwExtraInfo )
309 INPUT input;
311 input.type = INPUT_MOUSE;
312 input.u.mi.dx = dx;
313 input.u.mi.dy = dy;
314 input.u.mi.mouseData = dwData;
315 input.u.mi.dwFlags = dwFlags;
316 input.u.mi.time = 0;
317 input.u.mi.dwExtraInfo = dwExtraInfo;
318 SendInput( 1, &input, sizeof(input) );
322 /***********************************************************************
323 * GetCursorPos (USER32.@)
325 BOOL WINAPI DECLSPEC_HOTPATCH GetCursorPos( POINT *pt )
327 BOOL ret;
328 DWORD last_change;
329 UINT dpi;
331 if (!pt) return FALSE;
333 SERVER_START_REQ( set_cursor )
335 if ((ret = !wine_server_call( req )))
337 pt->x = reply->new_x;
338 pt->y = reply->new_y;
339 last_change = reply->last_change;
342 SERVER_END_REQ;
344 /* query new position from graphics driver if we haven't updated recently */
345 if (ret && GetTickCount() - last_change > 100) ret = USER_Driver->pGetCursorPos( pt );
346 if (ret && (dpi = get_thread_dpi()))
348 DPI_AWARENESS_CONTEXT context;
349 context = SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
350 *pt = map_dpi_point( *pt, get_monitor_dpi( MonitorFromPoint( *pt, MONITOR_DEFAULTTOPRIMARY )), dpi );
351 SetThreadDpiAwarenessContext( context );
353 return ret;
357 /***********************************************************************
358 * GetCursorInfo (USER32.@)
360 BOOL WINAPI GetCursorInfo( PCURSORINFO pci )
362 BOOL ret;
364 if (!pci) return FALSE;
366 SERVER_START_REQ( get_thread_input )
368 req->tid = 0;
369 if ((ret = !wine_server_call( req )))
371 pci->hCursor = wine_server_ptr_handle( reply->cursor );
372 pci->flags = (reply->show_count >= 0) ? CURSOR_SHOWING : 0;
375 SERVER_END_REQ;
376 GetCursorPos(&pci->ptScreenPos);
377 return ret;
381 /***********************************************************************
382 * SetCursorPos (USER32.@)
384 BOOL WINAPI DECLSPEC_HOTPATCH SetCursorPos( INT x, INT y )
386 POINT pt = { x, y };
387 BOOL ret;
388 INT prev_x, prev_y, new_x, new_y;
389 UINT dpi;
391 if ((dpi = get_thread_dpi()))
392 pt = map_dpi_point( pt, dpi, get_monitor_dpi( MonitorFromPoint( pt, MONITOR_DEFAULTTOPRIMARY )));
394 SERVER_START_REQ( set_cursor )
396 req->flags = SET_CURSOR_POS;
397 req->x = pt.x;
398 req->y = pt.y;
399 if ((ret = !wine_server_call( req )))
401 prev_x = reply->prev_x;
402 prev_y = reply->prev_y;
403 new_x = reply->new_x;
404 new_y = reply->new_y;
407 SERVER_END_REQ;
408 if (ret && (prev_x != new_x || prev_y != new_y)) USER_Driver->pSetCursorPos( new_x, new_y );
409 return ret;
412 /**********************************************************************
413 * SetCapture (USER32.@)
415 HWND WINAPI DECLSPEC_HOTPATCH SetCapture( HWND hwnd )
417 HWND previous = 0;
419 set_capture_window( hwnd, 0, &previous );
420 return previous;
424 /**********************************************************************
425 * ReleaseCapture (USER32.@)
427 BOOL WINAPI DECLSPEC_HOTPATCH ReleaseCapture(void)
429 BOOL ret = set_capture_window( 0, 0, NULL );
431 /* Somebody may have missed some mouse movements */
432 if (ret) mouse_event( MOUSEEVENTF_MOVE, 0, 0, 0, 0 );
434 return ret;
438 /**********************************************************************
439 * GetCapture (USER32.@)
441 HWND WINAPI GetCapture(void)
443 HWND ret = 0;
445 SERVER_START_REQ( get_thread_input )
447 req->tid = GetCurrentThreadId();
448 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->capture );
450 SERVER_END_REQ;
451 return ret;
455 static void check_for_events( UINT flags )
457 if (USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, flags, 0 ) == WAIT_TIMEOUT)
458 flush_window_surfaces( TRUE );
461 /**********************************************************************
462 * GetAsyncKeyState (USER32.@)
464 * Determine if a key is or was pressed. retval has high-order
465 * bit set to 1 if currently pressed, low-order bit set to 1 if key has
466 * been pressed.
468 SHORT WINAPI DECLSPEC_HOTPATCH GetAsyncKeyState( INT key )
470 struct user_key_state_info *key_state_info = get_user_thread_info()->key_state;
471 INT counter = global_key_state_counter;
472 BYTE prev_key_state;
473 SHORT ret;
475 if (key < 0 || key >= 256) return 0;
477 check_for_events( QS_INPUT );
479 if (key_state_info && !(key_state_info->state[key] & 0xc0) &&
480 key_state_info->counter == counter && GetTickCount() - key_state_info->time < 50)
482 /* use cached value */
483 return 0;
485 else if (!key_state_info)
487 key_state_info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*key_state_info) );
488 get_user_thread_info()->key_state = key_state_info;
491 ret = 0;
492 SERVER_START_REQ( get_key_state )
494 req->async = 1;
495 req->key = key;
496 if (key_state_info)
498 prev_key_state = key_state_info->state[key];
499 wine_server_set_reply( req, key_state_info->state, sizeof(key_state_info->state) );
501 if (!wine_server_call( req ))
503 if (reply->state & 0x40) ret |= 0x0001;
504 if (reply->state & 0x80) ret |= 0x8000;
505 if (key_state_info)
507 /* force refreshing the key state cache - some multithreaded programs
508 * (like Adobe Photoshop CS5) expect that changes to the async key state
509 * are also immediately available in other threads. */
510 if (prev_key_state != key_state_info->state[key])
511 counter = InterlockedIncrement( &global_key_state_counter );
513 key_state_info->time = GetTickCount();
514 key_state_info->counter = counter;
518 SERVER_END_REQ;
520 return ret;
524 /***********************************************************************
525 * GetQueueStatus (USER32.@)
527 DWORD WINAPI GetQueueStatus( UINT flags )
529 DWORD ret;
531 if (flags & ~(QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_SMRESULT))
533 SetLastError( ERROR_INVALID_FLAGS );
534 return 0;
537 check_for_events( flags );
539 SERVER_START_REQ( get_queue_status )
541 req->clear_bits = flags;
542 wine_server_call( req );
543 ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags );
545 SERVER_END_REQ;
546 return ret;
550 /***********************************************************************
551 * GetInputState (USER32.@)
553 BOOL WINAPI GetInputState(void)
555 DWORD ret;
557 check_for_events( QS_INPUT );
559 SERVER_START_REQ( get_queue_status )
561 req->clear_bits = 0;
562 wine_server_call( req );
563 ret = reply->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
565 SERVER_END_REQ;
566 return ret;
570 /******************************************************************
571 * GetLastInputInfo (USER32.@)
573 BOOL WINAPI GetLastInputInfo(PLASTINPUTINFO plii)
575 BOOL ret;
577 TRACE("%p\n", plii);
579 if (plii->cbSize != sizeof (*plii) )
581 SetLastError(ERROR_INVALID_PARAMETER);
582 return FALSE;
585 SERVER_START_REQ( get_last_input_time )
587 ret = !wine_server_call_err( req );
588 if (ret)
589 plii->dwTime = reply->time;
591 SERVER_END_REQ;
592 return ret;
596 /**********************************************************************
597 * AttachThreadInput (USER32.@)
599 * Attaches the input processing mechanism of one thread to that of
600 * another thread.
602 BOOL WINAPI AttachThreadInput( DWORD from, DWORD to, BOOL attach )
604 BOOL ret;
606 SERVER_START_REQ( attach_thread_input )
608 req->tid_from = from;
609 req->tid_to = to;
610 req->attach = attach;
611 ret = !wine_server_call_err( req );
613 SERVER_END_REQ;
614 return ret;
618 /**********************************************************************
619 * GetKeyState (USER32.@)
621 * An application calls the GetKeyState function in response to a
622 * keyboard-input message. This function retrieves the state of the key
623 * at the time the input message was generated.
625 SHORT WINAPI DECLSPEC_HOTPATCH GetKeyState(INT vkey)
627 SHORT retval = 0;
629 SERVER_START_REQ( get_key_state )
631 req->key = vkey;
632 if (!wine_server_call( req )) retval = (signed char)(reply->state & 0x81);
634 SERVER_END_REQ;
635 TRACE("key (0x%x) -> %x\n", vkey, retval);
636 return retval;
640 /**********************************************************************
641 * GetKeyboardState (USER32.@)
643 BOOL WINAPI DECLSPEC_HOTPATCH GetKeyboardState( LPBYTE state )
645 BOOL ret;
646 UINT i;
648 TRACE("(%p)\n", state);
650 memset( state, 0, 256 );
651 SERVER_START_REQ( get_key_state )
653 req->key = -1;
654 wine_server_set_reply( req, state, 256 );
655 ret = !wine_server_call_err( req );
656 for (i = 0; i < 256; i++) state[i] &= 0x81;
658 SERVER_END_REQ;
659 return ret;
663 /**********************************************************************
664 * SetKeyboardState (USER32.@)
666 BOOL WINAPI SetKeyboardState( LPBYTE state )
668 BOOL ret;
670 SERVER_START_REQ( set_key_state )
672 wine_server_add_data( req, state, 256 );
673 ret = !wine_server_call_err( req );
675 SERVER_END_REQ;
676 return ret;
680 /**********************************************************************
681 * VkKeyScanA (USER32.@)
683 * VkKeyScan translates an ANSI character to a virtual-key and shift code
684 * for the current keyboard.
685 * high-order byte yields :
686 * 0 Unshifted
687 * 1 Shift
688 * 2 Ctrl
689 * 3-5 Shift-key combinations that are not used for characters
690 * 6 Ctrl-Alt
691 * 7 Ctrl-Alt-Shift
692 * I.e. : Shift = 1, Ctrl = 2, Alt = 4.
693 * FIXME : works ok except for dead chars :
694 * VkKeyScan '^'(0x5e, 94) ... got keycode 00 ... returning 00
695 * VkKeyScan '`'(0x60, 96) ... got keycode 00 ... returning 00
697 SHORT WINAPI VkKeyScanA(CHAR cChar)
699 WCHAR wChar;
701 if (IsDBCSLeadByte(cChar)) return -1;
703 MultiByteToWideChar(CP_ACP, 0, &cChar, 1, &wChar, 1);
704 return VkKeyScanW(wChar);
707 /******************************************************************************
708 * VkKeyScanW (USER32.@)
710 SHORT WINAPI VkKeyScanW(WCHAR cChar)
712 return VkKeyScanExW(cChar, GetKeyboardLayout(0));
715 /**********************************************************************
716 * VkKeyScanExA (USER32.@)
718 WORD WINAPI VkKeyScanExA(CHAR cChar, HKL dwhkl)
720 WCHAR wChar;
722 if (IsDBCSLeadByte(cChar)) return -1;
724 MultiByteToWideChar(CP_ACP, 0, &cChar, 1, &wChar, 1);
725 return VkKeyScanExW(wChar, dwhkl);
728 /******************************************************************************
729 * VkKeyScanExW (USER32.@)
731 WORD WINAPI VkKeyScanExW( WCHAR chr, HKL layout )
733 WORD shift = 0x100, ctrl = 0x200;
734 SHORT ret;
736 TRACE_(keyboard)( "chr %s, layout %p\n", debugstr_wn(&chr, 1), layout );
738 if ((ret = USER_Driver->pVkKeyScanEx( chr, layout )) != -256) return ret;
740 /* FIXME: English keyboard layout specific */
742 if (chr == VK_CANCEL || chr == VK_BACK || chr == VK_TAB || chr == VK_RETURN ||
743 chr == VK_ESCAPE || chr == VK_SPACE) ret = chr;
744 else if (chr >= '0' && chr <= '9') ret = chr;
745 else if (chr == ')') ret = shift + '0';
746 else if (chr == '!') ret = shift + '1';
747 else if (chr == '@') ret = shift + '2';
748 else if (chr == '#') ret = shift + '3';
749 else if (chr == '$') ret = shift + '4';
750 else if (chr == '%') ret = shift + '5';
751 else if (chr == '^') ret = shift + '6';
752 else if (chr == '&') ret = shift + '7';
753 else if (chr == '*') ret = shift + '8';
754 else if (chr == '(') ret = shift + '9';
755 else if (chr >= 'a' && chr <= 'z') ret = chr - 'a' + 'A';
756 else if (chr >= 'A' && chr <= 'Z') ret = shift + chr;
757 else if (chr == ';') ret = VK_OEM_1;
758 else if (chr == '=') ret = VK_OEM_PLUS;
759 else if (chr == ',') ret = VK_OEM_COMMA;
760 else if (chr == '-') ret = VK_OEM_MINUS;
761 else if (chr == '.') ret = VK_OEM_PERIOD;
762 else if (chr == '/') ret = VK_OEM_2;
763 else if (chr == '`') ret = VK_OEM_3;
764 else if (chr == '[') ret = VK_OEM_4;
765 else if (chr == '\\') ret = VK_OEM_5;
766 else if (chr == ']') ret = VK_OEM_6;
767 else if (chr == '\'') ret = VK_OEM_7;
768 else if (chr == ':') ret = shift + VK_OEM_1;
769 else if (chr == '+') ret = shift + VK_OEM_PLUS;
770 else if (chr == '<') ret = shift + VK_OEM_COMMA;
771 else if (chr == '_') ret = shift + VK_OEM_MINUS;
772 else if (chr == '>') ret = shift + VK_OEM_PERIOD;
773 else if (chr == '?') ret = shift + VK_OEM_2;
774 else if (chr == '~') ret = shift + VK_OEM_3;
775 else if (chr == '{') ret = shift + VK_OEM_4;
776 else if (chr == '|') ret = shift + VK_OEM_5;
777 else if (chr == '}') ret = shift + VK_OEM_6;
778 else if (chr == '\"') ret = shift + VK_OEM_7;
779 else if (chr == 0x7f) ret = ctrl + VK_BACK;
780 else if (chr == '\n') ret = ctrl + VK_RETURN;
781 else if (chr == 0xf000) ret = ctrl + '2';
782 else if (chr == 0x0000) ret = ctrl + shift + '2';
783 else if (chr >= 0x0001 && chr <= 0x001a) ret = ctrl + 'A' + chr - 1;
784 else if (chr >= 0x001c && chr <= 0x001d) ret = ctrl + VK_OEM_3 + chr;
785 else if (chr == 0x001e) ret = ctrl + shift + '6';
786 else if (chr == 0x001f) ret = ctrl + shift + VK_OEM_MINUS;
787 else ret = -1;
789 TRACE_(keyboard)( "ret %04x\n", ret );
790 return ret;
793 /**********************************************************************
794 * OemKeyScan (USER32.@)
796 DWORD WINAPI OemKeyScan( WORD oem )
798 WCHAR wchr;
799 DWORD vkey, scan;
800 char oem_char = LOBYTE( oem );
802 if (!OemToCharBuffW( &oem_char, &wchr, 1 ))
803 return -1;
805 vkey = VkKeyScanW( wchr );
806 scan = MapVirtualKeyW( LOBYTE( vkey ), MAPVK_VK_TO_VSC );
807 if (!scan) return -1;
809 vkey &= 0xff00;
810 vkey <<= 8;
811 return vkey | scan;
814 /******************************************************************************
815 * GetKeyboardType (USER32.@)
817 INT WINAPI GetKeyboardType(INT nTypeFlag)
819 TRACE_(keyboard)("(%d)\n", nTypeFlag);
820 if (LOWORD(GetKeyboardLayout(0)) == MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN))
822 /* scan code for `_', the key left of r-shift, in Japanese 106 keyboard */
823 const UINT JP106_VSC_USCORE = 0x73;
825 switch(nTypeFlag)
827 case 0: /* Keyboard type */
828 return 7; /* Japanese keyboard */
829 case 1: /* Keyboard Subtype */
830 /* Test keyboard mappings to detect Japanese keyboard */
831 if (MapVirtualKeyW(VK_OEM_102, MAPVK_VK_TO_VSC) == JP106_VSC_USCORE
832 && MapVirtualKeyW(JP106_VSC_USCORE, MAPVK_VSC_TO_VK) == VK_OEM_102)
833 return 2; /* Japanese 106 */
834 else
835 return 0; /* AT-101 */
836 case 2: /* Number of F-keys */
837 return 12; /* It has 12 F-keys */
840 else
842 switch(nTypeFlag)
844 case 0: /* Keyboard type */
845 return 4; /* AT-101 */
846 case 1: /* Keyboard Subtype */
847 return 0; /* There are no defined subtypes */
848 case 2: /* Number of F-keys */
849 return 12; /* We're doing an 101 for now, so return 12 F-keys */
852 WARN_(keyboard)("Unknown type\n");
853 return 0; /* The book says 0 here, so 0 */
856 /******************************************************************************
857 * MapVirtualKeyA (USER32.@)
859 UINT WINAPI MapVirtualKeyA(UINT code, UINT maptype)
861 return MapVirtualKeyExA( code, maptype, GetKeyboardLayout(0) );
864 /******************************************************************************
865 * MapVirtualKeyW (USER32.@)
867 UINT WINAPI MapVirtualKeyW(UINT code, UINT maptype)
869 return MapVirtualKeyExW(code, maptype, GetKeyboardLayout(0));
872 /******************************************************************************
873 * MapVirtualKeyExA (USER32.@)
875 UINT WINAPI MapVirtualKeyExA(UINT code, UINT maptype, HKL hkl)
877 UINT ret;
879 ret = MapVirtualKeyExW( code, maptype, hkl );
880 if (maptype == MAPVK_VK_TO_CHAR)
882 BYTE ch = 0;
883 WCHAR wch = ret;
885 WideCharToMultiByte( CP_ACP, 0, &wch, 1, (LPSTR)&ch, 1, NULL, NULL );
886 ret = ch;
888 return ret;
892 /* English keyboard layout (0x0409) */
893 static const UINT kbd_en_vsc2vk[] =
895 0x00, 0x1b, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0xbd, 0xbb, 0x08, 0x09,
896 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4f, 0x50, 0xdb, 0xdd, 0x0d, 0xa2, 0x41, 0x53,
897 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0xba, 0xde, 0xc0, 0xa0, 0xdc, 0x5a, 0x58, 0x43, 0x56,
898 0x42, 0x4e, 0x4d, 0xbc, 0xbe, 0xbf, 0xa1, 0x6a, 0xa4, 0x20, 0x14, 0x70, 0x71, 0x72, 0x73, 0x74,
899 0x75, 0x76, 0x77, 0x78, 0x79, 0x90, 0x91, 0x24, 0x26, 0x21, 0x6d, 0x25, 0x0c, 0x27, 0x6b, 0x23,
900 0x28, 0x22, 0x2d, 0x2e, 0x2c, 0x00, 0xe2, 0x7a, 0x7b, 0x0c, 0xee, 0xf1, 0xea, 0xf9, 0xf5, 0xf3,
901 0x00, 0x00, 0xfb, 0x2f, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0xed,
902 0x00, 0xe9, 0x00, 0xc1, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x09, 0x00, 0xc2, 0x00,
903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
911 /* 0xe000 */
912 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
913 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x0d, 0xa3, 0x00, 0x00,
914 0xad, 0xb7, 0xb3, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x00,
915 0xaf, 0x00, 0xac, 0x00, 0x00, 0x6f, 0x00, 0x2c, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x24, 0x26, 0x21, 0x00, 0x25, 0x00, 0x27, 0x00, 0x23,
917 0x28, 0x22, 0x2d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x00, 0x5f,
918 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xab, 0xa8, 0xa9, 0xa7, 0xa6, 0xb6, 0xb4, 0xb5, 0x00, 0x00,
919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
925 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
927 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
928 /* 0xe100 */
929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00,
931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
947 static const UINT kbd_en_vk2char[] =
949 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00,
950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00,
951 ' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
952 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
953 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
954 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00,
955 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '+', 0x00, '-', '.', '/',
956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
957 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
958 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
960 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ';', '=', ',', '-', '.', '/',
961 '`', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '[', '\\', ']', '\'', 0x00,
963 0x00, 0x00, '\\', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
964 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
967 static const WCHAR *kbd_en_vscname[] =
969 0, L"Esc", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Backspace", L"Tab",
970 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Enter", L"Ctrl", 0, 0,
971 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Shift", 0, 0, 0, 0, 0,
972 0, 0, 0, 0, 0, 0, L"Right Shift", L"Num *", L"Alt", L"Space", L"Caps Lock", L"F1", L"F2", L"F3", L"F4", L"F5",
973 L"F6", L"F7", L"F8", L"F9", L"F10", L"Pause", L"Scroll Lock", L"Num 7", L"Num 8", L"Num 9", L"Num -", L"Num 4", L"Num 5", L"Num 6", L"Num +", L"Num 1",
974 L"Num 2", L"Num 3", L"Num 0", L"Num Del", L"Sys Req", 0, 0, L"F11", L"F12", 0, 0, 0, 0, 0, 0, 0,
975 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
976 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"F13", L"F14", L"F15", L"F16",
977 L"F17", L"F18", L"F19", L"F20", L"F21", L"F22", L"F23", L"F24", 0, 0, 0, 0, 0, 0, 0, 0,
978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
979 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
980 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
982 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
985 /* extended */
986 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Num Enter", L"Right Ctrl", 0, 0,
988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
989 0, 0, 0, 0, 0, L"Num /", 0, L"Prnt Scrn", L"Right Alt", 0, 0, 0, 0, 0, 0, 0,
990 0, 0, 0, 0, 0, L"Num Lock", L"Break", L"Home", L"Up", L"Page Up", 0, L"Left", 0, L"Right", 0, L"End",
991 L"Down", L"Page Down", L"Insert", L"Delete", L"<00>", 0, L"Help", 0, 0, 0, 0, L"Left Windows", L"Right Windows", L"Application", 0, 0,
992 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
993 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
994 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
995 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
996 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
997 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
998 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
999 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1000 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1001 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1005 /******************************************************************************
1006 * MapVirtualKeyExW (USER32.@)
1008 UINT WINAPI MapVirtualKeyExW( UINT code, UINT type, HKL layout )
1010 const UINT *vsc2vk, *vk2char;
1011 UINT vsc2vk_size, vk2char_size;
1012 UINT ret;
1014 TRACE_(keyboard)( "code %u, type %u, layout %p.\n", code, type, layout );
1016 if ((ret = USER_Driver->pMapVirtualKeyEx( code, type, layout )) != -1) return ret;
1018 /* FIXME: English keyboard layout specific */
1020 vsc2vk = kbd_en_vsc2vk;
1021 vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk);
1022 vk2char = kbd_en_vk2char;
1023 vk2char_size = ARRAYSIZE(kbd_en_vk2char);
1025 switch (type)
1027 case MAPVK_VK_TO_VSC_EX:
1028 case MAPVK_VK_TO_VSC:
1029 switch (code)
1031 case VK_SHIFT: code = VK_LSHIFT; break;
1032 case VK_CONTROL: code = VK_LCONTROL; break;
1033 case VK_MENU: code = VK_LMENU; break;
1034 case VK_NUMPAD0: code = VK_INSERT; break;
1035 case VK_NUMPAD1: code = VK_END; break;
1036 case VK_NUMPAD2: code = VK_DOWN; break;
1037 case VK_NUMPAD3: code = VK_NEXT; break;
1038 case VK_NUMPAD4: code = VK_LEFT; break;
1039 case VK_NUMPAD5: code = VK_CLEAR; break;
1040 case VK_NUMPAD6: code = VK_RIGHT; break;
1041 case VK_NUMPAD7: code = VK_HOME; break;
1042 case VK_NUMPAD8: code = VK_UP; break;
1043 case VK_NUMPAD9: code = VK_PRIOR; break;
1044 case VK_DECIMAL: code = VK_DELETE; break;
1047 for (ret = 0; ret < vsc2vk_size; ++ret) if (vsc2vk[ret] == code) break;
1048 if (ret >= vsc2vk_size) ret = 0;
1050 if (type == MAPVK_VK_TO_VSC)
1052 if (ret >= 0x200) ret = 0;
1053 else ret &= 0xff;
1055 else if (ret >= 0x100) ret += 0xdf00;
1056 break;
1057 case MAPVK_VSC_TO_VK:
1058 case MAPVK_VSC_TO_VK_EX:
1059 if (code & 0xe000) code -= 0xdf00;
1060 if (code >= vsc2vk_size) ret = 0;
1061 else ret = vsc2vk[code];
1063 if (type == MAPVK_VSC_TO_VK)
1065 switch (ret)
1067 case VK_LSHIFT: case VK_RSHIFT: ret = VK_SHIFT; break;
1068 case VK_LCONTROL: case VK_RCONTROL: ret = VK_CONTROL; break;
1069 case VK_LMENU: case VK_RMENU: ret = VK_MENU; break;
1072 break;
1073 case MAPVK_VK_TO_CHAR:
1074 if (code >= vk2char_size) ret = 0;
1075 else ret = vk2char[code];
1076 break;
1077 default:
1078 FIXME_(keyboard)( "unknown type %d\n", type );
1079 return 0;
1082 TRACE_(keyboard)( "returning 0x%04x\n", ret );
1083 return ret;
1086 /****************************************************************************
1087 * GetKBCodePage (USER32.@)
1089 UINT WINAPI GetKBCodePage(void)
1091 return GetOEMCP();
1094 /***********************************************************************
1095 * GetKeyboardLayout (USER32.@)
1097 * - device handle for keyboard layout defaulted to
1098 * the language id. This is the way Windows default works.
1099 * - the thread identifier is also ignored.
1101 HKL WINAPI GetKeyboardLayout(DWORD thread_id)
1103 struct user_thread_info *thread = get_user_thread_info();
1104 HKL layout = thread->kbd_layout;
1106 if (thread_id && thread_id != GetCurrentThreadId())
1107 FIXME( "couldn't return keyboard layout for thread %04x\n", thread_id );
1109 if (!layout) return get_locale_kbd_layout();
1110 return layout;
1113 /****************************************************************************
1114 * GetKeyboardLayoutNameA (USER32.@)
1116 BOOL WINAPI GetKeyboardLayoutNameA(LPSTR pszKLID)
1118 WCHAR buf[KL_NAMELENGTH];
1120 if (GetKeyboardLayoutNameW(buf))
1121 return WideCharToMultiByte( CP_ACP, 0, buf, -1, pszKLID, KL_NAMELENGTH, NULL, NULL ) != 0;
1122 return FALSE;
1125 /****************************************************************************
1126 * GetKeyboardLayoutNameW (USER32.@)
1128 BOOL WINAPI GetKeyboardLayoutNameW( WCHAR *name )
1130 struct user_thread_info *info = get_user_thread_info();
1131 WCHAR klid[KL_NAMELENGTH], value[5];
1132 DWORD value_size, tmp, i = 0;
1133 HKEY hkey;
1134 HKL layout;
1136 TRACE_(keyboard)( "name %p\n", name );
1138 if (!name)
1140 SetLastError( ERROR_NOACCESS );
1141 return FALSE;
1144 if (info->kbd_layout_id)
1146 swprintf( name, KL_NAMELENGTH, L"%08X", info->kbd_layout_id );
1147 return TRUE;
1150 layout = GetKeyboardLayout( 0 );
1151 tmp = HandleToUlong( layout );
1152 if (HIWORD( tmp ) == LOWORD( tmp )) tmp = LOWORD( tmp );
1153 swprintf( name, KL_NAMELENGTH, L"%08X", tmp );
1155 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Keyboard Layouts", &hkey ))
1157 while (!RegEnumKeyW( hkey, i++, klid, ARRAY_SIZE(klid) ))
1159 value_size = sizeof(value);
1160 if (!RegGetValueW( hkey, klid, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size ))
1161 tmp = 0xf000 | (wcstoul( value, NULL, 16 ) & 0xfff);
1162 else
1163 tmp = wcstoul( klid, NULL, 16 );
1165 if (HIWORD( layout ) == tmp)
1167 lstrcpynW( name, klid, KL_NAMELENGTH );
1168 break;
1171 RegCloseKey( hkey );
1174 info->kbd_layout_id = wcstoul( name, NULL, 16 );
1176 TRACE_(keyboard)( "ret %s\n", debugstr_w( name ) );
1177 return TRUE;
1180 /****************************************************************************
1181 * GetKeyNameTextA (USER32.@)
1183 INT WINAPI GetKeyNameTextA(LONG lParam, LPSTR lpBuffer, INT nSize)
1185 WCHAR buf[256];
1186 INT ret;
1188 if (!nSize || !GetKeyNameTextW(lParam, buf, 256))
1190 lpBuffer[0] = 0;
1191 return 0;
1193 ret = WideCharToMultiByte(CP_ACP, 0, buf, -1, lpBuffer, nSize, NULL, NULL);
1194 if (!ret && nSize)
1196 ret = nSize - 1;
1197 lpBuffer[ret] = 0;
1199 else ret--;
1201 return ret;
1204 /****************************************************************************
1205 * GetKeyNameTextW (USER32.@)
1207 INT WINAPI GetKeyNameTextW( LONG lparam, LPWSTR buffer, INT size )
1209 INT code = ((lparam >> 16) & 0x1ff), vkey, len;
1210 UINT vsc2vk_size, vscname_size;
1211 const WCHAR *const *vscname;
1212 const UINT *vsc2vk;
1213 WCHAR tmp[2];
1215 TRACE_(keyboard)( "lparam %d, buffer %p, size %d.\n", lparam, buffer, size );
1217 if (!buffer || !size) return 0;
1218 if ((len = USER_Driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len;
1220 /* FIXME: English keyboard layout specific */
1222 vsc2vk = kbd_en_vsc2vk;
1223 vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk);
1224 vscname = kbd_en_vscname;
1225 vscname_size = ARRAYSIZE(kbd_en_vscname);
1227 if (lparam & 0x2000000)
1229 switch ((vkey = vsc2vk[code]))
1231 case VK_RSHIFT:
1232 case VK_RCONTROL:
1233 case VK_RMENU:
1234 for (code = 0; code < vsc2vk_size; ++code)
1235 if (vsc2vk[code] == (vkey - 1)) break;
1236 break;
1240 if (code >= vscname_size) buffer[0] = 0;
1241 else if (vscname[code]) lstrcpynW( buffer, vscname[code], size );
1242 else
1244 vkey = MapVirtualKeyW( code & 0xff, MAPVK_VSC_TO_VK );
1245 tmp[0] = MapVirtualKeyW( vkey, MAPVK_VK_TO_CHAR );
1246 tmp[1] = 0;
1247 lstrcpynW( buffer, tmp, size );
1249 len = wcslen( buffer );
1251 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(buffer) );
1252 return len;
1255 /****************************************************************************
1256 * ToUnicode (USER32.@)
1258 INT WINAPI ToUnicode(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1259 LPWSTR lpwStr, int size, UINT flags)
1261 return ToUnicodeEx(virtKey, scanCode, lpKeyState, lpwStr, size, flags, GetKeyboardLayout(0));
1264 /****************************************************************************
1265 * ToUnicodeEx (USER32.@)
1267 INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
1268 WCHAR *str, int size, UINT flags, HKL layout )
1270 BOOL shift, ctrl, alt, numlock;
1271 WCHAR buffer[2];
1272 INT len;
1274 TRACE_(keyboard)( "virt %u, scan %u, state %p, str %p, size %d, flags %x, layout %p.\n",
1275 virt, scan, state, str, size, flags, layout );
1277 if (!state) return 0;
1278 if ((len = USER_Driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1) return len;
1280 alt = state[VK_MENU] & 0x80;
1281 shift = state[VK_SHIFT] & 0x80;
1282 ctrl = state[VK_CONTROL] & 0x80;
1283 numlock = state[VK_NUMLOCK] & 0x01;
1285 /* FIXME: English keyboard layout specific */
1287 if (scan & 0x8000) buffer[0] = 0; /* key up */
1288 else if (virt == VK_ESCAPE) buffer[0] = VK_ESCAPE;
1289 else if (!ctrl)
1291 switch (virt)
1293 case VK_BACK: buffer[0] = '\b'; break;
1294 case VK_OEM_1: buffer[0] = shift ? ':' : ';'; break;
1295 case VK_OEM_2: buffer[0] = shift ? '?' : '/'; break;
1296 case VK_OEM_3: buffer[0] = shift ? '~' : '`'; break;
1297 case VK_OEM_4: buffer[0] = shift ? '{' : '['; break;
1298 case VK_OEM_5: buffer[0] = shift ? '|' : '\\'; break;
1299 case VK_OEM_6: buffer[0] = shift ? '}' : ']'; break;
1300 case VK_OEM_7: buffer[0] = shift ? '"' : '\''; break;
1301 case VK_OEM_COMMA: buffer[0] = shift ? '<' : ','; break;
1302 case VK_OEM_MINUS: buffer[0] = shift ? '_' : '-'; break;
1303 case VK_OEM_PERIOD: buffer[0] = shift ? '>' : '.'; break;
1304 case VK_OEM_PLUS: buffer[0] = shift ? '+' : '='; break;
1305 case VK_RETURN: buffer[0] = '\r'; break;
1306 case VK_SPACE: buffer[0] = ' '; break;
1307 case VK_TAB: buffer[0] = '\t'; break;
1308 case VK_MULTIPLY: buffer[0] = '*'; break;
1309 case VK_ADD: buffer[0] = '+'; break;
1310 case VK_SUBTRACT: buffer[0] = '-'; break;
1311 case VK_DIVIDE: buffer[0] = '/'; break;
1312 default:
1313 if (virt >= '0' && virt <= '9')
1314 buffer[0] = shift ? ")!@#$%^&*("[virt - '0'] : virt;
1315 else if (virt >= 'A' && virt <= 'Z')
1316 buffer[0] = shift || (state[VK_CAPITAL] & 0x01) ? virt : virt + 'a' - 'A';
1317 else if (virt >= VK_NUMPAD0 && virt <= VK_NUMPAD9 && numlock && !shift)
1318 buffer[0] = '0' + virt - VK_NUMPAD0;
1319 else if (virt == VK_DECIMAL && numlock && !shift)
1320 buffer[0] = '.';
1321 else
1322 buffer[0] = 0;
1323 break;
1326 else if (!alt) /* Control codes */
1328 switch (virt)
1330 case VK_OEM_4: buffer[0] = 0x1b; break;
1331 case VK_OEM_5: buffer[0] = 0x1c; break;
1332 case VK_OEM_6: buffer[0] = 0x1d; break;
1333 case '6': buffer[0] = shift ? 0x1e : 0; break;
1334 case VK_OEM_MINUS: buffer[0] = shift ? 0x1f : 0; break;
1335 case VK_BACK: buffer[0] = 0x7f; break;
1336 case VK_RETURN: buffer[0] = shift ? 0 : '\n'; break;
1337 case '2': buffer[0] = shift ? 0xffff : 0xf000; break;
1338 case VK_SPACE: buffer[0] = ' '; break;
1339 default:
1340 if (virt >= 'A' && virt <= 'Z') buffer[0] = virt - 'A' + 1;
1341 else buffer[0] = 0;
1342 break;
1345 else buffer[0] = 0;
1346 buffer[1] = 0;
1347 len = wcslen( buffer );
1348 if (buffer[0] == 0xffff) buffer[0] = 0;
1349 lstrcpynW( str, buffer, size );
1351 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(str) );
1352 return len;
1355 /****************************************************************************
1356 * ToAscii (USER32.@)
1358 INT WINAPI ToAscii( UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1359 LPWORD lpChar, UINT flags )
1361 return ToAsciiEx(virtKey, scanCode, lpKeyState, lpChar, flags, GetKeyboardLayout(0));
1364 /****************************************************************************
1365 * ToAsciiEx (USER32.@)
1367 INT WINAPI ToAsciiEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1368 LPWORD lpChar, UINT flags, HKL dwhkl )
1370 WCHAR uni_chars[2];
1371 INT ret, n_ret;
1373 ret = ToUnicodeEx(virtKey, scanCode, lpKeyState, uni_chars, 2, flags, dwhkl);
1374 if (ret < 0) n_ret = 1; /* FIXME: make ToUnicode return 2 for dead chars */
1375 else n_ret = ret;
1376 WideCharToMultiByte(CP_ACP, 0, uni_chars, n_ret, (LPSTR)lpChar, 2, NULL, NULL);
1377 return ret;
1380 /**********************************************************************
1381 * ActivateKeyboardLayout (USER32.@)
1383 HKL WINAPI ActivateKeyboardLayout( HKL layout, UINT flags )
1385 struct user_thread_info *info = get_user_thread_info();
1386 HKL old_layout;
1388 TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags );
1390 if (flags) FIXME_(keyboard)( "flags %x not supported\n", flags );
1392 if (layout == (HKL)HKL_NEXT || layout == (HKL)HKL_PREV)
1394 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1395 FIXME_(keyboard)( "HKL_NEXT and HKL_PREV not supported\n" );
1396 return 0;
1399 if (!USER_Driver->pActivateKeyboardLayout( layout, flags ))
1400 return 0;
1402 old_layout = info->kbd_layout;
1403 info->kbd_layout = layout;
1404 if (old_layout != layout) info->kbd_layout_id = 0;
1406 if (!old_layout) return get_locale_kbd_layout();
1407 return old_layout;
1410 /**********************************************************************
1411 * BlockInput (USER32.@)
1413 BOOL WINAPI BlockInput(BOOL fBlockIt)
1415 FIXME_(keyboard)("(%d): stub\n", fBlockIt);
1416 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1418 return FALSE;
1421 /***********************************************************************
1422 * GetKeyboardLayoutList (USER32.@)
1424 * Return number of values available if either input parm is
1425 * 0, per MS documentation.
1427 UINT WINAPI GetKeyboardLayoutList( INT size, HKL *layouts )
1429 WCHAR klid[KL_NAMELENGTH], value[5];
1430 DWORD value_size, count, tmp, i = 0;
1431 HKEY hkey;
1432 HKL layout;
1434 TRACE_(keyboard)( "size %d, layouts %p.\n", size, layouts );
1436 if ((count = USER_Driver->pGetKeyboardLayoutList( size, layouts )) != ~0) return count;
1438 layout = get_locale_kbd_layout();
1439 count = 0;
1441 count++;
1442 if (size && layouts)
1444 layouts[count - 1] = layout;
1445 if (count == size) return count;
1448 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Keyboard Layouts", &hkey ))
1450 while (!RegEnumKeyW( hkey, i++, klid, ARRAY_SIZE(klid) ))
1452 tmp = wcstoul( klid, NULL, 16 );
1453 value_size = sizeof(value);
1454 if (!RegGetValueW( hkey, klid, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size ))
1455 tmp = MAKELONG( LOWORD( tmp ), 0xf000 | (wcstoul( value, NULL, 16 ) & 0xfff) );
1457 if (layout == UlongToHandle( tmp )) continue;
1459 count++;
1460 if (size && layouts)
1462 layouts[count - 1] = UlongToHandle( tmp );
1463 if (count == size) break;
1466 RegCloseKey( hkey );
1469 return count;
1473 /***********************************************************************
1474 * RegisterHotKey (USER32.@)
1476 BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
1478 BOOL ret;
1479 int replaced=0;
1481 TRACE_(keyboard)("(%p,%d,0x%08x,%X)\n",hwnd,id,modifiers,vk);
1483 if ((hwnd == NULL || WIN_IsCurrentThread(hwnd)) &&
1484 !USER_Driver->pRegisterHotKey(hwnd, modifiers, vk))
1485 return FALSE;
1487 SERVER_START_REQ( register_hotkey )
1489 req->window = wine_server_user_handle( hwnd );
1490 req->id = id;
1491 req->flags = modifiers;
1492 req->vkey = vk;
1493 if ((ret = !wine_server_call_err( req )))
1495 replaced = reply->replaced;
1496 modifiers = reply->flags;
1497 vk = reply->vkey;
1500 SERVER_END_REQ;
1502 if (ret && replaced)
1503 USER_Driver->pUnregisterHotKey(hwnd, modifiers, vk);
1505 return ret;
1508 /***********************************************************************
1509 * UnregisterHotKey (USER32.@)
1511 BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id)
1513 BOOL ret;
1514 UINT modifiers, vk;
1516 TRACE_(keyboard)("(%p,%d)\n",hwnd,id);
1518 SERVER_START_REQ( unregister_hotkey )
1520 req->window = wine_server_user_handle( hwnd );
1521 req->id = id;
1522 if ((ret = !wine_server_call_err( req )))
1524 modifiers = reply->flags;
1525 vk = reply->vkey;
1528 SERVER_END_REQ;
1530 if (ret)
1531 USER_Driver->pUnregisterHotKey(hwnd, modifiers, vk);
1533 return ret;
1536 /***********************************************************************
1537 * LoadKeyboardLayoutW (USER32.@)
1539 HKL WINAPI LoadKeyboardLayoutW( const WCHAR *name, UINT flags )
1541 WCHAR layout_path[MAX_PATH], value[5];
1542 DWORD value_size, tmp;
1543 HKEY hkey;
1544 HKL layout;
1546 FIXME_(keyboard)( "name %s, flags %x, semi-stub!\n", debugstr_w( name ), flags );
1548 tmp = wcstoul( name, NULL, 16 );
1549 if (HIWORD( tmp )) layout = UlongToHandle( tmp );
1550 else layout = UlongToHandle( MAKELONG( LOWORD( tmp ), LOWORD( tmp ) ) );
1552 wcscpy( layout_path, L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\" );
1553 wcscat( layout_path, name );
1555 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, layout_path, &hkey ))
1557 value_size = sizeof(value);
1558 if (!RegGetValueW( hkey, NULL, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size ))
1559 layout = UlongToHandle( MAKELONG( LOWORD( tmp ), 0xf000 | (wcstoul( value, NULL, 16 ) & 0xfff) ) );
1561 RegCloseKey( hkey );
1564 if ((flags & KLF_ACTIVATE) && ActivateKeyboardLayout( layout, 0 )) return layout;
1566 /* FIXME: semi-stub: returning default layout */
1567 return get_locale_kbd_layout();
1570 /***********************************************************************
1571 * LoadKeyboardLayoutA (USER32.@)
1573 HKL WINAPI LoadKeyboardLayoutA(LPCSTR pwszKLID, UINT Flags)
1575 HKL ret;
1576 UNICODE_STRING pwszKLIDW;
1578 if (pwszKLID) RtlCreateUnicodeStringFromAsciiz(&pwszKLIDW, pwszKLID);
1579 else pwszKLIDW.Buffer = NULL;
1581 ret = LoadKeyboardLayoutW(pwszKLIDW.Buffer, Flags);
1582 RtlFreeUnicodeString(&pwszKLIDW);
1583 return ret;
1587 /***********************************************************************
1588 * UnloadKeyboardLayout (USER32.@)
1590 BOOL WINAPI UnloadKeyboardLayout( HKL layout )
1592 FIXME_(keyboard)( "layout %p, stub!\n", layout );
1593 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1594 return FALSE;
1597 typedef struct __TRACKINGLIST {
1598 TRACKMOUSEEVENT tme;
1599 POINT pos; /* center of hover rectangle */
1600 } _TRACKINGLIST;
1602 /* FIXME: move tracking stuff into a per thread data */
1603 static _TRACKINGLIST tracking_info;
1604 static UINT_PTR timer;
1606 static void check_mouse_leave(HWND hwnd, int hittest)
1608 if (tracking_info.tme.hwndTrack != hwnd)
1610 if (tracking_info.tme.dwFlags & TME_NONCLIENT)
1611 PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0);
1612 else
1613 PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
1615 /* remove the TME_LEAVE flag */
1616 tracking_info.tme.dwFlags &= ~TME_LEAVE;
1618 else
1620 if (hittest == HTCLIENT)
1622 if (tracking_info.tme.dwFlags & TME_NONCLIENT)
1624 PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0);
1625 /* remove the TME_LEAVE flag */
1626 tracking_info.tme.dwFlags &= ~TME_LEAVE;
1629 else
1631 if (!(tracking_info.tme.dwFlags & TME_NONCLIENT))
1633 PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
1634 /* remove the TME_LEAVE flag */
1635 tracking_info.tme.dwFlags &= ~TME_LEAVE;
1641 static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent,
1642 DWORD dwTime)
1644 POINT pos;
1645 INT hoverwidth = 0, hoverheight = 0, hittest;
1647 TRACE("hwnd %p, msg %04x, id %04lx, time %u\n", hwnd, uMsg, idEvent, dwTime);
1649 GetCursorPos(&pos);
1650 hwnd = WINPOS_WindowFromPoint(hwnd, pos, &hittest);
1652 TRACE("point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest);
1654 SystemParametersInfoW(SPI_GETMOUSEHOVERWIDTH, 0, &hoverwidth, 0);
1655 SystemParametersInfoW(SPI_GETMOUSEHOVERHEIGHT, 0, &hoverheight, 0);
1657 TRACE("tracked pos %s, current pos %s, hover width %d, hover height %d\n",
1658 wine_dbgstr_point(&tracking_info.pos), wine_dbgstr_point(&pos),
1659 hoverwidth, hoverheight);
1661 /* see if this tracking event is looking for TME_LEAVE and that the */
1662 /* mouse has left the window */
1663 if (tracking_info.tme.dwFlags & TME_LEAVE)
1665 check_mouse_leave(hwnd, hittest);
1668 if (tracking_info.tme.hwndTrack != hwnd)
1670 /* mouse is gone, stop tracking mouse hover */
1671 tracking_info.tme.dwFlags &= ~TME_HOVER;
1674 /* see if we are tracking hovering for this hwnd */
1675 if (tracking_info.tme.dwFlags & TME_HOVER)
1677 /* has the cursor moved outside the rectangle centered around pos? */
1678 if ((abs(pos.x - tracking_info.pos.x) > (hoverwidth / 2)) ||
1679 (abs(pos.y - tracking_info.pos.y) > (hoverheight / 2)))
1681 /* record this new position as the current position */
1682 tracking_info.pos = pos;
1684 else
1686 if (hittest == HTCLIENT)
1688 ScreenToClient(hwnd, &pos);
1689 TRACE("client cursor pos %s\n", wine_dbgstr_point(&pos));
1691 PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSEHOVER,
1692 get_key_state(), MAKELPARAM( pos.x, pos.y ));
1694 else
1696 if (tracking_info.tme.dwFlags & TME_NONCLIENT)
1697 PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSEHOVER,
1698 hittest, MAKELPARAM( pos.x, pos.y ));
1701 /* stop tracking mouse hover */
1702 tracking_info.tme.dwFlags &= ~TME_HOVER;
1706 /* stop the timer if the tracking list is empty */
1707 if (!(tracking_info.tme.dwFlags & (TME_HOVER | TME_LEAVE)))
1709 KillSystemTimer(tracking_info.tme.hwndTrack, timer);
1710 timer = 0;
1711 tracking_info.tme.hwndTrack = 0;
1712 tracking_info.tme.dwFlags = 0;
1713 tracking_info.tme.dwHoverTime = 0;
1718 /***********************************************************************
1719 * TrackMouseEvent [USER32]
1721 * Requests notification of mouse events
1723 * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
1724 * to the hwnd specified in the ptme structure. After the event message
1725 * is posted to the hwnd, the entry in the queue is removed.
1727 * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
1728 * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
1729 * immediately and the TME_LEAVE flag being ignored.
1731 * PARAMS
1732 * ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
1734 * RETURNS
1735 * Success: non-zero
1736 * Failure: zero
1740 BOOL WINAPI
1741 TrackMouseEvent (TRACKMOUSEEVENT *ptme)
1743 HWND hwnd;
1744 POINT pos;
1745 DWORD hover_time;
1746 INT hittest;
1748 TRACE("%x, %x, %p, %u\n", ptme->cbSize, ptme->dwFlags, ptme->hwndTrack, ptme->dwHoverTime);
1750 if (ptme->cbSize != sizeof(TRACKMOUSEEVENT)) {
1751 WARN("wrong TRACKMOUSEEVENT size from app\n");
1752 SetLastError(ERROR_INVALID_PARAMETER);
1753 return FALSE;
1756 /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */
1757 if (ptme->dwFlags & TME_QUERY )
1759 *ptme = tracking_info.tme;
1760 /* set cbSize in the case it's not initialized yet */
1761 ptme->cbSize = sizeof(TRACKMOUSEEVENT);
1763 return TRUE; /* return here, TME_QUERY is retrieving information */
1766 if (!IsWindow(ptme->hwndTrack))
1768 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
1769 return FALSE;
1772 hover_time = (ptme->dwFlags & TME_HOVER) ? ptme->dwHoverTime : HOVER_DEFAULT;
1774 /* if HOVER_DEFAULT was specified replace this with the system's current value.
1775 * TME_LEAVE doesn't need to specify hover time so use default */
1776 if (hover_time == HOVER_DEFAULT || hover_time == 0)
1777 SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hover_time, 0);
1779 GetCursorPos(&pos);
1780 hwnd = WINPOS_WindowFromPoint(ptme->hwndTrack, pos, &hittest);
1781 TRACE("point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest);
1783 if (ptme->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT))
1784 FIXME("Unknown flag(s) %08x\n", ptme->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT));
1786 if (ptme->dwFlags & TME_CANCEL)
1788 if (tracking_info.tme.hwndTrack == ptme->hwndTrack)
1790 tracking_info.tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
1792 /* if we aren't tracking on hover or leave remove this entry */
1793 if (!(tracking_info.tme.dwFlags & (TME_HOVER | TME_LEAVE)))
1795 KillSystemTimer(tracking_info.tme.hwndTrack, timer);
1796 timer = 0;
1797 tracking_info.tme.hwndTrack = 0;
1798 tracking_info.tme.dwFlags = 0;
1799 tracking_info.tme.dwHoverTime = 0;
1802 } else {
1803 /* In our implementation it's possible that another window will receive a
1804 * WM_MOUSEMOVE and call TrackMouseEvent before TrackMouseEventProc is
1805 * called. In such a situation post the WM_MOUSELEAVE now */
1806 if (tracking_info.tme.dwFlags & TME_LEAVE && tracking_info.tme.hwndTrack != NULL)
1807 check_mouse_leave(hwnd, hittest);
1809 if (timer)
1811 KillSystemTimer(tracking_info.tme.hwndTrack, timer);
1812 timer = 0;
1813 tracking_info.tme.hwndTrack = 0;
1814 tracking_info.tme.dwFlags = 0;
1815 tracking_info.tme.dwHoverTime = 0;
1818 if (ptme->hwndTrack == hwnd)
1820 /* Adding new mouse event to the tracking list */
1821 tracking_info.tme = *ptme;
1822 tracking_info.tme.dwHoverTime = hover_time;
1824 /* Initialize HoverInfo variables even if not hover tracking */
1825 tracking_info.pos = pos;
1827 timer = SetSystemTimer(tracking_info.tme.hwndTrack, (UINT_PTR)&tracking_info.tme, hover_time, TrackMouseEventProc);
1831 return TRUE;
1834 /***********************************************************************
1835 * GetMouseMovePointsEx [USER32]
1837 * RETURNS
1838 * Success: count of point set in the buffer
1839 * Failure: -1
1841 int WINAPI GetMouseMovePointsEx( UINT size, LPMOUSEMOVEPOINT ptin, LPMOUSEMOVEPOINT ptout, int count, DWORD resolution )
1843 cursor_pos_t *pos, positions[64];
1844 int copied;
1845 unsigned int i;
1848 TRACE( "%d, %p, %p, %d, %d\n", size, ptin, ptout, count, resolution );
1850 if ((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > ARRAY_SIZE( positions )))
1852 SetLastError( ERROR_INVALID_PARAMETER );
1853 return -1;
1856 if (!ptin || (!ptout && count))
1858 SetLastError( ERROR_NOACCESS );
1859 return -1;
1862 if (resolution != GMMP_USE_DISPLAY_POINTS)
1864 FIXME( "only GMMP_USE_DISPLAY_POINTS is supported for now\n" );
1865 SetLastError( ERROR_POINT_NOT_FOUND );
1866 return -1;
1869 SERVER_START_REQ( get_cursor_history )
1871 wine_server_set_reply( req, &positions, sizeof(positions) );
1872 if (wine_server_call_err( req )) return -1;
1874 SERVER_END_REQ;
1876 for (i = 0; i < ARRAY_SIZE( positions ); i++)
1878 pos = &positions[i];
1879 if (ptin->x == pos->x && ptin->y == pos->y && (!ptin->time || ptin->time == pos->time))
1880 break;
1883 if (i == ARRAY_SIZE( positions ))
1885 SetLastError( ERROR_POINT_NOT_FOUND );
1886 return -1;
1889 for (copied = 0; copied < count && i < ARRAY_SIZE( positions ); copied++, i++)
1891 pos = &positions[i];
1892 ptout[copied].x = pos->x;
1893 ptout[copied].y = pos->y;
1894 ptout[copied].time = pos->time;
1895 ptout[copied].dwExtraInfo = pos->info;
1898 return copied;
1901 /***********************************************************************
1902 * EnableMouseInPointer (USER32.@)
1904 BOOL WINAPI EnableMouseInPointer(BOOL enable)
1906 FIXME("(%#x) stub\n", enable);
1908 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1909 return FALSE;
1912 static DWORD CALLBACK devnotify_window_callback(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header)
1914 SendMessageTimeoutW(handle, WM_DEVICECHANGE, flags, (LPARAM)header, SMTO_ABORTIFHUNG, 2000, NULL);
1915 return 0;
1918 static DWORD CALLBACK devnotify_service_callback(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header)
1920 FIXME("Support for service handles is not yet implemented!\n");
1921 return 0;
1924 struct device_notification_details
1926 DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header);
1927 HANDLE handle;
1930 extern HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details,
1931 void *filter, DWORD flags );
1932 extern BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle );
1934 /***********************************************************************
1935 * RegisterDeviceNotificationA (USER32.@)
1937 * See RegisterDeviceNotificationW.
1939 HDEVNOTIFY WINAPI RegisterDeviceNotificationA(HANDLE hRecipient, LPVOID pNotificationFilter, DWORD dwFlags)
1941 TRACE("(hwnd=%p, filter=%p,flags=0x%08x)\n",
1942 hRecipient,pNotificationFilter,dwFlags);
1943 if (pNotificationFilter)
1944 FIXME("The notification filter will requires an A->W when filter support is implemented\n");
1945 return RegisterDeviceNotificationW(hRecipient, pNotificationFilter, dwFlags);
1948 /***********************************************************************
1949 * RegisterDeviceNotificationW (USER32.@)
1951 HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWORD flags )
1953 struct device_notification_details details;
1955 TRACE("handle %p, filter %p, flags %#x\n", handle, filter, flags);
1957 if (flags & ~(DEVICE_NOTIFY_SERVICE_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES))
1958 FIXME("unhandled flags %#x\n", flags);
1960 details.handle = handle;
1962 if (flags & DEVICE_NOTIFY_SERVICE_HANDLE)
1963 details.cb = devnotify_service_callback;
1964 else
1965 details.cb = devnotify_window_callback;
1967 return I_ScRegisterDeviceNotification( &details, filter, 0 );
1970 /***********************************************************************
1971 * UnregisterDeviceNotification (USER32.@)
1973 BOOL WINAPI UnregisterDeviceNotification( HDEVNOTIFY handle )
1975 TRACE("%p\n", handle);
1977 return I_ScUnregisterDeviceNotification( handle );