gdi32/tests: Mark tests failing randomly on Windows as flaky.
[wine.git] / dlls / win32u / input.c
blobf3b724e7010e0c4d5b55272a2753a245a7764fa2
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 * NtUserDragDetect (win32u.@)
1400 BOOL WINAPI NtUserDragDetect( HWND hwnd, int x, int y )
1402 WORD width, height;
1403 RECT rect;
1404 MSG msg;
1406 TRACE( "%p (%d,%d)\n", hwnd, x, y );
1408 if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000)) return FALSE;
1410 width = get_system_metrics( SM_CXDRAG );
1411 height = get_system_metrics( SM_CYDRAG );
1412 SetRect( &rect, x - width, y - height, x + width, y + height );
1414 NtUserSetCapture( hwnd );
1416 for (;;)
1418 while (NtUserPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
1420 if (msg.message == WM_LBUTTONUP)
1422 release_capture();
1423 return FALSE;
1425 if (msg.message == WM_MOUSEMOVE)
1427 POINT tmp;
1428 tmp.x = (short)LOWORD( msg.lParam );
1429 tmp.y = (short)HIWORD( msg.lParam );
1430 if (!PtInRect( &rect, tmp ))
1432 release_capture();
1433 return TRUE;
1437 NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
1439 return FALSE;
1442 /**********************************************************************
1443 * set_capture_window
1445 BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
1447 HWND previous = 0;
1448 UINT flags = 0;
1449 BOOL ret;
1451 if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU;
1452 if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE;
1454 SERVER_START_REQ( set_capture_window )
1456 req->handle = wine_server_user_handle( hwnd );
1457 req->flags = flags;
1458 if ((ret = !wine_server_call_err( req )))
1460 previous = wine_server_ptr_handle( reply->previous );
1461 hwnd = wine_server_ptr_handle( reply->full_handle );
1464 SERVER_END_REQ;
1466 if (ret)
1468 user_driver->pSetCapture( hwnd, gui_flags );
1470 if (previous)
1471 send_message( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
1473 if (prev_ret) *prev_ret = previous;
1475 return ret;
1478 /**********************************************************************
1479 * NtUserSetCapture (win32u.@)
1481 HWND WINAPI NtUserSetCapture( HWND hwnd )
1483 HWND previous = 0;
1485 set_capture_window( hwnd, 0, &previous );
1486 return previous;
1489 /**********************************************************************
1490 * release_capture
1492 BOOL WINAPI release_capture(void)
1494 BOOL ret = set_capture_window( 0, 0, NULL );
1496 /* Somebody may have missed some mouse movements */
1497 if (ret)
1499 INPUT input = { .type = INPUT_MOUSE };
1500 input.mi.dwFlags = MOUSEEVENTF_MOVE;
1501 NtUserSendInput( 1, &input, sizeof(input) );
1504 return ret;
1507 /*******************************************************************
1508 * NtUserGetForegroundWindow (win32u.@)
1510 HWND WINAPI NtUserGetForegroundWindow(void)
1512 HWND ret = 0;
1514 SERVER_START_REQ( get_thread_input )
1516 req->tid = 0;
1517 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground );
1519 SERVER_END_REQ;
1520 return ret;
1523 /* see GetActiveWindow */
1524 HWND get_active_window(void)
1526 GUITHREADINFO info;
1527 info.cbSize = sizeof(info);
1528 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
1531 /* see GetCapture */
1532 HWND get_capture(void)
1534 GUITHREADINFO info;
1535 info.cbSize = sizeof(info);
1536 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0;
1539 /* see GetFocus */
1540 HWND get_focus(void)
1542 GUITHREADINFO info;
1543 info.cbSize = sizeof(info);
1544 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
1547 /*****************************************************************
1548 * set_focus_window
1550 * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
1552 static HWND set_focus_window( HWND hwnd )
1554 HWND previous = 0, ime_hwnd;
1555 BOOL ret;
1557 SERVER_START_REQ( set_focus_window )
1559 req->handle = wine_server_user_handle( hwnd );
1560 if ((ret = !wine_server_call_err( req )))
1561 previous = wine_server_ptr_handle( reply->previous );
1563 SERVER_END_REQ;
1564 if (!ret) return 0;
1565 if (previous == hwnd) return previous;
1567 if (previous)
1569 send_message( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
1571 ime_hwnd = get_default_ime_window( previous );
1572 if (ime_hwnd)
1573 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_DEACTIVATE,
1574 HandleToUlong(previous) );
1576 if (hwnd != get_focus()) return previous; /* changed by the message */
1578 if (is_window(hwnd))
1580 user_driver->pSetFocus(hwnd);
1582 ime_hwnd = get_default_ime_window( hwnd );
1583 if (ime_hwnd)
1584 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE,
1585 HandleToUlong(hwnd) );
1587 if (previous)
1588 NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 );
1590 send_message( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
1592 return previous;
1595 /*******************************************************************
1596 * set_active_window
1598 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
1600 HWND previous = get_active_window();
1601 BOOL ret;
1602 DWORD old_thread, new_thread;
1603 CBTACTIVATESTRUCT cbt;
1605 if (previous == hwnd)
1607 if (prev) *prev = hwnd;
1608 return TRUE;
1611 /* call CBT hook chain */
1612 cbt.fMouse = mouse;
1613 cbt.hWndActive = previous;
1614 if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, sizeof(cbt) )) return FALSE;
1616 if (is_window( previous ))
1618 send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
1619 send_message( previous, WM_ACTIVATE,
1620 MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd );
1623 SERVER_START_REQ( set_active_window )
1625 req->handle = wine_server_user_handle( hwnd );
1626 if ((ret = !wine_server_call_err( req )))
1627 previous = wine_server_ptr_handle( reply->previous );
1629 SERVER_END_REQ;
1630 if (!ret) return FALSE;
1631 if (prev) *prev = previous;
1632 if (previous == hwnd) return TRUE;
1634 if (hwnd)
1636 /* send palette messages */
1637 if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
1638 send_message_timeout( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
1639 SMTO_ABORTIFHUNG, 2000, FALSE );
1640 if (!is_window(hwnd)) return FALSE;
1643 old_thread = previous ? get_window_thread( previous, NULL ) : 0;
1644 new_thread = hwnd ? get_window_thread( hwnd, NULL ) : 0;
1646 if (old_thread != new_thread)
1648 HWND *list, *phwnd;
1650 if ((list = list_window_children( NULL, get_desktop_window(), NULL, 0 )))
1652 if (old_thread)
1654 for (phwnd = list; *phwnd; phwnd++)
1656 if (get_window_thread( *phwnd, NULL ) == old_thread)
1657 send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
1660 if (new_thread)
1662 for (phwnd = list; *phwnd; phwnd++)
1664 if (get_window_thread( *phwnd, NULL ) == new_thread)
1665 send_message( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
1668 free( list );
1672 if (is_window(hwnd))
1674 send_message( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous );
1675 send_message( hwnd, WM_ACTIVATE,
1676 MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, is_iconic(hwnd) ),
1677 (LPARAM)previous );
1678 if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
1679 NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
1682 /* now change focus if necessary */
1683 if (focus)
1685 GUITHREADINFO info;
1687 info.cbSize = sizeof(info);
1688 NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info );
1689 /* Do not change focus if the window is no more active */
1690 if (hwnd == info.hwndActive)
1692 if (!info.hwndFocus || !hwnd || NtUserGetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
1693 set_focus_window( hwnd );
1697 return TRUE;
1700 /**********************************************************************
1701 * NtUserSetActiveWindow (win32u.@)
1703 HWND WINAPI NtUserSetActiveWindow( HWND hwnd )
1705 HWND prev;
1707 TRACE( "%p\n", hwnd );
1709 if (hwnd)
1711 LONG style;
1713 hwnd = get_full_window_handle( hwnd );
1714 if (!is_window( hwnd ))
1716 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1717 return 0;
1720 style = get_window_long( hwnd, GWL_STYLE );
1721 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
1722 return get_active_window(); /* Windows doesn't seem to return an error here */
1725 if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
1726 return prev;
1729 /*****************************************************************
1730 * NtUserSetFocus (win32u.@)
1732 HWND WINAPI NtUserSetFocus( HWND hwnd )
1734 HWND hwndTop = hwnd;
1735 HWND previous = get_focus();
1737 TRACE( "%p prev %p\n", hwnd, previous );
1739 if (hwnd)
1741 /* Check if we can set the focus to this window */
1742 hwnd = get_full_window_handle( hwnd );
1743 if (!is_window( hwnd ))
1745 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1746 return 0;
1748 if (hwnd == previous) return previous; /* nothing to do */
1749 for (;;)
1751 HWND parent;
1752 LONG style = get_window_long( hwndTop, GWL_STYLE );
1753 if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
1754 if (!(style & WS_CHILD)) break;
1755 parent = NtUserGetAncestor( hwndTop, GA_PARENT );
1756 if (!parent || parent == get_desktop_window())
1758 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
1759 break;
1761 if (parent == get_hwnd_message_parent()) return 0;
1762 hwndTop = parent;
1765 /* call hooks */
1766 if (call_hooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, 0 )) return 0;
1768 /* activate hwndTop if needed. */
1769 if (hwndTop != get_active_window())
1771 if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
1772 if (!is_window( hwnd )) return 0; /* Abort if window destroyed */
1774 /* Do not change focus if the window is no longer active */
1775 if (hwndTop != get_active_window()) return 0;
1778 else /* NULL hwnd passed in */
1780 if (!previous) return 0; /* nothing to do */
1781 if (call_hooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, 0 )) return 0;
1784 /* change focus and send messages */
1785 return set_focus_window( hwnd );
1788 /*******************************************************************
1789 * set_foreground_window
1791 BOOL set_foreground_window( HWND hwnd, BOOL mouse )
1793 BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
1794 HWND previous = 0;
1796 if (mouse) hwnd = get_full_window_handle( hwnd );
1798 SERVER_START_REQ( set_foreground_window )
1800 req->handle = wine_server_user_handle( hwnd );
1801 if ((ret = !wine_server_call_err( req )))
1803 previous = wine_server_ptr_handle( reply->previous );
1804 send_msg_old = reply->send_msg_old;
1805 send_msg_new = reply->send_msg_new;
1808 SERVER_END_REQ;
1810 if (ret && previous != hwnd)
1812 if (send_msg_old) /* old window belongs to other thread */
1813 NtUserMessageCall( previous, WM_WINE_SETACTIVEWINDOW, 0, 0,
1814 0, NtUserSendNotifyMessage, FALSE );
1815 else if (send_msg_new) /* old window belongs to us but new one to other thread */
1816 ret = set_active_window( 0, NULL, mouse, TRUE );
1818 if (send_msg_new) /* new window belongs to other thread */
1819 NtUserMessageCall( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0,
1820 0, NtUserSendNotifyMessage, FALSE );
1821 else /* new window belongs to us */
1822 ret = set_active_window( hwnd, NULL, mouse, TRUE );
1824 return ret;
1827 struct
1829 HBITMAP bitmap;
1830 unsigned int timeout;
1831 } caret = {0, 500};
1833 static void display_caret( HWND hwnd, const RECT *r )
1835 HDC dc, mem_dc;
1837 /* do not use DCX_CACHE here, since coördinates are in logical units */
1838 if (!(dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE )))
1839 return;
1840 mem_dc = NtGdiCreateCompatibleDC(dc);
1841 if (mem_dc)
1843 HBITMAP prev_bitmap;
1845 prev_bitmap = NtGdiSelectBitmap( mem_dc, caret.bitmap );
1846 NtGdiBitBlt( dc, r->left, r->top, r->right-r->left, r->bottom-r->top, mem_dc, 0, 0, SRCINVERT, 0, 0 );
1847 NtGdiSelectBitmap( mem_dc, prev_bitmap );
1848 NtGdiDeleteObjectApp( mem_dc );
1850 NtUserReleaseDC( hwnd, dc );
1853 static unsigned int get_caret_registry_timeout(void)
1855 char value_buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[11 * sizeof(WCHAR)])];
1856 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buffer;
1857 unsigned int ret = 500;
1858 HKEY key;
1860 if (!(key = reg_open_hkcu_key( "Control Panel\\Desktop" )))
1861 return ret;
1863 if (query_reg_ascii_value( key, "CursorBlinkRate", value, sizeof(value_buffer) ))
1864 ret = wcstoul( (WCHAR *)value->Data, NULL, 10 );
1865 NtClose( key );
1866 return ret;
1869 /*****************************************************************
1870 * NtUserCreateCaret (win32u.@)
1872 BOOL WINAPI NtUserCreateCaret( HWND hwnd, HBITMAP bitmap, int width, int height )
1874 HBITMAP caret_bitmap = 0;
1875 int old_state = 0;
1876 int hidden = 0;
1877 HWND prev = 0;
1878 BOOL ret;
1879 RECT r;
1881 TRACE( "hwnd %p, bitmap %p, width %d, height %d\n", hwnd, bitmap, width, height );
1883 if (!hwnd) return FALSE;
1885 if (bitmap && bitmap != (HBITMAP)1)
1887 BITMAP bitmap_data;
1889 if (!NtGdiExtGetObjectW( bitmap, sizeof(bitmap_data), &bitmap_data )) return FALSE;
1890 caret_bitmap = NtGdiCreateBitmap( bitmap_data.bmWidth, bitmap_data.bmHeight,
1891 bitmap_data.bmPlanes, bitmap_data.bmBitsPixel, NULL );
1892 if (caret_bitmap)
1894 size_t size = bitmap_data.bmWidthBytes * bitmap_data.bmHeight;
1895 BYTE *bits = malloc( size );
1897 NtGdiGetBitmapBits( bitmap, size, bits );
1898 NtGdiSetBitmapBits( caret_bitmap, size, bits );
1899 free( bits );
1902 else
1904 HDC dc;
1906 if (!width) width = get_system_metrics( SM_CXBORDER );
1907 if (!height) height = get_system_metrics( SM_CYBORDER );
1909 /* create the uniform bitmap on the fly */
1910 dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
1911 if (dc)
1913 HDC mem_dc = NtGdiCreateCompatibleDC( dc );
1914 if (mem_dc)
1916 if ((caret_bitmap = NtGdiCreateCompatibleBitmap( mem_dc, width, height )))
1918 HBITMAP prev_bitmap = NtGdiSelectBitmap( mem_dc, caret_bitmap );
1919 SetRect( &r, 0, 0, width, height );
1920 fill_rect( mem_dc, &r, GetStockObject( bitmap ? GRAY_BRUSH : WHITE_BRUSH ));
1921 NtGdiSelectBitmap( mem_dc, prev_bitmap );
1923 NtGdiDeleteObjectApp( mem_dc );
1925 NtUserReleaseDC( hwnd, dc );
1928 if (!caret_bitmap) return FALSE;
1930 SERVER_START_REQ( set_caret_window )
1932 req->handle = wine_server_user_handle( hwnd );
1933 req->width = width;
1934 req->height = height;
1935 if ((ret = !wine_server_call_err( req )))
1937 prev = wine_server_ptr_handle( reply->previous );
1938 r.left = reply->old_rect.left;
1939 r.top = reply->old_rect.top;
1940 r.right = reply->old_rect.right;
1941 r.bottom = reply->old_rect.bottom;
1942 old_state = reply->old_state;
1943 hidden = reply->old_hide;
1946 SERVER_END_REQ;
1947 if (!ret) return FALSE;
1949 if (prev && !hidden) /* hide the previous one */
1951 /* FIXME: won't work if prev belongs to a different process */
1952 kill_system_timer( prev, SYSTEM_TIMER_CARET );
1953 if (old_state) display_caret( prev, &r );
1956 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
1957 caret.bitmap = caret_bitmap;
1958 caret.timeout = get_caret_registry_timeout();
1959 return TRUE;
1962 /*******************************************************************
1963 * destroy_caret
1965 BOOL destroy_caret(void)
1967 int old_state = 0;
1968 int hidden = 0;
1969 HWND prev = 0;
1970 BOOL ret;
1971 RECT r;
1973 SERVER_START_REQ( set_caret_window )
1975 req->handle = 0;
1976 req->width = 0;
1977 req->height = 0;
1978 if ((ret = !wine_server_call_err( req )))
1980 prev = wine_server_ptr_handle( reply->previous );
1981 r.left = reply->old_rect.left;
1982 r.top = reply->old_rect.top;
1983 r.right = reply->old_rect.right;
1984 r.bottom = reply->old_rect.bottom;
1985 old_state = reply->old_state;
1986 hidden = reply->old_hide;
1989 SERVER_END_REQ;
1991 if (ret && prev && !hidden)
1993 /* FIXME: won't work if prev belongs to a different process */
1994 kill_system_timer( prev, SYSTEM_TIMER_CARET );
1995 if (old_state) display_caret( prev, &r );
1997 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
1998 caret.bitmap = 0;
1999 return ret;
2002 /*****************************************************************
2003 * NtUserGetCaretBlinkTime (win32u.@)
2005 UINT WINAPI NtUserGetCaretBlinkTime(void)
2007 return caret.timeout;
2010 /*******************************************************************
2011 * set_caret_blink_time
2013 BOOL set_caret_blink_time( unsigned int time )
2015 TRACE( "time %u\n", time );
2017 caret.timeout = time;
2018 /* FIXME: update the timer */
2019 return TRUE;
2022 /*****************************************************************
2023 * NtUserGetCaretPos (win32u.@)
2025 BOOL WINAPI NtUserGetCaretPos( POINT *pt )
2027 BOOL ret;
2029 SERVER_START_REQ( set_caret_info )
2031 req->flags = 0; /* don't set anything */
2032 req->handle = 0;
2033 req->x = 0;
2034 req->y = 0;
2035 req->hide = 0;
2036 req->state = 0;
2037 if ((ret = !wine_server_call_err( req )))
2039 pt->x = reply->old_rect.left;
2040 pt->y = reply->old_rect.top;
2043 SERVER_END_REQ;
2044 return ret;
2047 /*******************************************************************
2048 * set_caret_pos
2050 BOOL set_caret_pos( int x, int y )
2052 int old_state = 0;
2053 int hidden = 0;
2054 HWND hwnd = 0;
2055 BOOL ret;
2056 RECT r;
2058 TRACE( "(%d, %d)\n", x, y );
2060 SERVER_START_REQ( set_caret_info )
2062 req->flags = SET_CARET_POS|SET_CARET_STATE;
2063 req->handle = 0;
2064 req->x = x;
2065 req->y = y;
2066 req->hide = 0;
2067 req->state = CARET_STATE_ON_IF_MOVED;
2068 if ((ret = !wine_server_call_err( req )))
2070 hwnd = wine_server_ptr_handle( reply->full_handle );
2071 r.left = reply->old_rect.left;
2072 r.top = reply->old_rect.top;
2073 r.right = reply->old_rect.right;
2074 r.bottom = reply->old_rect.bottom;
2075 old_state = reply->old_state;
2076 hidden = reply->old_hide;
2079 SERVER_END_REQ;
2080 if (ret && !hidden && (x != r.left || y != r.top))
2082 if (old_state) display_caret( hwnd, &r );
2083 r.right += x - r.left;
2084 r.bottom += y - r.top;
2085 r.left = x;
2086 r.top = y;
2087 display_caret( hwnd, &r );
2088 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2090 return ret;
2093 /*****************************************************************
2094 * NtUserShowCaret (win32u.@)
2096 BOOL WINAPI NtUserShowCaret( HWND hwnd )
2098 int hidden = 0;
2099 BOOL ret;
2100 RECT r;
2102 SERVER_START_REQ( set_caret_info )
2104 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2105 req->handle = wine_server_user_handle( hwnd );
2106 req->x = 0;
2107 req->y = 0;
2108 req->hide = -1;
2109 req->state = CARET_STATE_ON;
2110 if ((ret = !wine_server_call_err( req )))
2112 hwnd = wine_server_ptr_handle( reply->full_handle );
2113 r.left = reply->old_rect.left;
2114 r.top = reply->old_rect.top;
2115 r.right = reply->old_rect.right;
2116 r.bottom = reply->old_rect.bottom;
2117 hidden = reply->old_hide;
2120 SERVER_END_REQ;
2122 if (ret && hidden == 1) /* hidden was 1 so it's now 0 */
2124 display_caret( hwnd, &r );
2125 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2127 return ret;
2130 /*****************************************************************
2131 * NtUserHideCaret (win32u.@)
2133 BOOL WINAPI NtUserHideCaret( HWND hwnd )
2135 int old_state = 0;
2136 int hidden = 0;
2137 BOOL ret;
2138 RECT r;
2140 SERVER_START_REQ( set_caret_info )
2142 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2143 req->handle = wine_server_user_handle( hwnd );
2144 req->x = 0;
2145 req->y = 0;
2146 req->hide = 1;
2147 req->state = CARET_STATE_OFF;
2148 if ((ret = !wine_server_call_err( req )))
2150 hwnd = wine_server_ptr_handle( reply->full_handle );
2151 r.left = reply->old_rect.left;
2152 r.top = reply->old_rect.top;
2153 r.right = reply->old_rect.right;
2154 r.bottom = reply->old_rect.bottom;
2155 old_state = reply->old_state;
2156 hidden = reply->old_hide;
2159 SERVER_END_REQ;
2161 if (ret && !hidden)
2163 if (old_state) display_caret( hwnd, &r );
2164 kill_system_timer( hwnd, SYSTEM_TIMER_CARET );
2166 return ret;
2169 void toggle_caret( HWND hwnd )
2171 BOOL ret;
2172 RECT r;
2173 int hidden = 0;
2175 SERVER_START_REQ( set_caret_info )
2177 req->flags = SET_CARET_STATE;
2178 req->handle = wine_server_user_handle( hwnd );
2179 req->x = 0;
2180 req->y = 0;
2181 req->hide = 0;
2182 req->state = CARET_STATE_TOGGLE;
2183 if ((ret = !wine_server_call( req )))
2185 hwnd = wine_server_ptr_handle( reply->full_handle );
2186 r.left = reply->old_rect.left;
2187 r.top = reply->old_rect.top;
2188 r.right = reply->old_rect.right;
2189 r.bottom = reply->old_rect.bottom;
2190 hidden = reply->old_hide;
2193 SERVER_END_REQ;
2195 if (ret && !hidden) display_caret( hwnd, &r );