/* * USER Input processing * * Copyright 1993 Bob Amstadt * Copyright 1996 Albrecht Kleine * Copyright 1997 David Faure * Copyright 1998 Morten Welinder * Copyright 1998 Ulrich Weigand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #if 0 #pragma makedep unix #endif #include "win32u_private.h" #include "ntuser_private.h" #include "wine/server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(win); WINE_DECLARE_DEBUG_CHANNEL(keyboard); static const WCHAR keyboard_layouts_keyW[] = { '\\','R','e','g','i','s','t','r','y', '\\','M','a','c','h','i','n','e', '\\','S','y','s','t','e','m', '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t', '\\','C','o','n','t','r','o','l', '\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s' }; LONG global_key_state_counter = 0; /********************************************************************** * NtUserAttachThreadInput (win32u.@) */ BOOL WINAPI NtUserAttachThreadInput( DWORD from, DWORD to, BOOL attach ) { BOOL ret; SERVER_START_REQ( attach_thread_input ) { req->tid_from = from; req->tid_to = to; req->attach = attach; ret = !wine_server_call_err( req ); } SERVER_END_REQ; return ret; } /*********************************************************************** * NtUserSendInput (win32u.@) */ UINT WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size ) { if (!user_callbacks) return 0; return user_callbacks->pSendInput( count, inputs, size ); } /*********************************************************************** * NtUserSetCursorPos (win32u.@) */ BOOL WINAPI NtUserSetCursorPos( INT x, INT y ) { POINT pt = { x, y }; BOOL ret; INT prev_x, prev_y, new_x, new_y; UINT dpi; if ((dpi = get_thread_dpi())) { HMONITOR monitor = monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() ); pt = map_dpi_point( pt, dpi, get_monitor_dpi( monitor )); } SERVER_START_REQ( set_cursor ) { req->flags = SET_CURSOR_POS; req->x = pt.x; req->y = pt.y; if ((ret = !wine_server_call( req ))) { prev_x = reply->prev_x; prev_y = reply->prev_y; new_x = reply->new_x; new_y = reply->new_y; } } SERVER_END_REQ; if (ret && (prev_x != new_x || prev_y != new_y)) user_driver->pSetCursorPos( new_x, new_y ); return ret; } /*********************************************************************** * get_cursor_pos */ BOOL get_cursor_pos( POINT *pt ) { BOOL ret; DWORD last_change; UINT dpi; if (!pt) return FALSE; SERVER_START_REQ( set_cursor ) { if ((ret = !wine_server_call( req ))) { pt->x = reply->new_x; pt->y = reply->new_y; last_change = reply->last_change; } } SERVER_END_REQ; /* query new position from graphics driver if we haven't updated recently */ if (ret && NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt ); if (ret && (dpi = get_thread_dpi())) { HMONITOR monitor = monitor_from_point( *pt, MONITOR_DEFAULTTOPRIMARY, 0 ); *pt = map_dpi_point( *pt, get_monitor_dpi( monitor ), dpi ); } return ret; } /*********************************************************************** * NtUserGetCursorInfo (win32u.@) */ BOOL WINAPI NtUserGetCursorInfo( CURSORINFO *info ) { BOOL ret; if (!info) return FALSE; SERVER_START_REQ( get_thread_input ) { req->tid = 0; if ((ret = !wine_server_call( req ))) { info->hCursor = wine_server_ptr_handle( reply->cursor ); info->flags = reply->show_count >= 0 ? CURSOR_SHOWING : 0; } } SERVER_END_REQ; get_cursor_pos( &info->ptScreenPos ); return ret; } static void check_for_events( UINT flags ) { if (user_driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, flags, 0 ) == WAIT_TIMEOUT) flush_window_surfaces( TRUE ); } /********************************************************************** * GetAsyncKeyState (win32u.@) */ SHORT WINAPI NtUserGetAsyncKeyState( INT key ) { struct user_key_state_info *key_state_info = get_user_thread_info()->key_state; INT counter = global_key_state_counter; BYTE prev_key_state; SHORT ret; if (key < 0 || key >= 256) return 0; check_for_events( QS_INPUT ); if (key_state_info && !(key_state_info->state[key] & 0xc0) && key_state_info->counter == counter && NtGetTickCount() - key_state_info->time < 50) { /* use cached value */ return 0; } else if (!key_state_info) { key_state_info = calloc( 1, sizeof(*key_state_info) ); get_user_thread_info()->key_state = key_state_info; } ret = 0; SERVER_START_REQ( get_key_state ) { req->async = 1; req->key = key; if (key_state_info) { prev_key_state = key_state_info->state[key]; wine_server_set_reply( req, key_state_info->state, sizeof(key_state_info->state) ); } if (!wine_server_call( req )) { if (reply->state & 0x40) ret |= 0x0001; if (reply->state & 0x80) ret |= 0x8000; if (key_state_info) { /* force refreshing the key state cache - some multithreaded programs * (like Adobe Photoshop CS5) expect that changes to the async key state * are also immediately available in other threads. */ if (prev_key_state != key_state_info->state[key]) counter = InterlockedIncrement( &global_key_state_counter ); key_state_info->time = NtGetTickCount(); key_state_info->counter = counter; } } } SERVER_END_REQ; return ret; } /*********************************************************************** * NtUserGetQueueStatus (win32u.@) */ DWORD WINAPI NtUserGetQueueStatus( UINT flags ) { DWORD ret; if (flags & ~(QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_SMRESULT)) { SetLastError( ERROR_INVALID_FLAGS ); return 0; } check_for_events( flags ); SERVER_START_REQ( get_queue_status ) { req->clear_bits = flags; wine_server_call( req ); ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags ); } SERVER_END_REQ; return ret; } /*********************************************************************** * get_input_state */ DWORD get_input_state(void) { DWORD ret; check_for_events( QS_INPUT ); SERVER_START_REQ( get_queue_status ) { req->clear_bits = 0; wine_server_call( req ); ret = reply->wake_bits & (QS_KEY | QS_MOUSEBUTTON); } SERVER_END_REQ; return ret; } /*********************************************************************** * get_locale_kbd_layout */ static HKL get_locale_kbd_layout(void) { LCID layout; LANGID langid; /* FIXME: * * layout = main_key_tab[kbd_layout].lcid; * * Winword uses return value of GetKeyboardLayout as a codepage * to translate ANSI keyboard messages to unicode. But we have * a problem with it: for instance Polish keyboard layout is * identical to the US one, and therefore instead of the Polish * locale id we return the US one. */ NtQueryDefaultLocale( TRUE, &layout ); /* * Microsoft Office expects this value to be something specific * for Japanese and Korean Windows with an IME the value is 0xe001 * We should probably check to see if an IME exists and if so then * set this word properly. */ langid = PRIMARYLANGID( LANGIDFROMLCID( layout ) ); if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN) layout = MAKELONG( layout, 0xe001 ); /* IME */ else layout = MAKELONG( layout, layout ); return ULongToHandle( layout ); } /*********************************************************************** * NtUserGetKeyboardLayout (win32u.@) * * Device handle for keyboard layout defaulted to * the language id. This is the way Windows default works. */ HKL WINAPI NtUserGetKeyboardLayout( DWORD thread_id ) { struct user_thread_info *thread = get_user_thread_info(); HKL layout = thread->kbd_layout; if (thread_id && thread_id != GetCurrentThreadId()) FIXME( "couldn't return keyboard layout for thread %04x\n", thread_id ); if (!layout) return get_locale_kbd_layout(); return layout; } /********************************************************************** * NtUserGetKeyState (win32u.@) * * An application calls the GetKeyState function in response to a * keyboard-input message. This function retrieves the state of the key * at the time the input message was generated. */ SHORT WINAPI NtUserGetKeyState( INT vkey ) { SHORT retval = 0; SERVER_START_REQ( get_key_state ) { req->key = vkey; if (!wine_server_call( req )) retval = (signed char)(reply->state & 0x81); } SERVER_END_REQ; TRACE("key (0x%x) -> %x\n", vkey, retval); return retval; } /********************************************************************** * NtUserGetKeyboardState (win32u.@) */ BOOL WINAPI NtUserGetKeyboardState( BYTE *state ) { BOOL ret; UINT i; TRACE("(%p)\n", state); memset( state, 0, 256 ); SERVER_START_REQ( get_key_state ) { req->key = -1; wine_server_set_reply( req, state, 256 ); ret = !wine_server_call_err( req ); for (i = 0; i < 256; i++) state[i] &= 0x81; } SERVER_END_REQ; return ret; } /********************************************************************** * NtUserSetKeyboardState (win32u.@) */ BOOL WINAPI NtUserSetKeyboardState( BYTE *state ) { BOOL ret; SERVER_START_REQ( set_key_state ) { wine_server_add_data( req, state, 256 ); ret = !wine_server_call_err( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtUserVkKeyScanEx (win32u.@) */ WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout ) { WORD shift = 0x100, ctrl = 0x200; SHORT ret; TRACE_(keyboard)( "chr %s, layout %p\n", debugstr_wn(&chr, 1), layout ); if ((ret = user_driver->pVkKeyScanEx( chr, layout )) != -256) return ret; /* FIXME: English keyboard layout specific */ if (chr == VK_CANCEL || chr == VK_BACK || chr == VK_TAB || chr == VK_RETURN || chr == VK_ESCAPE || chr == VK_SPACE) ret = chr; else if (chr >= '0' && chr <= '9') ret = chr; else if (chr == ')') ret = shift + '0'; else if (chr == '!') ret = shift + '1'; else if (chr == '@') ret = shift + '2'; else if (chr == '#') ret = shift + '3'; else if (chr == '$') ret = shift + '4'; else if (chr == '%') ret = shift + '5'; else if (chr == '^') ret = shift + '6'; else if (chr == '&') ret = shift + '7'; else if (chr == '*') ret = shift + '8'; else if (chr == '(') ret = shift + '9'; else if (chr >= 'a' && chr <= 'z') ret = chr - 'a' + 'A'; else if (chr >= 'A' && chr <= 'Z') ret = shift + chr; else if (chr == ';') ret = VK_OEM_1; else if (chr == '=') ret = VK_OEM_PLUS; else if (chr == ',') ret = VK_OEM_COMMA; else if (chr == '-') ret = VK_OEM_MINUS; else if (chr == '.') ret = VK_OEM_PERIOD; else if (chr == '/') ret = VK_OEM_2; else if (chr == '`') ret = VK_OEM_3; else if (chr == '[') ret = VK_OEM_4; else if (chr == '\\') ret = VK_OEM_5; else if (chr == ']') ret = VK_OEM_6; else if (chr == '\'') ret = VK_OEM_7; else if (chr == ':') ret = shift + VK_OEM_1; else if (chr == '+') ret = shift + VK_OEM_PLUS; else if (chr == '<') ret = shift + VK_OEM_COMMA; else if (chr == '_') ret = shift + VK_OEM_MINUS; else if (chr == '>') ret = shift + VK_OEM_PERIOD; else if (chr == '?') ret = shift + VK_OEM_2; else if (chr == '~') ret = shift + VK_OEM_3; else if (chr == '{') ret = shift + VK_OEM_4; else if (chr == '|') ret = shift + VK_OEM_5; else if (chr == '}') ret = shift + VK_OEM_6; else if (chr == '\"') ret = shift + VK_OEM_7; else if (chr == 0x7f) ret = ctrl + VK_BACK; else if (chr == '\n') ret = ctrl + VK_RETURN; else if (chr == 0xf000) ret = ctrl + '2'; else if (chr == 0x0000) ret = ctrl + shift + '2'; else if (chr >= 0x0001 && chr <= 0x001a) ret = ctrl + 'A' + chr - 1; else if (chr >= 0x001c && chr <= 0x001d) ret = ctrl + VK_OEM_3 + chr; else if (chr == 0x001e) ret = ctrl + shift + '6'; else if (chr == 0x001f) ret = ctrl + shift + VK_OEM_MINUS; else ret = -1; TRACE_(keyboard)( "ret %04x\n", ret ); return ret; } /* English keyboard layout (0x0409) */ static const UINT kbd_en_vsc2vk[] = { 0x00, 0x1b, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0xbd, 0xbb, 0x08, 0x09, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4f, 0x50, 0xdb, 0xdd, 0x0d, 0xa2, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0xba, 0xde, 0xc0, 0xa0, 0xdc, 0x5a, 0x58, 0x43, 0x56, 0x42, 0x4e, 0x4d, 0xbc, 0xbe, 0xbf, 0xa1, 0x6a, 0xa4, 0x20, 0x14, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x90, 0x91, 0x24, 0x26, 0x21, 0x6d, 0x25, 0x0c, 0x27, 0x6b, 0x23, 0x28, 0x22, 0x2d, 0x2e, 0x2c, 0x00, 0xe2, 0x7a, 0x7b, 0x0c, 0xee, 0xf1, 0xea, 0xf9, 0xf5, 0xf3, 0x00, 0x00, 0xfb, 0x2f, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0xed, 0x00, 0xe9, 0x00, 0xc1, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x09, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe000 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x0d, 0xa3, 0x00, 0x00, 0xad, 0xb7, 0xb3, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xac, 0x00, 0x00, 0x6f, 0x00, 0x2c, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x24, 0x26, 0x21, 0x00, 0x25, 0x00, 0x27, 0x00, 0x23, 0x28, 0x22, 0x2d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xab, 0xa8, 0xa9, 0xa7, 0xa6, 0xb6, 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe100 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const UINT kbd_en_vk2char[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, ' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '+', 0x00, '-', '.', '/', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ';', '=', ',', '-', '.', '/', '`', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '[', '\\', ']', '\'', 0x00, 0x00, 0x00, '\\', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const char *kbd_en_vscname[] = { 0, "Esc", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Backspace", "Tab", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Enter", "Ctrl", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Shift", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Right Shift", "Num *", "Alt", "Space", "Caps Lock", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "Pause", "Scroll Lock", "Num 7", "Num 8", "Num 9", "Num -", "Num 4", "Num 5", "Num 6", "Num +", "Num 1", "Num 2", "Num 3", "Num 0", "Num Del", "Sys Req", 0, 0, "F11", "F12", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* extended */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Num Enter", "Right Ctrl", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Num /", 0, "Prnt Scrn", "Right Alt", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Num Lock", "Break", "Home", "Up", "Page Up", 0, "Left", 0, "Right", 0, "End", "Down", "Page Down", "Insert", "Delete", "<00>", 0, "Help", 0, 0, 0, 0, "Left Windows", "Right Windows", "Application", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /****************************************************************************** * NtUserMapVirtualKeyEx (win32u.@) */ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout ) { const UINT *vsc2vk, *vk2char; UINT vsc2vk_size, vk2char_size; UINT ret; TRACE_(keyboard)( "code %u, type %u, layout %p.\n", code, type, layout ); if ((ret = user_driver->pMapVirtualKeyEx( code, type, layout )) != -1) return ret; /* FIXME: English keyboard layout specific */ vsc2vk = kbd_en_vsc2vk; vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk); vk2char = kbd_en_vk2char; vk2char_size = ARRAYSIZE(kbd_en_vk2char); switch (type) { case MAPVK_VK_TO_VSC_EX: case MAPVK_VK_TO_VSC: switch (code) { case VK_SHIFT: code = VK_LSHIFT; break; case VK_CONTROL: code = VK_LCONTROL; break; case VK_MENU: code = VK_LMENU; break; case VK_NUMPAD0: code = VK_INSERT; break; case VK_NUMPAD1: code = VK_END; break; case VK_NUMPAD2: code = VK_DOWN; break; case VK_NUMPAD3: code = VK_NEXT; break; case VK_NUMPAD4: code = VK_LEFT; break; case VK_NUMPAD5: code = VK_CLEAR; break; case VK_NUMPAD6: code = VK_RIGHT; break; case VK_NUMPAD7: code = VK_HOME; break; case VK_NUMPAD8: code = VK_UP; break; case VK_NUMPAD9: code = VK_PRIOR; break; case VK_DECIMAL: code = VK_DELETE; break; } for (ret = 0; ret < vsc2vk_size; ++ret) if (vsc2vk[ret] == code) break; if (ret >= vsc2vk_size) ret = 0; if (type == MAPVK_VK_TO_VSC) { if (ret >= 0x200) ret = 0; else ret &= 0xff; } else if (ret >= 0x100) ret += 0xdf00; break; case MAPVK_VSC_TO_VK: case MAPVK_VSC_TO_VK_EX: if (code & 0xe000) code -= 0xdf00; if (code >= vsc2vk_size) ret = 0; else ret = vsc2vk[code]; if (type == MAPVK_VSC_TO_VK) { switch (ret) { case VK_LSHIFT: case VK_RSHIFT: ret = VK_SHIFT; break; case VK_LCONTROL: case VK_RCONTROL: ret = VK_CONTROL; break; case VK_LMENU: case VK_RMENU: ret = VK_MENU; break; } } break; case MAPVK_VK_TO_CHAR: if (code >= vk2char_size) ret = 0; else ret = vk2char[code]; break; default: FIXME_(keyboard)( "unknown type %d\n", type ); return 0; } TRACE_(keyboard)( "returning 0x%04x\n", ret ); return ret; } /**************************************************************************** * NtUserGetKeyNameText (win32u.@) */ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) { INT code = ((lparam >> 16) & 0x1ff), vkey, len; UINT vsc2vk_size, vscname_size; const char *const *vscname; const UINT *vsc2vk; TRACE_(keyboard)( "lparam %d, buffer %p, size %d.\n", lparam, buffer, size ); if (!buffer || !size) return 0; if ((len = user_driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len; /* FIXME: English keyboard layout specific */ vsc2vk = kbd_en_vsc2vk; vsc2vk_size = ARRAYSIZE(kbd_en_vsc2vk); vscname = kbd_en_vscname; vscname_size = ARRAYSIZE(kbd_en_vscname); if (lparam & 0x2000000) { switch ((vkey = vsc2vk[code])) { case VK_RSHIFT: case VK_RCONTROL: case VK_RMENU: for (code = 0; code < vsc2vk_size; ++code) if (vsc2vk[code] == (vkey - 1)) break; break; } } if (code < vscname_size) { if (vscname[code]) { len = min( size - 1, strlen(vscname[code]) ); ascii_to_unicode( buffer, vscname[code], len ); } else if (size > 1) { HKL hkl = NtUserGetKeyboardLayout( 0 ); vkey = NtUserMapVirtualKeyEx( code & 0xff, MAPVK_VSC_TO_VK, hkl ); buffer[0] = NtUserMapVirtualKeyEx( vkey, MAPVK_VK_TO_CHAR, hkl ); len = 1; } } buffer[len] = 0; TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(buffer) ); return len; } /**************************************************************************** * NtUserToUnicodeEx (win32u.@) */ INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state, WCHAR *str, int size, UINT flags, HKL layout ) { BOOL shift, ctrl, alt, numlock; WCHAR buffer[2]; INT len; TRACE_(keyboard)( "virt %u, scan %u, state %p, str %p, size %d, flags %x, layout %p.\n", virt, scan, state, str, size, flags, layout ); if (!state) return 0; if ((len = user_driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1) return len; alt = state[VK_MENU] & 0x80; shift = state[VK_SHIFT] & 0x80; ctrl = state[VK_CONTROL] & 0x80; numlock = state[VK_NUMLOCK] & 0x01; /* FIXME: English keyboard layout specific */ if (scan & 0x8000) buffer[0] = 0; /* key up */ else if (virt == VK_ESCAPE) buffer[0] = VK_ESCAPE; else if (!ctrl) { switch (virt) { case VK_BACK: buffer[0] = '\b'; break; case VK_OEM_1: buffer[0] = shift ? ':' : ';'; break; case VK_OEM_2: buffer[0] = shift ? '?' : '/'; break; case VK_OEM_3: buffer[0] = shift ? '~' : '`'; break; case VK_OEM_4: buffer[0] = shift ? '{' : '['; break; case VK_OEM_5: buffer[0] = shift ? '|' : '\\'; break; case VK_OEM_6: buffer[0] = shift ? '}' : ']'; break; case VK_OEM_7: buffer[0] = shift ? '"' : '\''; break; case VK_OEM_COMMA: buffer[0] = shift ? '<' : ','; break; case VK_OEM_MINUS: buffer[0] = shift ? '_' : '-'; break; case VK_OEM_PERIOD: buffer[0] = shift ? '>' : '.'; break; case VK_OEM_PLUS: buffer[0] = shift ? '+' : '='; break; case VK_RETURN: buffer[0] = '\r'; break; case VK_SPACE: buffer[0] = ' '; break; case VK_TAB: buffer[0] = '\t'; break; case VK_MULTIPLY: buffer[0] = '*'; break; case VK_ADD: buffer[0] = '+'; break; case VK_SUBTRACT: buffer[0] = '-'; break; case VK_DIVIDE: buffer[0] = '/'; break; default: if (virt >= '0' && virt <= '9') buffer[0] = shift ? ")!@#$%^&*("[virt - '0'] : virt; else if (virt >= 'A' && virt <= 'Z') buffer[0] = shift || (state[VK_CAPITAL] & 0x01) ? virt : virt + 'a' - 'A'; else if (virt >= VK_NUMPAD0 && virt <= VK_NUMPAD9 && numlock && !shift) buffer[0] = '0' + virt - VK_NUMPAD0; else if (virt == VK_DECIMAL && numlock && !shift) buffer[0] = '.'; else buffer[0] = 0; break; } } else if (!alt) /* Control codes */ { switch (virt) { case VK_OEM_4: buffer[0] = 0x1b; break; case VK_OEM_5: buffer[0] = 0x1c; break; case VK_OEM_6: buffer[0] = 0x1d; break; case '6': buffer[0] = shift ? 0x1e : 0; break; case VK_OEM_MINUS: buffer[0] = shift ? 0x1f : 0; break; case VK_BACK: buffer[0] = 0x7f; break; case VK_RETURN: buffer[0] = shift ? 0 : '\n'; break; case '2': buffer[0] = shift ? 0xffff : 0xf000; break; case VK_SPACE: buffer[0] = ' '; break; default: if (virt >= 'A' && virt <= 'Z') buffer[0] = virt - 'A' + 1; else buffer[0] = 0; break; } } else buffer[0] = 0; buffer[1] = 0; len = lstrlenW( buffer ); if (buffer[0] == 0xffff) buffer[0] = 0; lstrcpynW( str, buffer, size ); TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(str) ); return len; } /********************************************************************** * NtUserActivateKeyboardLayout (win32u.@) */ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) { struct user_thread_info *info = get_user_thread_info(); HKL old_layout; TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags ); if (flags) FIXME_(keyboard)( "flags %x not supported\n", flags ); if (layout == (HKL)HKL_NEXT || layout == (HKL)HKL_PREV) { SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); FIXME_(keyboard)( "HKL_NEXT and HKL_PREV not supported\n" ); return 0; } if (!user_driver->pActivateKeyboardLayout( layout, flags )) return 0; old_layout = info->kbd_layout; info->kbd_layout = layout; if (old_layout != layout) info->kbd_layout_id = 0; if (!old_layout) return get_locale_kbd_layout(); return old_layout; } /*********************************************************************** * NtUserGetKeyboardLayoutList (win32u.@) * * Return number of values available if either input parm is * 0, per MS documentation. */ UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts ) { char buffer[4096]; KEY_NODE_INFORMATION *key_info = (KEY_NODE_INFORMATION *)buffer; KEY_VALUE_PARTIAL_INFORMATION *value_info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; DWORD count, tmp, i = 0; HKEY hkey, subkey; HKL layout; TRACE_(keyboard)( "size %d, layouts %p.\n", size, layouts ); if ((count = user_driver->pGetKeyboardLayoutList( size, layouts )) != ~0) return count; layout = get_locale_kbd_layout(); count = 0; count++; if (size && layouts) { layouts[count - 1] = layout; if (count == size) return count; } if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) ))) { while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key_info, sizeof(buffer) - sizeof(WCHAR), &tmp )) { if (!(subkey = reg_open_key( hkey, key_info->Name, key_info->NameLength ))) continue; key_info->Name[key_info->NameLength / sizeof(WCHAR)] = 0; tmp = wcstoul( key_info->Name, NULL, 16 ); if (query_reg_ascii_value( subkey, "Layout Id", value_info, sizeof(buffer) ) && value_info->Type == REG_SZ) tmp = MAKELONG( LOWORD( tmp ), 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff) ); NtClose( subkey ); if (layout == UlongToHandle( tmp )) continue; count++; if (size && layouts) { layouts[count - 1] = UlongToHandle( tmp ); if (count == size) break; } } NtClose( hkey ); } return count; } /**************************************************************************** * NtUserGetKeyboardLayoutName (win32u.@) */ BOOL WINAPI NtUserGetKeyboardLayoutName( WCHAR *name ) { struct user_thread_info *info = get_user_thread_info(); char buffer[4096]; KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer; KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; WCHAR klid[KL_NAMELENGTH]; DWORD tmp, i = 0; HKEY hkey, subkey; HKL layout; TRACE_(keyboard)( "name %p\n", name ); if (!name) { SetLastError( ERROR_NOACCESS ); return FALSE; } if (info->kbd_layout_id) { sprintf( buffer, "%08X", info->kbd_layout_id ); asciiz_to_unicode( name, buffer ); return TRUE; } layout = NtUserGetKeyboardLayout( 0 ); tmp = HandleToUlong( layout ); if (HIWORD( tmp ) == LOWORD( tmp )) tmp = LOWORD( tmp ); sprintf( buffer, "%08X", tmp ); asciiz_to_unicode( name, buffer ); if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) ))) { while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer) - sizeof(WCHAR), &tmp )) { if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue; memcpy( klid, key->Name, key->NameLength ); klid[key->NameLength / sizeof(WCHAR)] = 0; if (query_reg_ascii_value( subkey, "Layout Id", value, sizeof(buffer) ) && value->Type == REG_SZ) tmp = 0xf000 | (wcstoul( (const WCHAR *)value->Data, NULL, 16 ) & 0xfff); else tmp = wcstoul( klid, NULL, 16 ); NtClose( subkey ); if (HIWORD( layout ) == tmp) { lstrcpynW( name, klid, KL_NAMELENGTH ); break; } } NtClose( hkey ); } info->kbd_layout_id = wcstoul( name, NULL, 16 ); TRACE_(keyboard)( "ret %s\n", debugstr_w( name ) ); return TRUE; } /*********************************************************************** * NtUserRegisterHotKey (win32u.@) */ BOOL WINAPI NtUserRegisterHotKey( HWND hwnd, INT id, UINT modifiers, UINT vk ) { BOOL ret; int replaced = 0; TRACE_(keyboard)( "(%p,%d,0x%08x,%X)\n", hwnd, id, modifiers, vk ); if ((!hwnd || is_current_thread_window( hwnd )) && !user_driver->pRegisterHotKey( hwnd, modifiers, vk )) return FALSE; SERVER_START_REQ( register_hotkey ) { req->window = wine_server_user_handle( hwnd ); req->id = id; req->flags = modifiers; req->vkey = vk; if ((ret = !wine_server_call_err( req ))) { replaced = reply->replaced; modifiers = reply->flags; vk = reply->vkey; } } SERVER_END_REQ; if (ret && replaced) user_driver->pUnregisterHotKey(hwnd, modifiers, vk); return ret; } /*********************************************************************** * NtUserUnregisterHotKey (win32u.@) */ BOOL WINAPI NtUserUnregisterHotKey( HWND hwnd, INT id ) { BOOL ret; UINT modifiers, vk; TRACE_(keyboard)("(%p,%d)\n",hwnd,id); SERVER_START_REQ( unregister_hotkey ) { req->window = wine_server_user_handle( hwnd ); req->id = id; if ((ret = !wine_server_call_err( req ))) { modifiers = reply->flags; vk = reply->vkey; } } SERVER_END_REQ; if (ret) user_driver->pUnregisterHotKey(hwnd, modifiers, vk); return ret; } /*********************************************************************** * NtUserGetMouseMovePointsEx (win32u.@) */ int WINAPI NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout, int count, DWORD resolution ) { cursor_pos_t *pos, positions[64]; int copied; unsigned int i; TRACE( "%d, %p, %p, %d, %d\n", size, ptin, ptout, count, resolution ); if ((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > ARRAY_SIZE( positions ))) { SetLastError( ERROR_INVALID_PARAMETER ); return -1; } if (!ptin || (!ptout && count)) { SetLastError( ERROR_NOACCESS ); return -1; } if (resolution != GMMP_USE_DISPLAY_POINTS) { FIXME( "only GMMP_USE_DISPLAY_POINTS is supported for now\n" ); SetLastError( ERROR_POINT_NOT_FOUND ); return -1; } SERVER_START_REQ( get_cursor_history ) { wine_server_set_reply( req, &positions, sizeof(positions) ); if (wine_server_call_err( req )) return -1; } SERVER_END_REQ; for (i = 0; i < ARRAY_SIZE( positions ); i++) { pos = &positions[i]; if (ptin->x == pos->x && ptin->y == pos->y && (!ptin->time || ptin->time == pos->time)) break; } if (i == ARRAY_SIZE( positions )) { SetLastError( ERROR_POINT_NOT_FOUND ); return -1; } for (copied = 0; copied < count && i < ARRAY_SIZE( positions ); copied++, i++) { pos = &positions[i]; ptout[copied].x = pos->x; ptout[copied].y = pos->y; ptout[copied].time = pos->time; ptout[copied].dwExtraInfo = pos->info; } return copied; } /********************************************************************** * set_capture_window */ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) { HWND previous = 0; UINT flags = 0; BOOL ret; if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU; if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE; SERVER_START_REQ( set_capture_window ) { req->handle = wine_server_user_handle( hwnd ); req->flags = flags; if ((ret = !wine_server_call_err( req ))) { previous = wine_server_ptr_handle( reply->previous ); hwnd = wine_server_ptr_handle( reply->full_handle ); } } SERVER_END_REQ; if (ret) { user_driver->pSetCapture( hwnd, gui_flags ); if (previous) send_message( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd ); if (prev_ret) *prev_ret = previous; } return ret; } /********************************************************************** * NtUserSetCapture (win32u.@) */ HWND WINAPI NtUserSetCapture( HWND hwnd ) { HWND previous = 0; set_capture_window( hwnd, 0, &previous ); return previous; } /********************************************************************** * release_capture */ BOOL WINAPI release_capture(void) { BOOL ret = set_capture_window( 0, 0, NULL ); /* Somebody may have missed some mouse movements */ if (ret) { INPUT input = { .type = INPUT_MOUSE }; input.mi.dwFlags = MOUSEEVENTF_MOVE; NtUserSendInput( 1, &input, sizeof(input) ); } return ret; } /******************************************************************* * NtUserGetForegroundWindow (win32u.@) */ HWND WINAPI NtUserGetForegroundWindow(void) { HWND ret = 0; SERVER_START_REQ( get_thread_input ) { req->tid = 0; if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground ); } SERVER_END_REQ; return ret; } /* see GetActiveWindow */ HWND get_active_window(void) { GUITHREADINFO info; info.cbSize = sizeof(info); return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0; } /* see GetFocus */ static HWND get_focus(void) { GUITHREADINFO info; info.cbSize = sizeof(info); return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0; } /***************************************************************** * set_focus_window * * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages */ static HWND set_focus_window( HWND hwnd ) { HWND previous = 0; BOOL ret; SERVER_START_REQ( set_focus_window ) { req->handle = wine_server_user_handle( hwnd ); if ((ret = !wine_server_call_err( req ))) previous = wine_server_ptr_handle( reply->previous ); } SERVER_END_REQ; if (!ret) return 0; if (previous == hwnd) return previous; if (previous) { send_message( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 ); if (user_callbacks) user_callbacks->notify_ime( previous, IME_INTERNAL_DEACTIVATE ); if (hwnd != get_focus()) return previous; /* changed by the message */ } if (is_window(hwnd)) { user_driver->pSetFocus(hwnd); if (user_callbacks) user_callbacks->notify_ime( hwnd, IME_INTERNAL_ACTIVATE ); if (previous) NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 ); send_message( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 ); } return previous; } /******************************************************************* * set_active_window */ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = get_active_window(); BOOL ret; DWORD old_thread, new_thread; CBTACTIVATESTRUCT cbt; if (previous == hwnd) { if (prev) *prev = hwnd; return TRUE; } /* call CBT hook chain */ cbt.fMouse = mouse; cbt.hWndActive = previous; if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE; if (is_window( previous )) { send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); send_message( previous, WM_ACTIVATE, MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd ); } SERVER_START_REQ( set_active_window ) { req->handle = wine_server_user_handle( hwnd ); if ((ret = !wine_server_call_err( req ))) previous = wine_server_ptr_handle( reply->previous ); } SERVER_END_REQ; if (!ret) return FALSE; if (prev) *prev = previous; if (previous == hwnd) return TRUE; if (hwnd) { /* send palette messages */ if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 ) && user_callbacks) user_callbacks->pSendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, SMTO_ABORTIFHUNG, 2000, NULL ); if (!is_window(hwnd)) return FALSE; } old_thread = previous ? get_window_thread( previous, NULL ) : 0; new_thread = hwnd ? get_window_thread( hwnd, NULL ) : 0; if (old_thread != new_thread) { HWND *list, *phwnd; if ((list = list_window_children( NULL, get_desktop_window(), NULL, 0 ))) { if (old_thread) { for (phwnd = list; *phwnd; phwnd++) { if (get_window_thread( *phwnd, NULL ) == old_thread) send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); } } if (new_thread) { for (phwnd = list; *phwnd; phwnd++) { if (get_window_thread( *phwnd, NULL ) == new_thread) send_message( *phwnd, WM_ACTIVATEAPP, 1, old_thread ); } } free( list ); } } if (is_window(hwnd)) { send_message( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous ); send_message( hwnd, WM_ACTIVATE, MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, is_iconic(hwnd) ), (LPARAM)previous ); if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window()) post_message( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd ); } /* now change focus if necessary */ if (focus) { GUITHREADINFO info; info.cbSize = sizeof(info); NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ); /* Do not change focus if the window is no more active */ if (hwnd == info.hwndActive) { if (!info.hwndFocus || !hwnd || NtUserGetAncestor( info.hwndFocus, GA_ROOT ) != hwnd) set_focus_window( hwnd ); } } return TRUE; } /********************************************************************** * NtUserSetActiveWindow (win32u.@) */ HWND WINAPI NtUserSetActiveWindow( HWND hwnd ) { HWND prev; TRACE( "%p\n", hwnd ); if (hwnd) { LONG style; hwnd = get_full_window_handle( hwnd ); if (!is_window( hwnd )) { SetLastError( ERROR_INVALID_WINDOW_HANDLE ); return 0; } style = get_window_long( hwnd, GWL_STYLE ); if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return get_active_window(); /* Windows doesn't seem to return an error here */ } if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0; return prev; } /***************************************************************** * NtUserSetFocus (win32u.@) */ HWND WINAPI NtUserSetFocus( HWND hwnd ) { HWND hwndTop = hwnd; HWND previous = get_focus(); TRACE( "%p prev %p\n", hwnd, previous ); if (hwnd) { /* Check if we can set the focus to this window */ hwnd = get_full_window_handle( hwnd ); if (!is_window( hwnd )) { SetLastError( ERROR_INVALID_WINDOW_HANDLE ); return 0; } if (hwnd == previous) return previous; /* nothing to do */ for (;;) { HWND parent; LONG style = get_window_long( hwndTop, GWL_STYLE ); if (style & (WS_MINIMIZE | WS_DISABLED)) return 0; if (!(style & WS_CHILD)) break; parent = NtUserGetAncestor( hwndTop, GA_PARENT ); if (!parent || parent == get_desktop_window()) { if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0; break; } if (parent == get_hwnd_message_parent()) return 0; hwndTop = parent; } /* call hooks */ if (call_hooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0; /* activate hwndTop if needed. */ if (hwndTop != get_active_window()) { if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0; if (!is_window( hwnd )) return 0; /* Abort if window destroyed */ /* Do not change focus if the window is no longer active */ if (hwndTop != get_active_window()) return 0; } } else /* NULL hwnd passed in */ { if (!previous) return 0; /* nothing to do */ if (call_hooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0; } /* change focus and send messages */ return set_focus_window( hwnd ); } /******************************************************************* * set_foreground_window */ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) { BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE; HWND previous = 0; if (mouse) hwnd = get_full_window_handle( hwnd ); SERVER_START_REQ( set_foreground_window ) { req->handle = wine_server_user_handle( hwnd ); if ((ret = !wine_server_call_err( req ))) { previous = wine_server_ptr_handle( reply->previous ); send_msg_old = reply->send_msg_old; send_msg_new = reply->send_msg_new; } } SERVER_END_REQ; if (ret && previous != hwnd) { if (send_msg_old) /* old window belongs to other thread */ NtUserMessageCall( previous, WM_WINE_SETACTIVEWINDOW, 0, 0, 0, FNID_SENDNOTIFYMESSAGE, FALSE ); else if (send_msg_new) /* old window belongs to us but new one to other thread */ ret = set_active_window( 0, NULL, mouse, TRUE ); if (send_msg_new) /* new window belongs to other thread */ NtUserMessageCall( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0, 0, FNID_SENDNOTIFYMESSAGE, FALSE ); else /* new window belongs to us */ ret = set_active_window( hwnd, NULL, mouse, TRUE ); } return ret; }