comctl32/tests: Flush events before testing edit control IME messages.
[wine.git] / dlls / win32u / input.c
blob5ba7d151f571c25e9b27b8b99cabb872f3f059e9
1 /*
2 * USER Input processing
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1993 David Metcalfe
6 * Copyright 1996 Albrecht Kleine
7 * Copyright 1996 Frans van Dorsselaer
8 * Copyright 1997 David Faure
9 * Copyright 1998 Morten Welinder
10 * Copyright 1998 Ulrich Weigand
11 * Copyright 2001 Eric Pouech
12 * Copyright 2002 Alexandre Julliard
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #if 0
30 #pragma makedep unix
31 #endif
33 #include "ntstatus.h"
34 #define WIN32_NO_STATUS
35 #include "win32u_private.h"
36 #include "ntuser_private.h"
37 #include "wine/server.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(win);
41 WINE_DECLARE_DEBUG_CHANNEL(keyboard);
43 static const WCHAR keyboard_layouts_keyW[] =
45 '\\','R','e','g','i','s','t','r','y',
46 '\\','M','a','c','h','i','n','e',
47 '\\','S','y','s','t','e','m',
48 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
49 '\\','C','o','n','t','r','o','l',
50 '\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s'
54 LONG global_key_state_counter = 0;
56 /**********************************************************************
57 * NtUserAttachThreadInput (win32u.@)
59 BOOL WINAPI NtUserAttachThreadInput( DWORD from, DWORD to, BOOL attach )
61 BOOL ret;
63 SERVER_START_REQ( attach_thread_input )
65 req->tid_from = from;
66 req->tid_to = to;
67 req->attach = attach;
68 ret = !wine_server_call_err( req );
70 SERVER_END_REQ;
71 return ret;
74 /***********************************************************************
75 * __wine_send_input (win32u.@)
77 * Internal SendInput function to allow the graphics driver to inject real events.
79 BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput )
81 return set_ntstatus( send_hardware_message( hwnd, input, rawinput, 0 ));
84 /***********************************************************************
85 * update_mouse_coords
87 * Helper for NtUserSendInput.
89 static void update_mouse_coords( INPUT *input )
91 if (!(input->mi.dwFlags & MOUSEEVENTF_MOVE)) return;
93 if (input->mi.dwFlags & MOUSEEVENTF_ABSOLUTE)
95 RECT rc;
97 if (input->mi.dwFlags & MOUSEEVENTF_VIRTUALDESK)
98 rc = get_virtual_screen_rect( 0 );
99 else
100 rc = get_primary_monitor_rect( 0 );
102 input->mi.dx = rc.left + ((input->mi.dx * (rc.right - rc.left)) >> 16);
103 input->mi.dy = rc.top + ((input->mi.dy * (rc.bottom - rc.top)) >> 16);
105 else
107 int accel[3];
109 /* dx and dy can be negative numbers for relative movements */
110 NtUserSystemParametersInfo( SPI_GETMOUSE, 0, accel, 0 );
112 if (!accel[2]) return;
114 if (abs( input->mi.dx ) > accel[0])
116 input->mi.dx *= 2;
117 if (abs( input->mi.dx ) > accel[1] && accel[2] == 2) input->mi.dx *= 2;
119 if (abs(input->mi.dy) > accel[0])
121 input->mi.dy *= 2;
122 if (abs( input->mi.dy ) > accel[1] && accel[2] == 2) input->mi.dy *= 2;
127 /***********************************************************************
128 * NtUserSendInput (win32u.@)
130 UINT WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size )
132 UINT i;
133 NTSTATUS status = STATUS_SUCCESS;
135 if (size != sizeof(INPUT))
137 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
138 return 0;
141 if (!count)
143 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
144 return 0;
147 if (!inputs)
149 RtlSetLastWin32Error( ERROR_NOACCESS );
150 return 0;
153 for (i = 0; i < count; i++)
155 INPUT input = inputs[i];
156 switch (input.type)
158 case INPUT_MOUSE:
159 /* we need to update the coordinates to what the server expects */
160 update_mouse_coords( &input );
161 /* fallthrough */
162 case INPUT_KEYBOARD:
163 status = send_hardware_message( 0, &input, NULL, SEND_HWMSG_INJECTED );
164 break;
165 case INPUT_HARDWARE:
166 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
167 return 0;
170 if (status)
172 RtlSetLastWin32Error( RtlNtStatusToDosError(status) );
173 break;
177 return i;
180 /***********************************************************************
181 * NtUserSetCursorPos (win32u.@)
183 BOOL WINAPI NtUserSetCursorPos( INT x, INT y )
185 POINT pt = { x, y };
186 BOOL ret;
187 INT prev_x, prev_y, new_x, new_y;
188 UINT dpi;
190 if ((dpi = get_thread_dpi()))
192 HMONITOR monitor = monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
193 pt = map_dpi_point( pt, dpi, get_monitor_dpi( monitor ));
196 SERVER_START_REQ( set_cursor )
198 req->flags = SET_CURSOR_POS;
199 req->x = pt.x;
200 req->y = pt.y;
201 if ((ret = !wine_server_call( req )))
203 prev_x = reply->prev_x;
204 prev_y = reply->prev_y;
205 new_x = reply->new_x;
206 new_y = reply->new_y;
209 SERVER_END_REQ;
210 if (ret && (prev_x != new_x || prev_y != new_y)) user_driver->pSetCursorPos( new_x, new_y );
211 return ret;
214 /***********************************************************************
215 * get_cursor_pos
217 BOOL get_cursor_pos( POINT *pt )
219 BOOL ret;
220 DWORD last_change;
221 UINT dpi;
223 if (!pt) return FALSE;
225 SERVER_START_REQ( set_cursor )
227 if ((ret = !wine_server_call( req )))
229 pt->x = reply->new_x;
230 pt->y = reply->new_y;
231 last_change = reply->last_change;
234 SERVER_END_REQ;
236 /* query new position from graphics driver if we haven't updated recently */
237 if (ret && NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt );
238 if (ret && (dpi = get_thread_dpi()))
240 HMONITOR monitor = monitor_from_point( *pt, MONITOR_DEFAULTTOPRIMARY, 0 );
241 *pt = map_dpi_point( *pt, get_monitor_dpi( monitor ), dpi );
243 return ret;
246 /***********************************************************************
247 * NtUserGetCursorInfo (win32u.@)
249 BOOL WINAPI NtUserGetCursorInfo( CURSORINFO *info )
251 BOOL ret;
253 if (!info) return FALSE;
255 SERVER_START_REQ( get_thread_input )
257 req->tid = 0;
258 if ((ret = !wine_server_call( req )))
260 info->hCursor = wine_server_ptr_handle( reply->cursor );
261 info->flags = reply->show_count >= 0 ? CURSOR_SHOWING : 0;
264 SERVER_END_REQ;
265 get_cursor_pos( &info->ptScreenPos );
266 return ret;
269 static void check_for_events( UINT flags )
271 LARGE_INTEGER zero = { .QuadPart = 0 };
272 if (user_driver->pMsgWaitForMultipleObjectsEx( 0, NULL, &zero, flags, 0 ) == WAIT_TIMEOUT)
273 flush_window_surfaces( TRUE );
276 /**********************************************************************
277 * GetAsyncKeyState (win32u.@)
279 SHORT WINAPI NtUserGetAsyncKeyState( INT key )
281 struct user_key_state_info *key_state_info = get_user_thread_info()->key_state;
282 INT counter = global_key_state_counter;
283 BYTE prev_key_state;
284 SHORT ret;
286 if (key < 0 || key >= 256) return 0;
288 check_for_events( QS_INPUT );
290 if (key_state_info && !(key_state_info->state[key] & 0xc0) &&
291 key_state_info->counter == counter && NtGetTickCount() - key_state_info->time < 50)
293 /* use cached value */
294 return 0;
296 else if (!key_state_info)
298 key_state_info = calloc( 1, sizeof(*key_state_info) );
299 get_user_thread_info()->key_state = key_state_info;
302 ret = 0;
303 SERVER_START_REQ( get_key_state )
305 req->async = 1;
306 req->key = key;
307 if (key_state_info)
309 prev_key_state = key_state_info->state[key];
310 wine_server_set_reply( req, key_state_info->state, sizeof(key_state_info->state) );
312 if (!wine_server_call( req ))
314 if (reply->state & 0x40) ret |= 0x0001;
315 if (reply->state & 0x80) ret |= 0x8000;
316 if (key_state_info)
318 /* force refreshing the key state cache - some multithreaded programs
319 * (like Adobe Photoshop CS5) expect that changes to the async key state
320 * are also immediately available in other threads. */
321 if (prev_key_state != key_state_info->state[key])
322 counter = InterlockedIncrement( &global_key_state_counter );
324 key_state_info->time = NtGetTickCount();
325 key_state_info->counter = counter;
329 SERVER_END_REQ;
331 return ret;
334 /***********************************************************************
335 * NtUserGetQueueStatus (win32u.@)
337 DWORD WINAPI NtUserGetQueueStatus( UINT flags )
339 DWORD ret;
341 if (flags & ~(QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_SMRESULT))
343 RtlSetLastWin32Error( ERROR_INVALID_FLAGS );
344 return 0;
347 check_for_events( flags );
349 SERVER_START_REQ( get_queue_status )
351 req->clear_bits = flags;
352 wine_server_call( req );
353 ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags );
355 SERVER_END_REQ;
356 return ret;
359 /***********************************************************************
360 * get_input_state
362 DWORD get_input_state(void)
364 DWORD ret;
366 check_for_events( QS_INPUT );
368 SERVER_START_REQ( get_queue_status )
370 req->clear_bits = 0;
371 wine_server_call( req );
372 ret = reply->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
374 SERVER_END_REQ;
375 return ret;
378 /***********************************************************************
379 * get_locale_kbd_layout
381 static HKL get_locale_kbd_layout(void)
383 LCID layout;
384 LANGID langid;
386 /* FIXME:
388 * layout = main_key_tab[kbd_layout].lcid;
390 * Winword uses return value of GetKeyboardLayout as a codepage
391 * to translate ANSI keyboard messages to unicode. But we have
392 * a problem with it: for instance Polish keyboard layout is
393 * identical to the US one, and therefore instead of the Polish
394 * locale id we return the US one.
397 NtQueryDefaultLocale( TRUE, &layout );
400 * Microsoft Office expects this value to be something specific
401 * for Japanese and Korean Windows with an IME the value is 0xe001
402 * We should probably check to see if an IME exists and if so then
403 * set this word properly.
405 langid = PRIMARYLANGID( LANGIDFROMLCID( layout ) );
406 if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
407 layout = MAKELONG( layout, 0xe001 ); /* IME */
408 else
409 layout = MAKELONG( layout, layout );
411 return ULongToHandle( layout );
414 /***********************************************************************
415 * NtUserGetKeyboardLayout (win32u.@)
417 * Device handle for keyboard layout defaulted to
418 * the language id. This is the way Windows default works.
420 HKL WINAPI NtUserGetKeyboardLayout( DWORD thread_id )
422 struct user_thread_info *thread = get_user_thread_info();
423 HKL layout = thread->kbd_layout;
425 if (thread_id && thread_id != GetCurrentThreadId())
426 FIXME( "couldn't return keyboard layout for thread %04x\n", thread_id );
428 if (!layout) return get_locale_kbd_layout();
429 return layout;
432 /**********************************************************************
433 * NtUserGetKeyState (win32u.@)
435 * An application calls the GetKeyState function in response to a
436 * keyboard-input message. This function retrieves the state of the key
437 * at the time the input message was generated.
439 SHORT WINAPI NtUserGetKeyState( INT vkey )
441 SHORT retval = 0;
443 SERVER_START_REQ( get_key_state )
445 req->key = vkey;
446 if (!wine_server_call( req )) retval = (signed char)(reply->state & 0x81);
448 SERVER_END_REQ;
449 TRACE("key (0x%x) -> %x\n", vkey, retval);
450 return retval;
453 /**********************************************************************
454 * NtUserGetKeyboardState (win32u.@)
456 BOOL WINAPI NtUserGetKeyboardState( BYTE *state )
458 BOOL ret;
459 UINT i;
461 TRACE("(%p)\n", state);
463 memset( state, 0, 256 );
464 SERVER_START_REQ( get_key_state )
466 req->key = -1;
467 wine_server_set_reply( req, state, 256 );
468 ret = !wine_server_call_err( req );
469 for (i = 0; i < 256; i++) state[i] &= 0x81;
471 SERVER_END_REQ;
472 return ret;
475 /**********************************************************************
476 * NtUserSetKeyboardState (win32u.@)
478 BOOL WINAPI NtUserSetKeyboardState( BYTE *state )
480 BOOL ret;
482 SERVER_START_REQ( set_key_state )
484 wine_server_add_data( req, state, 256 );
485 ret = !wine_server_call_err( req );
487 SERVER_END_REQ;
488 return ret;
491 /******************************************************************************
492 * NtUserVkKeyScanEx (win32u.@)
494 WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout )
496 WORD shift = 0x100, ctrl = 0x200;
497 SHORT ret;
499 TRACE_(keyboard)( "chr %s, layout %p\n", debugstr_wn(&chr, 1), layout );
501 if ((ret = user_driver->pVkKeyScanEx( chr, layout )) != -256) return ret;
503 /* FIXME: English keyboard layout specific */
505 if (chr == VK_CANCEL || chr == VK_BACK || chr == VK_TAB || chr == VK_RETURN ||
506 chr == VK_ESCAPE || chr == VK_SPACE) ret = chr;
507 else if (chr >= '0' && chr <= '9') ret = chr;
508 else if (chr == ')') ret = shift + '0';
509 else if (chr == '!') ret = shift + '1';
510 else if (chr == '@') ret = shift + '2';
511 else if (chr == '#') ret = shift + '3';
512 else if (chr == '$') ret = shift + '4';
513 else if (chr == '%') ret = shift + '5';
514 else if (chr == '^') ret = shift + '6';
515 else if (chr == '&') ret = shift + '7';
516 else if (chr == '*') ret = shift + '8';
517 else if (chr == '(') ret = shift + '9';
518 else if (chr >= 'a' && chr <= 'z') ret = chr - 'a' + 'A';
519 else if (chr >= 'A' && chr <= 'Z') ret = shift + chr;
520 else if (chr == ';') ret = VK_OEM_1;
521 else if (chr == '=') ret = VK_OEM_PLUS;
522 else if (chr == ',') ret = VK_OEM_COMMA;
523 else if (chr == '-') ret = VK_OEM_MINUS;
524 else if (chr == '.') ret = VK_OEM_PERIOD;
525 else if (chr == '/') ret = VK_OEM_2;
526 else if (chr == '`') ret = VK_OEM_3;
527 else if (chr == '[') ret = VK_OEM_4;
528 else if (chr == '\\') ret = VK_OEM_5;
529 else if (chr == ']') ret = VK_OEM_6;
530 else if (chr == '\'') ret = VK_OEM_7;
531 else if (chr == ':') ret = shift + VK_OEM_1;
532 else if (chr == '+') ret = shift + VK_OEM_PLUS;
533 else if (chr == '<') ret = shift + VK_OEM_COMMA;
534 else if (chr == '_') ret = shift + VK_OEM_MINUS;
535 else if (chr == '>') ret = shift + VK_OEM_PERIOD;
536 else if (chr == '?') ret = shift + VK_OEM_2;
537 else if (chr == '~') ret = shift + VK_OEM_3;
538 else if (chr == '{') ret = shift + VK_OEM_4;
539 else if (chr == '|') ret = shift + VK_OEM_5;
540 else if (chr == '}') ret = shift + VK_OEM_6;
541 else if (chr == '\"') ret = shift + VK_OEM_7;
542 else if (chr == 0x7f) ret = ctrl + VK_BACK;
543 else if (chr == '\n') ret = ctrl + VK_RETURN;
544 else if (chr == 0xf000) ret = ctrl + '2';
545 else if (chr == 0x0000) ret = ctrl + shift + '2';
546 else if (chr >= 0x0001 && chr <= 0x001a) ret = ctrl + 'A' + chr - 1;
547 else if (chr >= 0x001c && chr <= 0x001d) ret = ctrl + VK_OEM_3 + chr;
548 else if (chr == 0x001e) ret = ctrl + shift + '6';
549 else if (chr == 0x001f) ret = ctrl + shift + VK_OEM_MINUS;
550 else ret = -1;
552 TRACE_(keyboard)( "ret %04x\n", ret );
553 return ret;
556 /* English keyboard layout (0x0409) */
557 static const UINT kbd_en_vsc2vk[] =
559 0x00, 0x1b, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0xbd, 0xbb, 0x08, 0x09,
560 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4f, 0x50, 0xdb, 0xdd, 0x0d, 0xa2, 0x41, 0x53,
561 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0xba, 0xde, 0xc0, 0xa0, 0xdc, 0x5a, 0x58, 0x43, 0x56,
562 0x42, 0x4e, 0x4d, 0xbc, 0xbe, 0xbf, 0xa1, 0x6a, 0xa4, 0x20, 0x14, 0x70, 0x71, 0x72, 0x73, 0x74,
563 0x75, 0x76, 0x77, 0x78, 0x79, 0x90, 0x91, 0x24, 0x26, 0x21, 0x6d, 0x25, 0x0c, 0x27, 0x6b, 0x23,
564 0x28, 0x22, 0x2d, 0x2e, 0x2c, 0x00, 0xe2, 0x7a, 0x7b, 0x0c, 0xee, 0xf1, 0xea, 0xf9, 0xf5, 0xf3,
565 0x00, 0x00, 0xfb, 0x2f, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0xed,
566 0x00, 0xe9, 0x00, 0xc1, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x09, 0x00, 0xc2, 0x00,
567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575 /* 0xe000 */
576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x0d, 0xa3, 0x00, 0x00,
578 0xad, 0xb7, 0xb3, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x00,
579 0xaf, 0x00, 0xac, 0x00, 0x00, 0x6f, 0x00, 0x2c, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x24, 0x26, 0x21, 0x00, 0x25, 0x00, 0x27, 0x00, 0x23,
581 0x28, 0x22, 0x2d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x00, 0x5f,
582 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xab, 0xa8, 0xa9, 0xa7, 0xa6, 0xb6, 0xb4, 0xb5, 0x00, 0x00,
583 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
586 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
588 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
591 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592 /* 0xe100 */
593 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
594 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00,
595 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
597 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
604 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
605 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
607 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611 static const UINT kbd_en_vk2char[] =
613 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00,
614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00,
615 ' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
618 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00,
619 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '+', 0x00, '-', '.', '/',
620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
623 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ';', '=', ',', '-', '.', '/',
625 '`', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '[', '\\', ']', '\'', 0x00,
627 0x00, 0x00, '\\', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
631 static const char *kbd_en_vscname[] =
633 0, "Esc", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Backspace", "Tab",
634 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Enter", "Ctrl", 0, 0,
635 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Shift", 0, 0, 0, 0, 0,
636 0, 0, 0, 0, 0, 0, "Right Shift", "Num *", "Alt", "Space", "Caps Lock", "F1", "F2", "F3", "F4", "F5",
637 "F6", "F7", "F8", "F9", "F10", "Pause", "Scroll Lock", "Num 7", "Num 8", "Num 9", "Num -", "Num 4", "Num 5", "Num 6", "Num +", "Num 1",
638 "Num 2", "Num 3", "Num 0", "Num Del", "Sys Req", 0, 0, "F11", "F12", 0, 0, 0, 0, 0, 0, 0,
639 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
640 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "F13", "F14", "F15", "F16",
641 "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", 0, 0, 0, 0, 0, 0, 0, 0,
642 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
643 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
644 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
645 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
646 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
647 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
648 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
649 /* extended */
650 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
651 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Num Enter", "Right Ctrl", 0, 0,
652 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
653 0, 0, 0, 0, 0, "Num /", 0, "Prnt Scrn", "Right Alt", 0, 0, 0, 0, 0, 0, 0,
654 0, 0, 0, 0, 0, "Num Lock", "Break", "Home", "Up", "Page Up", 0, "Left", 0, "Right", 0, "End",
655 "Down", "Page Down", "Insert", "Delete", "<00>", 0, "Help", 0, 0, 0, 0, "Left Windows", "Right Windows", "Application", 0, 0,
656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
661 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
662 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
663 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
664 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
665 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
668 /******************************************************************************
669 * NtUserMapVirtualKeyEx (win32u.@)
671 UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
673 const UINT *vsc2vk, *vk2char;
674 UINT vsc2vk_size, vk2char_size;
675 UINT ret;
677 TRACE_(keyboard)( "code %u, type %u, layout %p.\n", code, type, layout );
679 if ((ret = user_driver->pMapVirtualKeyEx( code, type, layout )) != -1) return ret;
681 /* FIXME: English keyboard layout specific */
683 vsc2vk = kbd_en_vsc2vk;
684 vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk);
685 vk2char = kbd_en_vk2char;
686 vk2char_size = ARRAYSIZE(kbd_en_vk2char);
688 switch (type)
690 case MAPVK_VK_TO_VSC_EX:
691 case MAPVK_VK_TO_VSC:
692 switch (code)
694 case VK_SHIFT: code = VK_LSHIFT; break;
695 case VK_CONTROL: code = VK_LCONTROL; break;
696 case VK_MENU: code = VK_LMENU; break;
697 case VK_NUMPAD0: code = VK_INSERT; break;
698 case VK_NUMPAD1: code = VK_END; break;
699 case VK_NUMPAD2: code = VK_DOWN; break;
700 case VK_NUMPAD3: code = VK_NEXT; break;
701 case VK_NUMPAD4: code = VK_LEFT; break;
702 case VK_NUMPAD5: code = VK_CLEAR; break;
703 case VK_NUMPAD6: code = VK_RIGHT; break;
704 case VK_NUMPAD7: code = VK_HOME; break;
705 case VK_NUMPAD8: code = VK_UP; break;
706 case VK_NUMPAD9: code = VK_PRIOR; break;
707 case VK_DECIMAL: code = VK_DELETE; break;
710 for (ret = 0; ret < vsc2vk_size; ++ret) if (vsc2vk[ret] == code) break;
711 if (ret >= vsc2vk_size) ret = 0;
713 if (type == MAPVK_VK_TO_VSC)
715 if (ret >= 0x200) ret = 0;
716 else ret &= 0xff;
718 else if (ret >= 0x100) ret += 0xdf00;
719 break;
720 case MAPVK_VSC_TO_VK:
721 case MAPVK_VSC_TO_VK_EX:
722 if (code & 0xe000) code -= 0xdf00;
723 if (code >= vsc2vk_size) ret = 0;
724 else ret = vsc2vk[code];
726 if (type == MAPVK_VSC_TO_VK)
728 switch (ret)
730 case VK_LSHIFT: case VK_RSHIFT: ret = VK_SHIFT; break;
731 case VK_LCONTROL: case VK_RCONTROL: ret = VK_CONTROL; break;
732 case VK_LMENU: case VK_RMENU: ret = VK_MENU; break;
735 break;
736 case MAPVK_VK_TO_CHAR:
737 if (code >= vk2char_size) ret = 0;
738 else ret = vk2char[code];
739 break;
740 default:
741 FIXME_(keyboard)( "unknown type %d\n", type );
742 return 0;
745 TRACE_(keyboard)( "returning 0x%04x\n", ret );
746 return ret;
749 /****************************************************************************
750 * NtUserGetKeyNameText (win32u.@)
752 INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
754 INT code = ((lparam >> 16) & 0x1ff), vkey, len;
755 UINT vsc2vk_size, vscname_size;
756 const char *const *vscname;
757 const UINT *vsc2vk;
759 TRACE_(keyboard)( "lparam %d, buffer %p, size %d.\n", lparam, buffer, size );
761 if (!buffer || !size) return 0;
762 if ((len = user_driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len;
764 /* FIXME: English keyboard layout specific */
766 vsc2vk = kbd_en_vsc2vk;
767 vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk);
768 vscname = kbd_en_vscname;
769 vscname_size = ARRAYSIZE(kbd_en_vscname);
771 if (lparam & 0x2000000)
773 switch ((vkey = vsc2vk[code]))
775 case VK_RSHIFT:
776 case VK_RCONTROL:
777 case VK_RMENU:
778 for (code = 0; code < vsc2vk_size; ++code)
779 if (vsc2vk[code] == (vkey - 1)) break;
780 break;
784 if (code < vscname_size)
786 if (vscname[code])
788 len = min( size - 1, strlen(vscname[code]) );
789 ascii_to_unicode( buffer, vscname[code], len );
791 else if (size > 1)
793 HKL hkl = NtUserGetKeyboardLayout( 0 );
794 vkey = NtUserMapVirtualKeyEx( code & 0xff, MAPVK_VSC_TO_VK, hkl );
795 buffer[0] = NtUserMapVirtualKeyEx( vkey, MAPVK_VK_TO_CHAR, hkl );
796 len = 1;
799 buffer[len] = 0;
801 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(buffer) );
802 return len;
805 /****************************************************************************
806 * NtUserToUnicodeEx (win32u.@)
808 INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
809 WCHAR *str, int size, UINT flags, HKL layout )
811 BOOL shift, ctrl, alt, numlock;
812 WCHAR buffer[2];
813 INT len;
815 TRACE_(keyboard)( "virt %u, scan %u, state %p, str %p, size %d, flags %x, layout %p.\n",
816 virt, scan, state, str, size, flags, layout );
818 if (!state) return 0;
819 if ((len = user_driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1)
820 return len;
822 alt = state[VK_MENU] & 0x80;
823 shift = state[VK_SHIFT] & 0x80;
824 ctrl = state[VK_CONTROL] & 0x80;
825 numlock = state[VK_NUMLOCK] & 0x01;
827 /* FIXME: English keyboard layout specific */
829 if (scan & 0x8000) buffer[0] = 0; /* key up */
830 else if (virt == VK_ESCAPE) buffer[0] = VK_ESCAPE;
831 else if (!ctrl)
833 switch (virt)
835 case VK_BACK: buffer[0] = '\b'; break;
836 case VK_OEM_1: buffer[0] = shift ? ':' : ';'; break;
837 case VK_OEM_2: buffer[0] = shift ? '?' : '/'; break;
838 case VK_OEM_3: buffer[0] = shift ? '~' : '`'; break;
839 case VK_OEM_4: buffer[0] = shift ? '{' : '['; break;
840 case VK_OEM_5: buffer[0] = shift ? '|' : '\\'; break;
841 case VK_OEM_6: buffer[0] = shift ? '}' : ']'; break;
842 case VK_OEM_7: buffer[0] = shift ? '"' : '\''; break;
843 case VK_OEM_COMMA: buffer[0] = shift ? '<' : ','; break;
844 case VK_OEM_MINUS: buffer[0] = shift ? '_' : '-'; break;
845 case VK_OEM_PERIOD: buffer[0] = shift ? '>' : '.'; break;
846 case VK_OEM_PLUS: buffer[0] = shift ? '+' : '='; break;
847 case VK_RETURN: buffer[0] = '\r'; break;
848 case VK_SPACE: buffer[0] = ' '; break;
849 case VK_TAB: buffer[0] = '\t'; break;
850 case VK_MULTIPLY: buffer[0] = '*'; break;
851 case VK_ADD: buffer[0] = '+'; break;
852 case VK_SUBTRACT: buffer[0] = '-'; break;
853 case VK_DIVIDE: buffer[0] = '/'; break;
854 default:
855 if (virt >= '0' && virt <= '9')
856 buffer[0] = shift ? ")!@#$%^&*("[virt - '0'] : virt;
857 else if (virt >= 'A' && virt <= 'Z')
858 buffer[0] = shift || (state[VK_CAPITAL] & 0x01) ? virt : virt + 'a' - 'A';
859 else if (virt >= VK_NUMPAD0 && virt <= VK_NUMPAD9 && numlock && !shift)
860 buffer[0] = '0' + virt - VK_NUMPAD0;
861 else if (virt == VK_DECIMAL && numlock && !shift)
862 buffer[0] = '.';
863 else
864 buffer[0] = 0;
865 break;
868 else if (!alt) /* Control codes */
870 switch (virt)
872 case VK_OEM_4: buffer[0] = 0x1b; break;
873 case VK_OEM_5: buffer[0] = 0x1c; break;
874 case VK_OEM_6: buffer[0] = 0x1d; break;
875 case '6': buffer[0] = shift ? 0x1e : 0; break;
876 case VK_OEM_MINUS: buffer[0] = shift ? 0x1f : 0; break;
877 case VK_BACK: buffer[0] = 0x7f; break;
878 case VK_RETURN: buffer[0] = shift ? 0 : '\n'; break;
879 case '2': buffer[0] = shift ? 0xffff : 0xf000; break;
880 case VK_SPACE: buffer[0] = ' '; break;
881 default:
882 if (virt >= 'A' && virt <= 'Z') buffer[0] = virt - 'A' + 1;
883 else buffer[0] = 0;
884 break;
887 else buffer[0] = 0;
888 buffer[1] = 0;
889 len = lstrlenW( buffer );
890 if (buffer[0] == 0xffff) buffer[0] = 0;
891 lstrcpynW( str, buffer, size );
893 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(str) );
894 return len;
897 /**********************************************************************
898 * NtUserActivateKeyboardLayout (win32u.@)
900 HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags )
902 struct user_thread_info *info = get_user_thread_info();
903 HKL old_layout;
905 TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags );
907 if (flags) FIXME_(keyboard)( "flags %x not supported\n", flags );
909 if (layout == (HKL)HKL_NEXT || layout == (HKL)HKL_PREV)
911 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
912 FIXME_(keyboard)( "HKL_NEXT and HKL_PREV not supported\n" );
913 return 0;
916 if (!user_driver->pActivateKeyboardLayout( layout, flags ))
917 return 0;
919 old_layout = info->kbd_layout;
920 info->kbd_layout = layout;
921 if (old_layout != layout) info->kbd_layout_id = 0;
923 if (!old_layout) return get_locale_kbd_layout();
924 return old_layout;
929 /***********************************************************************
930 * NtUserGetKeyboardLayoutList (win32u.@)
932 * Return number of values available if either input parm is
933 * 0, per MS documentation.
935 UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts )
937 char buffer[4096];
938 KEY_NODE_INFORMATION *key_info = (KEY_NODE_INFORMATION *)buffer;
939 KEY_VALUE_PARTIAL_INFORMATION *value_info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
940 DWORD count, tmp, i = 0;
941 HKEY hkey, subkey;
942 HKL layout;
944 TRACE_(keyboard)( "size %d, layouts %p.\n", size, layouts );
946 if ((count = user_driver->pGetKeyboardLayoutList( size, layouts )) != ~0) return count;
948 layout = get_locale_kbd_layout();
949 count = 0;
951 count++;
952 if (size && layouts)
954 layouts[count - 1] = layout;
955 if (count == size) return count;
958 if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
960 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key_info,
961 sizeof(buffer) - sizeof(WCHAR), &tmp ))
963 if (!(subkey = reg_open_key( hkey, key_info->Name, key_info->NameLength ))) continue;
964 key_info->Name[key_info->NameLength / sizeof(WCHAR)] = 0;
965 tmp = wcstoul( key_info->Name, NULL, 16 );
966 if (query_reg_ascii_value( subkey, "Layout Id", value_info, sizeof(buffer) ) &&
967 value_info->Type == REG_SZ)
968 tmp = MAKELONG( LOWORD( tmp ),
969 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff) );
970 NtClose( subkey );
972 if (layout == UlongToHandle( tmp )) continue;
974 count++;
975 if (size && layouts)
977 layouts[count - 1] = UlongToHandle( tmp );
978 if (count == size) break;
981 NtClose( hkey );
984 return count;
987 /****************************************************************************
988 * NtUserGetKeyboardLayoutName (win32u.@)
990 BOOL WINAPI NtUserGetKeyboardLayoutName( WCHAR *name )
992 struct user_thread_info *info = get_user_thread_info();
993 char buffer[4096];
994 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
995 KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
996 WCHAR klid[KL_NAMELENGTH];
997 DWORD tmp, i = 0;
998 HKEY hkey, subkey;
999 HKL layout;
1001 TRACE_(keyboard)( "name %p\n", name );
1003 if (!name)
1005 RtlSetLastWin32Error( ERROR_NOACCESS );
1006 return FALSE;
1009 if (info->kbd_layout_id)
1011 sprintf( buffer, "%08X", info->kbd_layout_id );
1012 asciiz_to_unicode( name, buffer );
1013 return TRUE;
1016 layout = NtUserGetKeyboardLayout( 0 );
1017 tmp = HandleToUlong( layout );
1018 if (HIWORD( tmp ) == LOWORD( tmp )) tmp = LOWORD( tmp );
1019 sprintf( buffer, "%08X", tmp );
1020 asciiz_to_unicode( name, buffer );
1022 if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
1024 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key,
1025 sizeof(buffer) - sizeof(WCHAR), &tmp ))
1027 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1028 memcpy( klid, key->Name, key->NameLength );
1029 klid[key->NameLength / sizeof(WCHAR)] = 0;
1030 if (query_reg_ascii_value( subkey, "Layout Id", value, sizeof(buffer) ) &&
1031 value->Type == REG_SZ)
1032 tmp = 0xf000 | (wcstoul( (const WCHAR *)value->Data, NULL, 16 ) & 0xfff);
1033 else
1034 tmp = wcstoul( klid, NULL, 16 );
1035 NtClose( subkey );
1037 if (HIWORD( layout ) == tmp)
1039 lstrcpynW( name, klid, KL_NAMELENGTH );
1040 break;
1043 NtClose( hkey );
1046 info->kbd_layout_id = wcstoul( name, NULL, 16 );
1048 TRACE_(keyboard)( "ret %s\n", debugstr_w( name ) );
1049 return TRUE;
1052 /***********************************************************************
1053 * NtUserRegisterHotKey (win32u.@)
1055 BOOL WINAPI NtUserRegisterHotKey( HWND hwnd, INT id, UINT modifiers, UINT vk )
1057 BOOL ret;
1058 int replaced = 0;
1060 TRACE_(keyboard)( "(%p,%d,0x%08x,%X)\n", hwnd, id, modifiers, vk );
1062 if ((!hwnd || is_current_thread_window( hwnd )) &&
1063 !user_driver->pRegisterHotKey( hwnd, modifiers, vk ))
1064 return FALSE;
1066 SERVER_START_REQ( register_hotkey )
1068 req->window = wine_server_user_handle( hwnd );
1069 req->id = id;
1070 req->flags = modifiers;
1071 req->vkey = vk;
1072 if ((ret = !wine_server_call_err( req )))
1074 replaced = reply->replaced;
1075 modifiers = reply->flags;
1076 vk = reply->vkey;
1079 SERVER_END_REQ;
1081 if (ret && replaced)
1082 user_driver->pUnregisterHotKey(hwnd, modifiers, vk);
1084 return ret;
1087 /***********************************************************************
1088 * NtUserUnregisterHotKey (win32u.@)
1090 BOOL WINAPI NtUserUnregisterHotKey( HWND hwnd, INT id )
1092 BOOL ret;
1093 UINT modifiers, vk;
1095 TRACE_(keyboard)("(%p,%d)\n",hwnd,id);
1097 SERVER_START_REQ( unregister_hotkey )
1099 req->window = wine_server_user_handle( hwnd );
1100 req->id = id;
1101 if ((ret = !wine_server_call_err( req )))
1103 modifiers = reply->flags;
1104 vk = reply->vkey;
1107 SERVER_END_REQ;
1109 if (ret)
1110 user_driver->pUnregisterHotKey(hwnd, modifiers, vk);
1112 return ret;
1115 /***********************************************************************
1116 * NtUserGetMouseMovePointsEx (win32u.@)
1118 int WINAPI NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout,
1119 int count, DWORD resolution )
1121 cursor_pos_t *pos, positions[64];
1122 int copied;
1123 unsigned int i;
1126 TRACE( "%d, %p, %p, %d, %d\n", size, ptin, ptout, count, resolution );
1128 if ((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > ARRAY_SIZE( positions )))
1130 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1131 return -1;
1134 if (!ptin || (!ptout && count))
1136 RtlSetLastWin32Error( ERROR_NOACCESS );
1137 return -1;
1140 if (resolution != GMMP_USE_DISPLAY_POINTS)
1142 FIXME( "only GMMP_USE_DISPLAY_POINTS is supported for now\n" );
1143 RtlSetLastWin32Error( ERROR_POINT_NOT_FOUND );
1144 return -1;
1147 SERVER_START_REQ( get_cursor_history )
1149 wine_server_set_reply( req, &positions, sizeof(positions) );
1150 if (wine_server_call_err( req )) return -1;
1152 SERVER_END_REQ;
1154 for (i = 0; i < ARRAY_SIZE( positions ); i++)
1156 pos = &positions[i];
1157 if (ptin->x == pos->x && ptin->y == pos->y && (!ptin->time || ptin->time == pos->time))
1158 break;
1161 if (i == ARRAY_SIZE( positions ))
1163 RtlSetLastWin32Error( ERROR_POINT_NOT_FOUND );
1164 return -1;
1167 for (copied = 0; copied < count && i < ARRAY_SIZE( positions ); copied++, i++)
1169 pos = &positions[i];
1170 ptout[copied].x = pos->x;
1171 ptout[copied].y = pos->y;
1172 ptout[copied].time = pos->time;
1173 ptout[copied].dwExtraInfo = pos->info;
1176 return copied;
1179 static WORD get_key_state(void)
1181 WORD ret = 0;
1183 if (get_system_metrics( SM_SWAPBUTTON ))
1185 if (NtUserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_LBUTTON;
1186 if (NtUserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_RBUTTON;
1188 else
1190 if (NtUserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_LBUTTON;
1191 if (NtUserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_RBUTTON;
1193 if (NtUserGetAsyncKeyState(VK_MBUTTON) & 0x80) ret |= MK_MBUTTON;
1194 if (NtUserGetAsyncKeyState(VK_SHIFT) & 0x80) ret |= MK_SHIFT;
1195 if (NtUserGetAsyncKeyState(VK_CONTROL) & 0x80) ret |= MK_CONTROL;
1196 if (NtUserGetAsyncKeyState(VK_XBUTTON1) & 0x80) ret |= MK_XBUTTON1;
1197 if (NtUserGetAsyncKeyState(VK_XBUTTON2) & 0x80) ret |= MK_XBUTTON2;
1198 return ret;
1201 struct tracking_list
1203 TRACKMOUSEEVENT info;
1204 POINT pos; /* center of hover rectangle */
1207 /* FIXME: move tracking stuff into per-thread data */
1208 static struct tracking_list tracking_info;
1210 static void check_mouse_leave( HWND hwnd, int hittest )
1212 if (tracking_info.info.hwndTrack != hwnd)
1214 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1215 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSELEAVE, 0, 0 );
1216 else
1217 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSELEAVE, 0, 0 );
1219 tracking_info.info.dwFlags &= ~TME_LEAVE;
1221 else
1223 if (hittest == HTCLIENT)
1225 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1227 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSELEAVE, 0, 0 );
1228 tracking_info.info.dwFlags &= ~TME_LEAVE;
1231 else
1233 if (!(tracking_info.info.dwFlags & TME_NONCLIENT))
1235 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSELEAVE, 0, 0 );
1236 tracking_info.info.dwFlags &= ~TME_LEAVE;
1242 void update_mouse_tracking_info( HWND hwnd )
1244 int hover_width = 0, hover_height = 0, hittest;
1245 POINT pos;
1247 TRACE( "hwnd %p\n", hwnd );
1249 get_cursor_pos( &pos );
1250 hwnd = window_from_point( hwnd, pos, &hittest );
1252 TRACE( "point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest );
1254 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERWIDTH, 0, &hover_width, 0 );
1255 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERHEIGHT, 0, &hover_height, 0 );
1257 TRACE( "tracked pos %s, current pos %s, hover width %d, hover height %d\n",
1258 wine_dbgstr_point(&tracking_info.pos), wine_dbgstr_point(&pos),
1259 hover_width, hover_height );
1261 if (tracking_info.info.dwFlags & TME_LEAVE)
1262 check_mouse_leave( hwnd, hittest );
1264 if (tracking_info.info.hwndTrack != hwnd)
1265 tracking_info.info.dwFlags &= ~TME_HOVER;
1267 if (tracking_info.info.dwFlags & TME_HOVER)
1269 /* has the cursor moved outside the rectangle centered around pos? */
1270 if ((abs( pos.x - tracking_info.pos.x ) > (hover_width / 2)) ||
1271 (abs( pos.y - tracking_info.pos.y ) > (hover_height / 2)))
1273 tracking_info.pos = pos;
1275 else
1277 if (hittest == HTCLIENT)
1279 screen_to_client(hwnd, &pos);
1280 TRACE( "client cursor pos %s\n", wine_dbgstr_point(&pos) );
1282 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSEHOVER,
1283 get_key_state(), MAKELPARAM( pos.x, pos.y ) );
1285 else
1287 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1288 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSEHOVER,
1289 hittest, MAKELPARAM( pos.x, pos.y ) );
1292 /* stop tracking mouse hover */
1293 tracking_info.info.dwFlags &= ~TME_HOVER;
1297 /* stop the timer if the tracking list is empty */
1298 if (!(tracking_info.info.dwFlags & (TME_HOVER | TME_LEAVE)))
1300 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1301 tracking_info.info.hwndTrack = 0;
1302 tracking_info.info.dwFlags = 0;
1303 tracking_info.info.dwHoverTime = 0;
1307 /***********************************************************************
1308 * NtUserTrackMouseEvent (win32u.@)
1310 BOOL WINAPI NtUserTrackMouseEvent( TRACKMOUSEEVENT *info )
1312 DWORD hover_time;
1313 int hittest;
1314 HWND hwnd;
1315 POINT pos;
1317 TRACE( "size %u, flags %#x, hwnd %p, time %u\n",
1318 info->cbSize, info->dwFlags, info->hwndTrack, info->dwHoverTime );
1320 if (info->cbSize != sizeof(TRACKMOUSEEVENT))
1322 WARN( "wrong size %u\n", info->cbSize );
1323 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1324 return FALSE;
1327 if (info->dwFlags & TME_QUERY)
1329 *info = tracking_info.info;
1330 info->cbSize = sizeof(TRACKMOUSEEVENT);
1331 return TRUE;
1334 if (!is_window( info->hwndTrack ))
1336 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1337 return FALSE;
1340 hover_time = (info->dwFlags & TME_HOVER) ? info->dwHoverTime : HOVER_DEFAULT;
1342 if (hover_time == HOVER_DEFAULT || hover_time == 0)
1343 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERTIME, 0, &hover_time, 0 );
1345 get_cursor_pos( &pos );
1346 hwnd = window_from_point( info->hwndTrack, pos, &hittest );
1347 TRACE( "point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest );
1349 if (info->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT))
1350 FIXME( "ignoring flags %#x\n", info->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT) );
1352 if (info->dwFlags & TME_CANCEL)
1354 if (tracking_info.info.hwndTrack == info->hwndTrack)
1356 tracking_info.info.dwFlags &= ~(info->dwFlags & ~TME_CANCEL);
1358 /* if we aren't tracking on hover or leave remove this entry */
1359 if (!(tracking_info.info.dwFlags & (TME_HOVER | TME_LEAVE)))
1361 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1362 tracking_info.info.hwndTrack = 0;
1363 tracking_info.info.dwFlags = 0;
1364 tracking_info.info.dwHoverTime = 0;
1368 else
1370 /* In our implementation, it's possible that another window will receive
1371 * WM_MOUSEMOVE and call TrackMouseEvent before TrackMouseEventProc is
1372 * called. In such a situation, post the WM_MOUSELEAVE now. */
1373 if ((tracking_info.info.dwFlags & TME_LEAVE) && tracking_info.info.hwndTrack != NULL)
1374 check_mouse_leave(hwnd, hittest);
1376 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1377 tracking_info.info.hwndTrack = 0;
1378 tracking_info.info.dwFlags = 0;
1379 tracking_info.info.dwHoverTime = 0;
1381 if (info->hwndTrack == hwnd)
1383 /* Adding new mouse event to the tracking list */
1384 tracking_info.info = *info;
1385 tracking_info.info.dwHoverTime = hover_time;
1387 /* Initialize HoverInfo variables even if not hover tracking */
1388 tracking_info.pos = pos;
1390 NtUserSetSystemTimer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE, hover_time );
1394 return TRUE;
1397 /**********************************************************************
1398 * set_capture_window
1400 BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
1402 HWND previous = 0;
1403 UINT flags = 0;
1404 BOOL ret;
1406 if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU;
1407 if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE;
1409 SERVER_START_REQ( set_capture_window )
1411 req->handle = wine_server_user_handle( hwnd );
1412 req->flags = flags;
1413 if ((ret = !wine_server_call_err( req )))
1415 previous = wine_server_ptr_handle( reply->previous );
1416 hwnd = wine_server_ptr_handle( reply->full_handle );
1419 SERVER_END_REQ;
1421 if (ret)
1423 user_driver->pSetCapture( hwnd, gui_flags );
1425 if (previous)
1426 send_message( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
1428 if (prev_ret) *prev_ret = previous;
1430 return ret;
1433 /**********************************************************************
1434 * NtUserSetCapture (win32u.@)
1436 HWND WINAPI NtUserSetCapture( HWND hwnd )
1438 HWND previous = 0;
1440 set_capture_window( hwnd, 0, &previous );
1441 return previous;
1444 /**********************************************************************
1445 * release_capture
1447 BOOL WINAPI release_capture(void)
1449 BOOL ret = set_capture_window( 0, 0, NULL );
1451 /* Somebody may have missed some mouse movements */
1452 if (ret)
1454 INPUT input = { .type = INPUT_MOUSE };
1455 input.mi.dwFlags = MOUSEEVENTF_MOVE;
1456 NtUserSendInput( 1, &input, sizeof(input) );
1459 return ret;
1462 /*******************************************************************
1463 * NtUserGetForegroundWindow (win32u.@)
1465 HWND WINAPI NtUserGetForegroundWindow(void)
1467 HWND ret = 0;
1469 SERVER_START_REQ( get_thread_input )
1471 req->tid = 0;
1472 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground );
1474 SERVER_END_REQ;
1475 return ret;
1478 /* see GetActiveWindow */
1479 HWND get_active_window(void)
1481 GUITHREADINFO info;
1482 info.cbSize = sizeof(info);
1483 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
1486 /* see GetCapture */
1487 HWND get_capture(void)
1489 GUITHREADINFO info;
1490 info.cbSize = sizeof(info);
1491 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0;
1494 /* see GetFocus */
1495 HWND get_focus(void)
1497 GUITHREADINFO info;
1498 info.cbSize = sizeof(info);
1499 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
1502 /*****************************************************************
1503 * set_focus_window
1505 * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
1507 static HWND set_focus_window( HWND hwnd )
1509 HWND previous = 0, ime_hwnd;
1510 BOOL ret;
1512 SERVER_START_REQ( set_focus_window )
1514 req->handle = wine_server_user_handle( hwnd );
1515 if ((ret = !wine_server_call_err( req )))
1516 previous = wine_server_ptr_handle( reply->previous );
1518 SERVER_END_REQ;
1519 if (!ret) return 0;
1520 if (previous == hwnd) return previous;
1522 if (previous)
1524 send_message( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
1526 ime_hwnd = get_default_ime_window( previous );
1527 if (ime_hwnd)
1528 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_DEACTIVATE,
1529 HandleToUlong(previous) );
1531 if (hwnd != get_focus()) return previous; /* changed by the message */
1533 if (is_window(hwnd))
1535 user_driver->pSetFocus(hwnd);
1537 ime_hwnd = get_default_ime_window( hwnd );
1538 if (ime_hwnd)
1539 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE,
1540 HandleToUlong(hwnd) );
1542 if (previous)
1543 NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 );
1545 send_message( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
1547 return previous;
1550 /*******************************************************************
1551 * set_active_window
1553 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
1555 HWND previous = get_active_window();
1556 BOOL ret;
1557 DWORD old_thread, new_thread;
1558 CBTACTIVATESTRUCT cbt;
1560 if (previous == hwnd)
1562 if (prev) *prev = hwnd;
1563 return TRUE;
1566 /* call CBT hook chain */
1567 cbt.fMouse = mouse;
1568 cbt.hWndActive = previous;
1569 if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, sizeof(cbt) )) return FALSE;
1571 if (is_window( previous ))
1573 send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
1574 send_message( previous, WM_ACTIVATE,
1575 MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd );
1578 SERVER_START_REQ( set_active_window )
1580 req->handle = wine_server_user_handle( hwnd );
1581 if ((ret = !wine_server_call_err( req )))
1582 previous = wine_server_ptr_handle( reply->previous );
1584 SERVER_END_REQ;
1585 if (!ret) return FALSE;
1586 if (prev) *prev = previous;
1587 if (previous == hwnd) return TRUE;
1589 if (hwnd)
1591 /* send palette messages */
1592 if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
1593 send_message_timeout( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
1594 SMTO_ABORTIFHUNG, 2000, FALSE );
1595 if (!is_window(hwnd)) return FALSE;
1598 old_thread = previous ? get_window_thread( previous, NULL ) : 0;
1599 new_thread = hwnd ? get_window_thread( hwnd, NULL ) : 0;
1601 if (old_thread != new_thread)
1603 HWND *list, *phwnd;
1605 if ((list = list_window_children( NULL, get_desktop_window(), NULL, 0 )))
1607 if (old_thread)
1609 for (phwnd = list; *phwnd; phwnd++)
1611 if (get_window_thread( *phwnd, NULL ) == old_thread)
1612 send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
1615 if (new_thread)
1617 for (phwnd = list; *phwnd; phwnd++)
1619 if (get_window_thread( *phwnd, NULL ) == new_thread)
1620 send_message( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
1623 free( list );
1627 if (is_window(hwnd))
1629 send_message( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous );
1630 send_message( hwnd, WM_ACTIVATE,
1631 MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, is_iconic(hwnd) ),
1632 (LPARAM)previous );
1633 if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
1634 NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
1637 /* now change focus if necessary */
1638 if (focus)
1640 GUITHREADINFO info;
1642 info.cbSize = sizeof(info);
1643 NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info );
1644 /* Do not change focus if the window is no more active */
1645 if (hwnd == info.hwndActive)
1647 if (!info.hwndFocus || !hwnd || NtUserGetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
1648 set_focus_window( hwnd );
1652 return TRUE;
1655 /**********************************************************************
1656 * NtUserSetActiveWindow (win32u.@)
1658 HWND WINAPI NtUserSetActiveWindow( HWND hwnd )
1660 HWND prev;
1662 TRACE( "%p\n", hwnd );
1664 if (hwnd)
1666 LONG style;
1668 hwnd = get_full_window_handle( hwnd );
1669 if (!is_window( hwnd ))
1671 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1672 return 0;
1675 style = get_window_long( hwnd, GWL_STYLE );
1676 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
1677 return get_active_window(); /* Windows doesn't seem to return an error here */
1680 if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
1681 return prev;
1684 /*****************************************************************
1685 * NtUserSetFocus (win32u.@)
1687 HWND WINAPI NtUserSetFocus( HWND hwnd )
1689 HWND hwndTop = hwnd;
1690 HWND previous = get_focus();
1692 TRACE( "%p prev %p\n", hwnd, previous );
1694 if (hwnd)
1696 /* Check if we can set the focus to this window */
1697 hwnd = get_full_window_handle( hwnd );
1698 if (!is_window( hwnd ))
1700 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1701 return 0;
1703 if (hwnd == previous) return previous; /* nothing to do */
1704 for (;;)
1706 HWND parent;
1707 LONG style = get_window_long( hwndTop, GWL_STYLE );
1708 if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
1709 if (!(style & WS_CHILD)) break;
1710 parent = NtUserGetAncestor( hwndTop, GA_PARENT );
1711 if (!parent || parent == get_desktop_window())
1713 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
1714 break;
1716 if (parent == get_hwnd_message_parent()) return 0;
1717 hwndTop = parent;
1720 /* call hooks */
1721 if (call_hooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, 0 )) return 0;
1723 /* activate hwndTop if needed. */
1724 if (hwndTop != get_active_window())
1726 if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
1727 if (!is_window( hwnd )) return 0; /* Abort if window destroyed */
1729 /* Do not change focus if the window is no longer active */
1730 if (hwndTop != get_active_window()) return 0;
1733 else /* NULL hwnd passed in */
1735 if (!previous) return 0; /* nothing to do */
1736 if (call_hooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, 0 )) return 0;
1739 /* change focus and send messages */
1740 return set_focus_window( hwnd );
1743 /*******************************************************************
1744 * set_foreground_window
1746 BOOL set_foreground_window( HWND hwnd, BOOL mouse )
1748 BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
1749 HWND previous = 0;
1751 if (mouse) hwnd = get_full_window_handle( hwnd );
1753 SERVER_START_REQ( set_foreground_window )
1755 req->handle = wine_server_user_handle( hwnd );
1756 if ((ret = !wine_server_call_err( req )))
1758 previous = wine_server_ptr_handle( reply->previous );
1759 send_msg_old = reply->send_msg_old;
1760 send_msg_new = reply->send_msg_new;
1763 SERVER_END_REQ;
1765 if (ret && previous != hwnd)
1767 if (send_msg_old) /* old window belongs to other thread */
1768 NtUserMessageCall( previous, WM_WINE_SETACTIVEWINDOW, 0, 0,
1769 0, NtUserSendNotifyMessage, FALSE );
1770 else if (send_msg_new) /* old window belongs to us but new one to other thread */
1771 ret = set_active_window( 0, NULL, mouse, TRUE );
1773 if (send_msg_new) /* new window belongs to other thread */
1774 NtUserMessageCall( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0,
1775 0, NtUserSendNotifyMessage, FALSE );
1776 else /* new window belongs to us */
1777 ret = set_active_window( hwnd, NULL, mouse, TRUE );
1779 return ret;
1782 struct
1784 HBITMAP bitmap;
1785 unsigned int timeout;
1786 } caret = {0, 500};
1788 static void display_caret( HWND hwnd, const RECT *r )
1790 HDC dc, mem_dc;
1792 /* do not use DCX_CACHE here, since coördinates are in logical units */
1793 if (!(dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE )))
1794 return;
1795 mem_dc = NtGdiCreateCompatibleDC(dc);
1796 if (mem_dc)
1798 HBITMAP prev_bitmap;
1800 prev_bitmap = NtGdiSelectBitmap( mem_dc, caret.bitmap );
1801 NtGdiBitBlt( dc, r->left, r->top, r->right-r->left, r->bottom-r->top, mem_dc, 0, 0, SRCINVERT, 0, 0 );
1802 NtGdiSelectBitmap( mem_dc, prev_bitmap );
1803 NtGdiDeleteObjectApp( mem_dc );
1805 NtUserReleaseDC( hwnd, dc );
1808 static unsigned int get_caret_registry_timeout(void)
1810 char value_buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[11 * sizeof(WCHAR)])];
1811 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buffer;
1812 unsigned int ret = 500;
1813 HKEY key;
1815 if (!(key = reg_open_hkcu_key( "Control Panel\\Desktop" )))
1816 return ret;
1818 if (query_reg_ascii_value( key, "CursorBlinkRate", value, sizeof(value_buffer) ))
1819 ret = wcstoul( (WCHAR *)value->Data, NULL, 10 );
1820 NtClose( key );
1821 return ret;
1824 /*****************************************************************
1825 * NtUserCreateCaret (win32u.@)
1827 BOOL WINAPI NtUserCreateCaret( HWND hwnd, HBITMAP bitmap, int width, int height )
1829 HBITMAP caret_bitmap = 0;
1830 int old_state = 0;
1831 int hidden = 0;
1832 HWND prev = 0;
1833 BOOL ret;
1834 RECT r;
1836 TRACE( "hwnd %p, bitmap %p, width %d, height %d\n", hwnd, bitmap, width, height );
1838 if (!hwnd) return FALSE;
1840 if (bitmap && bitmap != (HBITMAP)1)
1842 BITMAP bitmap_data;
1844 if (!NtGdiExtGetObjectW( bitmap, sizeof(bitmap_data), &bitmap_data )) return FALSE;
1845 caret_bitmap = NtGdiCreateBitmap( bitmap_data.bmWidth, bitmap_data.bmHeight,
1846 bitmap_data.bmPlanes, bitmap_data.bmBitsPixel, NULL );
1847 if (caret_bitmap)
1849 size_t size = bitmap_data.bmWidthBytes * bitmap_data.bmHeight;
1850 BYTE *bits = malloc( size );
1852 NtGdiGetBitmapBits( bitmap, size, bits );
1853 NtGdiSetBitmapBits( caret_bitmap, size, bits );
1854 free( bits );
1857 else
1859 HDC dc;
1861 if (!width) width = get_system_metrics( SM_CXBORDER );
1862 if (!height) height = get_system_metrics( SM_CYBORDER );
1864 /* create the uniform bitmap on the fly */
1865 dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
1866 if (dc)
1868 HDC mem_dc = NtGdiCreateCompatibleDC( dc );
1869 if (mem_dc)
1871 if ((caret_bitmap = NtGdiCreateCompatibleBitmap( mem_dc, width, height )))
1873 HBITMAP prev_bitmap = NtGdiSelectBitmap( mem_dc, caret_bitmap );
1874 SetRect( &r, 0, 0, width, height );
1875 fill_rect( mem_dc, &r, GetStockObject( bitmap ? GRAY_BRUSH : WHITE_BRUSH ));
1876 NtGdiSelectBitmap( mem_dc, prev_bitmap );
1878 NtGdiDeleteObjectApp( mem_dc );
1880 NtUserReleaseDC( hwnd, dc );
1883 if (!caret_bitmap) return FALSE;
1885 SERVER_START_REQ( set_caret_window )
1887 req->handle = wine_server_user_handle( hwnd );
1888 req->width = width;
1889 req->height = height;
1890 if ((ret = !wine_server_call_err( req )))
1892 prev = wine_server_ptr_handle( reply->previous );
1893 r.left = reply->old_rect.left;
1894 r.top = reply->old_rect.top;
1895 r.right = reply->old_rect.right;
1896 r.bottom = reply->old_rect.bottom;
1897 old_state = reply->old_state;
1898 hidden = reply->old_hide;
1901 SERVER_END_REQ;
1902 if (!ret) return FALSE;
1904 if (prev && !hidden) /* hide the previous one */
1906 /* FIXME: won't work if prev belongs to a different process */
1907 kill_system_timer( prev, SYSTEM_TIMER_CARET );
1908 if (old_state) display_caret( prev, &r );
1911 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
1912 caret.bitmap = caret_bitmap;
1913 caret.timeout = get_caret_registry_timeout();
1914 return TRUE;
1917 /*******************************************************************
1918 * destroy_caret
1920 BOOL destroy_caret(void)
1922 int old_state = 0;
1923 int hidden = 0;
1924 HWND prev = 0;
1925 BOOL ret;
1926 RECT r;
1928 SERVER_START_REQ( set_caret_window )
1930 req->handle = 0;
1931 req->width = 0;
1932 req->height = 0;
1933 if ((ret = !wine_server_call_err( req )))
1935 prev = wine_server_ptr_handle( reply->previous );
1936 r.left = reply->old_rect.left;
1937 r.top = reply->old_rect.top;
1938 r.right = reply->old_rect.right;
1939 r.bottom = reply->old_rect.bottom;
1940 old_state = reply->old_state;
1941 hidden = reply->old_hide;
1944 SERVER_END_REQ;
1946 if (ret && prev && !hidden)
1948 /* FIXME: won't work if prev belongs to a different process */
1949 kill_system_timer( prev, SYSTEM_TIMER_CARET );
1950 if (old_state) display_caret( prev, &r );
1952 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
1953 caret.bitmap = 0;
1954 return ret;
1957 /*****************************************************************
1958 * NtUserGetCaretBlinkTime (win32u.@)
1960 UINT WINAPI NtUserGetCaretBlinkTime(void)
1962 return caret.timeout;
1965 /*******************************************************************
1966 * set_caret_blink_time
1968 BOOL set_caret_blink_time( unsigned int time )
1970 TRACE( "time %u\n", time );
1972 caret.timeout = time;
1973 /* FIXME: update the timer */
1974 return TRUE;
1977 /*****************************************************************
1978 * NtUserGetCaretPos (win32u.@)
1980 BOOL WINAPI NtUserGetCaretPos( POINT *pt )
1982 BOOL ret;
1984 SERVER_START_REQ( set_caret_info )
1986 req->flags = 0; /* don't set anything */
1987 req->handle = 0;
1988 req->x = 0;
1989 req->y = 0;
1990 req->hide = 0;
1991 req->state = 0;
1992 if ((ret = !wine_server_call_err( req )))
1994 pt->x = reply->old_rect.left;
1995 pt->y = reply->old_rect.top;
1998 SERVER_END_REQ;
1999 return ret;
2002 /*******************************************************************
2003 * set_caret_pos
2005 BOOL set_caret_pos( int x, int y )
2007 int old_state = 0;
2008 int hidden = 0;
2009 HWND hwnd = 0;
2010 BOOL ret;
2011 RECT r;
2013 TRACE( "(%d, %d)\n", x, y );
2015 SERVER_START_REQ( set_caret_info )
2017 req->flags = SET_CARET_POS|SET_CARET_STATE;
2018 req->handle = 0;
2019 req->x = x;
2020 req->y = y;
2021 req->hide = 0;
2022 req->state = CARET_STATE_ON_IF_MOVED;
2023 if ((ret = !wine_server_call_err( req )))
2025 hwnd = wine_server_ptr_handle( reply->full_handle );
2026 r.left = reply->old_rect.left;
2027 r.top = reply->old_rect.top;
2028 r.right = reply->old_rect.right;
2029 r.bottom = reply->old_rect.bottom;
2030 old_state = reply->old_state;
2031 hidden = reply->old_hide;
2034 SERVER_END_REQ;
2035 if (ret && !hidden && (x != r.left || y != r.top))
2037 if (old_state) display_caret( hwnd, &r );
2038 r.right += x - r.left;
2039 r.bottom += y - r.top;
2040 r.left = x;
2041 r.top = y;
2042 display_caret( hwnd, &r );
2043 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2045 return ret;
2048 /*****************************************************************
2049 * NtUserShowCaret (win32u.@)
2051 BOOL WINAPI NtUserShowCaret( HWND hwnd )
2053 int hidden = 0;
2054 BOOL ret;
2055 RECT r;
2057 SERVER_START_REQ( set_caret_info )
2059 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2060 req->handle = wine_server_user_handle( hwnd );
2061 req->x = 0;
2062 req->y = 0;
2063 req->hide = -1;
2064 req->state = CARET_STATE_ON;
2065 if ((ret = !wine_server_call_err( req )))
2067 hwnd = wine_server_ptr_handle( reply->full_handle );
2068 r.left = reply->old_rect.left;
2069 r.top = reply->old_rect.top;
2070 r.right = reply->old_rect.right;
2071 r.bottom = reply->old_rect.bottom;
2072 hidden = reply->old_hide;
2075 SERVER_END_REQ;
2077 if (ret && hidden == 1) /* hidden was 1 so it's now 0 */
2079 display_caret( hwnd, &r );
2080 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2082 return ret;
2085 /*****************************************************************
2086 * NtUserHideCaret (win32u.@)
2088 BOOL WINAPI NtUserHideCaret( HWND hwnd )
2090 int old_state = 0;
2091 int hidden = 0;
2092 BOOL ret;
2093 RECT r;
2095 SERVER_START_REQ( set_caret_info )
2097 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2098 req->handle = wine_server_user_handle( hwnd );
2099 req->x = 0;
2100 req->y = 0;
2101 req->hide = 1;
2102 req->state = CARET_STATE_OFF;
2103 if ((ret = !wine_server_call_err( req )))
2105 hwnd = wine_server_ptr_handle( reply->full_handle );
2106 r.left = reply->old_rect.left;
2107 r.top = reply->old_rect.top;
2108 r.right = reply->old_rect.right;
2109 r.bottom = reply->old_rect.bottom;
2110 old_state = reply->old_state;
2111 hidden = reply->old_hide;
2114 SERVER_END_REQ;
2116 if (ret && !hidden)
2118 if (old_state) display_caret( hwnd, &r );
2119 kill_system_timer( hwnd, SYSTEM_TIMER_CARET );
2121 return ret;
2124 void toggle_caret( HWND hwnd )
2126 BOOL ret;
2127 RECT r;
2128 int hidden = 0;
2130 SERVER_START_REQ( set_caret_info )
2132 req->flags = SET_CARET_STATE;
2133 req->handle = wine_server_user_handle( hwnd );
2134 req->x = 0;
2135 req->y = 0;
2136 req->hide = 0;
2137 req->state = CARET_STATE_TOGGLE;
2138 if ((ret = !wine_server_call( req )))
2140 hwnd = wine_server_ptr_handle( reply->full_handle );
2141 r.left = reply->old_rect.left;
2142 r.top = reply->old_rect.top;
2143 r.right = reply->old_rect.right;
2144 r.bottom = reply->old_rect.bottom;
2145 hidden = reply->old_hide;
2148 SERVER_END_REQ;
2150 if (ret && !hidden) display_caret( hwnd, &r );