-- v2: winex11: Create registry keys for known Xkb layouts. winex11: Keep a list of every known Xkb layout. winex11: Read the _XKB_RULES_NAMES root window property. win32u: Init vsc2vk in NtUserGetKeyNameText only if necessary.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 11bb129134c..6a74a589311 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1113,17 +1113,16 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size ) INT code = ((lparam >> 16) & 0x1ff), vkey, len; const KBDTABLES *kbd_tables = &kbdus_tables; VSC_LPWSTR *key_name; - BYTE vsc2vk[0x300];
TRACE_(keyboard)( "lparam %#x, buffer %p, size %d.\n", (int)lparam, buffer, size );
if (!buffer || !size) return 0; if ((len = user_driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len;
- kbd_tables_init_vsc2vk( kbd_tables, vsc2vk ); - if (lparam & 0x2000000) { + BYTE vsc2vk[0x300]; + kbd_tables_init_vsc2vk( kbd_tables, vsc2vk ); switch ((vkey = vsc2vk[code])) { case VK_RSHIFT:
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/keyboard.c | 64 ++++++++++++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 3 files changed, 66 insertions(+)
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index b1c47d5258e..39f3323b1a4 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1514,6 +1514,68 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) TRACE("detected layout is "%s"\n", main_key_tab[kbd_layout].comment); }
+static void init_xkb_layouts( Display *display ) +{ + const char *rules, *model, *layouts, *layout, *variants, *variant, *options; + unsigned long count, remaining; + unsigned char *data; + int i, format; + Atom type; + + /* reset kbd_layout to some empty layout entry */ + while (main_key_tab[kbd_layout].comment) kbd_layout++; + + if (XGetWindowProperty( display, DefaultRootWindow( display ), x11drv_atom(_XKB_RULES_NAMES), 0, + 1024, False, XA_STRING, &type, &format, &count, &remaining, &data )) + data = NULL; + + if (!data || type != XA_STRING || format != 8 || remaining) + { + rules = model = layouts = variants = options = ""; + ERR( "Failed to read _XKB_RULES_NAMES property, assuming en-US QWERTY keyboard\n" ); + } + else + { + int pos = 0; + + rules = (char *)data + pos; + pos += strlen( rules ) + 1; + model = (char *)data + pos; + pos += strlen( model ) + 1; + layouts = (char *)data + pos; + pos += strlen( layouts ) + 1; + variants = (char *)data + pos; + pos += strlen( variants ) + 1; + options = (char *)data + pos; + pos += strlen( options ) + 1; + + TRACE( "Found rules %s, model %s, layouts %s, variants %s, options %s\n", debugstr_a(rules), + debugstr_a(model), debugstr_a(layouts), debugstr_a(variants), debugstr_a(options) ); + } + + /* There can be up to 4 active layouts, exposed as Xkb groups, + * with round robin if there is less than 4 configured layouts + */ + for (layout = layouts, variant = variants, i = 0; i < 4; ++i) + { + const char *next_layout = strchr( layout, ',' ), *next_variant = strchr( variant, ',' ); + int layout_len, variant_len; + + if (!next_layout) next_layout = layout + strlen( layout ); + if (!next_variant) next_variant = variant + strlen( variant ); + + layout_len = next_layout - layout; + variant_len = next_variant - variant; + + TRACE( "Found group %u layout %s variant %s\n", i, debugstr_an(layout, layout_len), debugstr_an(variant, variant_len) ); + + layout = *next_layout ? next_layout + 1 : layouts; + variant = *next_layout ? (*next_variant ? next_variant + 1 : next_variant) : variants; + } + + XFree( data ); +} +
/********************************************************************** * X11DRV_InitKeyboard @@ -1579,6 +1641,8 @@ void X11DRV_InitKeyboard( Display *display ) } XFreeModifiermap(mmp);
+ init_xkb_layouts( display ); + /* Detect the keyboard layout */ X11DRV_KEYBOARD_DetectLayout( display ); lkey = main_key_tab[kbd_layout].key; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index c6328cf3fde..ae3da561671 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -515,6 +515,7 @@ enum x11drv_atoms XATOM__GTK_WORKAREAS_D0, XATOM__XEMBED, XATOM__XEMBED_INFO, + XATOM__XKB_RULES_NAMES, XATOM_XdndAware, XATOM_XdndEnter, XATOM_XdndPosition, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 46f99e9b83b..6b7cab5a3f2 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -185,6 +185,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_GTK_WORKAREAS_D0", "_XEMBED", "_XEMBED_INFO", + "_XKB_RULES_NAMES", "XdndAware", "XdndEnter", "XdndPosition",
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/keyboard.c | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 39f3323b1a4..9af558ee538 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -51,6 +51,7 @@ #include "ime.h" #include "wine/server.h" #include "wine/debug.h" +#include "wine/list.h"
/* log format (add 0-padding as appropriate): keycode %u as in output from xev @@ -61,6 +62,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key);
+struct layout +{ + struct list entry; + + int xkb_group; + char *xkb_layout; +}; + static const unsigned int ControlMask = 1 << 2;
static int min_keycode, max_keycode, keysyms_per_keycode; @@ -69,9 +78,39 @@ static WORD keyc2vkey[256], keyc2scan[256]; static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */
static pthread_mutex_t kbd_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct list xkb_layouts = LIST_INIT( xkb_layouts );
static char KEYBOARD_MapDeadKeysym(KeySym keysym);
+static void create_layout_from_xkb( int xkb_group, const char *xkb_layout ) +{ + struct layout *layout; + + TRACE( "xkb_group %u, xkb_layout %s\n", xkb_group, xkb_layout ); + + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + { + if (!strcmp( layout->xkb_layout, xkb_layout )) + { + TRACE( "Found existing layout entry %p\n", layout ); + layout->xkb_group = xkb_group; + return; + } + } + + if (!(layout = calloc( 1, sizeof(*layout) + strlen( xkb_layout ) + 1 ))) + { + WARN( "Failed to allocate memory for Xkb layout entry\n" ); + return; + } + list_add_tail( &xkb_layouts, &layout->entry ); + + layout->xkb_group = xkb_group; + layout->xkb_layout = strcpy( (char *)(layout + 1), xkb_layout ); + + TRACE( "Created layout entry %p\n", layout ); +} + /* Keyboard translation tables */ #define MAIN_LEN 49 static const WORD main_key_scan_qwerty[MAIN_LEN] = @@ -1518,6 +1557,7 @@ static void init_xkb_layouts( Display *display ) { const char *rules, *model, *layouts, *layout, *variants, *variant, *options; unsigned long count, remaining; + struct layout *entry; unsigned char *data; int i, format; Atom type; @@ -1553,6 +1593,10 @@ static void init_xkb_layouts( Display *display ) debugstr_a(model), debugstr_a(layouts), debugstr_a(variants), debugstr_a(options) ); }
+ /* Flag any previously created Xkb layout as invalid */ + LIST_FOR_EACH_ENTRY( entry, &xkb_layouts, struct layout, entry ) + entry->xkb_group = -1; + /* There can be up to 4 active layouts, exposed as Xkb groups, * with round robin if there is less than 4 configured layouts */ @@ -1560,6 +1604,7 @@ static void init_xkb_layouts( Display *display ) { const char *next_layout = strchr( layout, ',' ), *next_variant = strchr( variant, ',' ); int layout_len, variant_len; + char buffer[1024];
if (!next_layout) next_layout = layout + strlen( layout ); if (!next_variant) next_variant = variant + strlen( variant ); @@ -1567,7 +1612,8 @@ static void init_xkb_layouts( Display *display ) layout_len = next_layout - layout; variant_len = next_variant - variant;
- TRACE( "Found group %u layout %s variant %s\n", i, debugstr_an(layout, layout_len), debugstr_an(variant, variant_len) ); + snprintf( buffer, ARRAY_SIZE(buffer), "%.*s:%.*s:%s", layout_len, layout, variant_len, variant, options ); + create_layout_from_xkb( i, buffer );
layout = *next_layout ? next_layout + 1 : layouts; variant = *next_layout ? (*next_variant ? next_variant + 1 : next_variant) : variants;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/input.c | 24 ++-- dlls/winex11.drv/keyboard.c | 226 +++++++++++++++++++++++++++++++++++- 2 files changed, 232 insertions(+), 18 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 8f3d5750c2f..8015b048527 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3337,7 +3337,6 @@ static void test_keyboard_layout_name(void) ActivateKeyboardLayout(layouts[i], 0);
tmplayout = GetKeyboardLayout(0); - todo_wine_if(tmplayout != layouts[i]) ok( tmplayout == layouts[i], "Failed to activate keyboard layout\n"); if (tmplayout != layouts[i]) { @@ -3352,21 +3351,25 @@ static void test_keyboard_layout_name(void) swprintf( tmpklid, KL_NAMELENGTH, L"%08X", layouts_preload[j] ); if (!wcscmp( tmpklid, klid )) break; } + todo_wine_if( layouts[i] != layout ) ok(j < len, "Could not find keyboard layout %s in preload list\n", wine_dbgstr_w(klid));
if (id & 0x80000000) { - todo_wine ok((id >> 28) == 0xf, "hkl high bits %#lx, expected 0xf\n", id >> 28); + todo_wine_if((UINT_PTR)layout >> 28 == 0xe) /* FIXME: winex11 currently fakes an IME on CJK locale */ + ok((id >> 28) == 0xf, "hkl high bits %#lx, expected 0xf\n", id >> 28);
value_size = sizeof(value); wcscpy(layout_path, L"System\CurrentControlSet\Control\Keyboard Layouts\"); wcscat(layout_path, klid); status = RegGetValueW(HKEY_LOCAL_MACHINE, layout_path, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size); - todo_wine ok(!status, "RegGetValueW returned %lx\n", status); + todo_wine_if((UINT_PTR)layout >> 28 == 0xe) /* FIXME: winex11 currently fakes an IME on CJK locale */ + ok(!status, "RegGetValueW returned %lx\n", status); ok(value_size == 5 * sizeof(WCHAR), "RegGetValueW returned size %ld\n", value_size);
swprintf(tmpklid, KL_NAMELENGTH, L"%04X", (id >> 16) & 0x0fff); - todo_wine ok(!wcsicmp(value, tmpklid), "RegGetValueW returned %s, expected %s\n", debugstr_w(value), debugstr_w(tmpklid)); + todo_wine_if((UINT_PTR)layout >> 28 == 0xe) /* FIXME: winex11 currently fakes an IME on CJK locale */ + ok(!wcsicmp(value, tmpklid), "RegGetValueW returned %s, expected %s\n", debugstr_w(value), debugstr_w(tmpklid)); } else { @@ -3378,7 +3381,7 @@ static void test_keyboard_layout_name(void) tmplayout = LoadKeyboardLayoutW(klid, KLF_ACTIVATE);
/* The low word of HKL is the selected user lang and may be different as LoadKeyboardLayoutW also selects the default lang from the layout */ - ok(((UINT_PTR)tmplayout & ~0xffff) == ((UINT_PTR)layouts[i] & ~0xffff), "LoadKeyboardLayoutW returned %p, expected %p\n", tmplayout, layouts[i]); + ok(HIWORD(tmplayout) == HIWORD(layouts[i]), "LoadKeyboardLayoutW returned %p, expected %p\n", tmplayout, layouts[i]);
/* The layout name only depends on the keyboard layout: the high word of HKL. */ GetKeyboardLayoutNameW(tmpklid); @@ -3423,6 +3426,7 @@ static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT
ok( !got_setfocus, "got WM_SETFOCUS before WM_INPUTLANGCHANGE\n" ); ok( layout == expect_hkl, "got layout %p\n", layout ); + todo_wine_if( wparam == 0xfe ) ok( wparam == info.ciCharset || broken(wparam == 0 && (HIWORD(layout) & 0x8000)), "got wparam %#Ix\n", wparam ); ok( lparam == (LPARAM)expect_hkl, "got lparam %#Ix\n", lparam ); @@ -3475,11 +3479,10 @@ static void test_ActivateKeyboardLayout( char **argv ) got_setfocus = 0; ActivateKeyboardLayout( other_layout, 0 ); if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); - else todo_wine ok( change_hkl == other_layout, "got change_hkl %p\n", change_hkl ); + else ok( change_hkl == other_layout, "got change_hkl %p\n", change_hkl ); change_hkl = expect_hkl = 0;
tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout );
/* changing the layout from another thread doesn't send the message */ @@ -3493,7 +3496,6 @@ static void test_ActivateKeyboardLayout( char **argv )
empty_message_queue(); tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout );
/* but the change only takes effect after focus changes */ @@ -3503,7 +3505,6 @@ static void test_ActivateKeyboardLayout( char **argv ) ok( !!hwnd2, "CreateWindow failed, error %lu\n", GetLastError() );
tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == layout || broken(layout != other_layout && tmp_layout == other_layout) /* w7u */, "got tmp_layout %p\n", tmp_layout ); if (broken(layout != other_layout && tmp_layout == other_layout)) @@ -3519,7 +3520,6 @@ static void test_ActivateKeyboardLayout( char **argv ) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl );
tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout );
thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); @@ -3528,7 +3528,6 @@ static void test_ActivateKeyboardLayout( char **argv ) CloseHandle( thread );
tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout );
/* changing focus is enough for the layout change to take effect */ @@ -3547,11 +3546,10 @@ static void test_ActivateKeyboardLayout( char **argv ) }
if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); - else todo_wine ok( change_hkl == layout, "got change_hkl %p\n", change_hkl ); + else ok( change_hkl == layout, "got change_hkl %p\n", change_hkl ); change_hkl = expect_hkl = 0;
tmp_layout = GetKeyboardLayout( 0 ); - todo_wine_if(layout != other_layout) ok( tmp_layout == layout, "got tmp_layout %p\n", tmp_layout );
DestroyWindow( hwnd2 ); diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 9af558ee538..9de8be71a18 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -42,6 +42,8 @@
#define NONAMELESSUNION
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "x11drv.h"
#include "wingdi.h" @@ -68,6 +70,11 @@ struct layout
int xkb_group; char *xkb_layout; + + LANGID lang; + WORD index; + /* "Layout Id", used by NtUserGetKeyboardLayoutName / LoadKeyboardLayoutW */ + WORD layout_id; };
static const unsigned int ControlMask = 1 << 2; @@ -82,20 +89,202 @@ static struct list xkb_layouts = LIST_INIT( xkb_layouts );
static char KEYBOARD_MapDeadKeysym(KeySym keysym);
-static void create_layout_from_xkb( int xkb_group, const char *xkb_layout ) +static inline LANGID langid_from_xkb_layout( const char *layout, size_t layout_len ) { +#define MAKEINDEX(c0, c1) (MAKEWORD(c0, c1) - MAKEWORD('a', 'a')) + static const LANGID langids[] = + { + [MAKEINDEX('a','f')] = MAKELANGID(LANG_DARI, SUBLANG_DEFAULT), + [MAKEINDEX('a','l')] = MAKELANGID(LANG_ALBANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('a','m')] = MAKELANGID(LANG_ARMENIAN, SUBLANG_DEFAULT), + [MAKEINDEX('a','t')] = MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN), + [MAKEINDEX('a','z')] = MAKELANGID(LANG_AZERBAIJANI, SUBLANG_DEFAULT), + [MAKEINDEX('b','a')] = MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC), + [MAKEINDEX('b','d')] = MAKELANGID(LANG_BANGLA, SUBLANG_DEFAULT), + [MAKEINDEX('b','e')] = MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN), + [MAKEINDEX('b','g')] = MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT), + [MAKEINDEX('b','r')] = MAKELANGID(LANG_PORTUGUESE, 2), + [MAKEINDEX('b','t')] = MAKELANGID(LANG_TIBETAN, 3), + [MAKEINDEX('b','w')] = MAKELANGID(LANG_TSWANA, SUBLANG_TSWANA_BOTSWANA), + [MAKEINDEX('b','y')] = MAKELANGID(LANG_BELARUSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('c','a')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN), + [MAKEINDEX('c','d')] = MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('c','h')] = MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS), + [MAKEINDEX('c','m')] = MAKELANGID(LANG_FRENCH, 11), + [MAKEINDEX('c','n')] = MAKELANGID(LANG_CHINESE, SUBLANG_DEFAULT), + [MAKEINDEX('c','z')] = MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT), + [MAKEINDEX('d','e')] = MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), + [MAKEINDEX('d','k')] = MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT), + [MAKEINDEX('d','z')] = MAKELANGID(LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN), + [MAKEINDEX('e','e')] = MAKELANGID(LANG_ESTONIAN, SUBLANG_DEFAULT), + [MAKEINDEX('e','s')] = MAKELANGID(LANG_SPANISH, SUBLANG_DEFAULT), + [MAKEINDEX('e','t')] = MAKELANGID(LANG_AMHARIC, SUBLANG_DEFAULT), + [MAKEINDEX('f','i')] = MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT), + [MAKEINDEX('f','o')] = MAKELANGID(LANG_FAEROESE, SUBLANG_DEFAULT), + [MAKEINDEX('f','r')] = MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), + [MAKEINDEX('g','b')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), + [MAKEINDEX('g','e')] = MAKELANGID(LANG_GEORGIAN, SUBLANG_DEFAULT), + [MAKEINDEX('g','h')] = MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('g','n')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('g','r')] = MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT), + [MAKEINDEX('h','r')] = MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT), + [MAKEINDEX('h','u')] = MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','d')] = MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','e')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), + [MAKEINDEX('i','l')] = MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), + [MAKEINDEX('i','n')] = MAKELANGID(LANG_HINDI, SUBLANG_DEFAULT), + [MAKEINDEX('i','r')] = MAKELANGID(LANG_PERSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('i','s')] = MAKELANGID(LANG_ICELANDIC, SUBLANG_DEFAULT), + [MAKEINDEX('i','t')] = MAKELANGID(LANG_ITALIAN, SUBLANG_DEFAULT), + [MAKEINDEX('j','p')] = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), + [MAKEINDEX('j','v')] = MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT), + [MAKEINDEX('k','e')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('k','g')] = MAKELANGID(LANG_KYRGYZ, SUBLANG_DEFAULT), + [MAKEINDEX('k','h')] = MAKELANGID(LANG_KHMER, SUBLANG_DEFAULT), + [MAKEINDEX('k','z')] = MAKELANGID(LANG_KAZAK, SUBLANG_DEFAULT), + [MAKEINDEX('l','a')] = MAKELANGID(LANG_LAO, SUBLANG_DEFAULT), + [MAKEINDEX('l','k')] = MAKELANGID(LANG_SINHALESE, SUBLANG_DEFAULT), + [MAKEINDEX('l','t')] = MAKELANGID(LANG_LITHUANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('l','v')] = MAKELANGID(LANG_LATVIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','a')] = MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), + [MAKEINDEX('m','d')] = MAKELANGID(LANG_ROMANIAN, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('m','e')] = MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_MONTENEGRO_LATIN), + [MAKEINDEX('m','k')] = MAKELANGID(LANG_MACEDONIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','l')] = MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), + [MAKEINDEX('m','m')] = MAKELANGID(0x55 /*LANG_BURMESE*/, SUBLANG_DEFAULT), + [MAKEINDEX('m','n')] = MAKELANGID(LANG_MONGOLIAN, SUBLANG_DEFAULT), + [MAKEINDEX('m','t')] = MAKELANGID(LANG_MALTESE, SUBLANG_DEFAULT), + [MAKEINDEX('m','v')] = MAKELANGID(LANG_DIVEHI, SUBLANG_DEFAULT), + [MAKEINDEX('m','y')] = MAKELANGID(LANG_MALAY, SUBLANG_DEFAULT), + [MAKEINDEX('n','g')] = MAKELANGID(LANG_ENGLISH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('n','l')] = MAKELANGID(LANG_DUTCH, SUBLANG_DEFAULT), + [MAKEINDEX('n','p')] = MAKELANGID(LANG_NEPALI, SUBLANG_DEFAULT), + [MAKEINDEX('p','h')] = MAKELANGID(LANG_FILIPINO, SUBLANG_DEFAULT), + [MAKEINDEX('p','k')] = MAKELANGID(LANG_URDU, SUBLANG_DEFAULT), + [MAKEINDEX('p','l')] = MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), + [MAKEINDEX('p','t')] = MAKELANGID(LANG_PORTUGUESE, SUBLANG_DEFAULT), + [MAKEINDEX('r','o')] = MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT), + [MAKEINDEX('r','s')] = MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN), + [MAKEINDEX('r','u')] = MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), + [MAKEINDEX('s','e')] = MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT), + [MAKEINDEX('s','i')] = MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT), + [MAKEINDEX('s','k')] = MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT), + [MAKEINDEX('s','n')] = MAKELANGID(LANG_WOLOF, SUBLANG_DEFAULT), + [MAKEINDEX('s','y')] = MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), + [MAKEINDEX('t','g')] = MAKELANGID(LANG_FRENCH, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('t','h')] = MAKELANGID(LANG_THAI, SUBLANG_DEFAULT), + [MAKEINDEX('t','j')] = MAKELANGID(LANG_TAJIK, SUBLANG_DEFAULT), + [MAKEINDEX('t','m')] = MAKELANGID(LANG_TURKMEN, SUBLANG_DEFAULT), + [MAKEINDEX('t','r')] = MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT), + [MAKEINDEX('t','w')] = MAKELANGID(LANG_CHINESE, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('t','z')] = MAKELANGID(LANG_SWAHILI, SUBLANG_CUSTOM_UNSPECIFIED), + [MAKEINDEX('u','a')] = MAKELANGID(LANG_UKRAINIAN, SUBLANG_DEFAULT), + [MAKEINDEX('u','s')] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + [MAKEINDEX('u','z')] = MAKELANGID(LANG_UZBEK, 2), + [MAKEINDEX('v','n')] = MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT), + [MAKEINDEX('z','a')] = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA), + }; + LANGID langid; + + if (layout_len == 2 && (langid = langids[MAKEINDEX(layout[0], layout[1])])) return langid; + if (layout_len == 3 && !memcmp( layout, "ara", layout_len )) return MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT); + if (layout_len == 3 && !memcmp( layout, "epo", layout_len )) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT); + if (layout_len == 3 && !memcmp( layout, "mao", layout_len )) return MAKELANGID(LANG_MAORI, SUBLANG_DEFAULT); + if (layout_len == 4 && !memcmp( layout, "brai", layout_len )) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT); + if (layout_len == 5 && !memcmp( layout, "latam", layout_len )) return MAKELANGID(LANG_SPANISH, SUBLANG_CUSTOM_UNSPECIFIED); + return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); +#undef MAKEINDEX +}; + +/* wrapper for NtCreateKey that creates the key recursively if necessary */ +static HKEY reg_create_key( HKEY root, const WCHAR *name, ULONG name_len, + DWORD options, DWORD *disposition ) +{ + UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name }; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE ret; + + attr.Length = sizeof(attr); + attr.RootDirectory = root; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtCreateKey( &ret, MAXIMUM_ALLOWED, &attr, 0, NULL, options, disposition ); + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + static const WCHAR registry_rootW[] = { '\','R','e','g','i','s','t','r','y','\' }; + DWORD pos = 0, i = 0, len = name_len / sizeof(WCHAR); + + /* don't try to create registry root */ + if (!root && len > ARRAY_SIZE(registry_rootW) && + !memcmp( name, registry_rootW, sizeof(registry_rootW) )) + i += ARRAY_SIZE(registry_rootW); + + while (i < len && name[i] != '\') i++; + if (i == len) return 0; + for (;;) + { + unsigned int subkey_options = options; + if (i < len) subkey_options &= ~(REG_OPTION_CREATE_LINK | REG_OPTION_OPEN_LINK); + nameW.Buffer = (WCHAR *)name + pos; + nameW.Length = (i - pos) * sizeof(WCHAR); + status = NtCreateKey( &ret, MAXIMUM_ALLOWED, &attr, 0, NULL, subkey_options, disposition ); + + if (attr.RootDirectory != root) NtClose( attr.RootDirectory ); + if (!NT_SUCCESS(status)) return 0; + if (i == len) break; + attr.RootDirectory = ret; + while (i < len && name[i] == '\') i++; + pos = i; + while (i < len && name[i] != '\') i++; + } + } + return ret; +} + +static BOOL set_reg_value( HKEY hkey, const WCHAR *name, UINT type, const void *value, DWORD count ) +{ + unsigned int name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0; + UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name }; + return !NtSetValueKey( hkey, &nameW, 0, type, value, count ); +} + +static void set_reg_ascii_value( HKEY hkey, const char *name, const char *value ) +{ + WCHAR nameW[64], valueW[128]; + asciiz_to_unicode( nameW, name ); + set_reg_value( hkey, nameW, REG_SZ, valueW, asciiz_to_unicode( valueW, value )); +} + +static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGID lang ) +{ + static WCHAR keyboard_layoutsW[] = + { + '\','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', + }; + static WORD next_layout_id = 1; + struct layout *layout; + WORD index = 0, len; + HKEY hkey, subkey;
- TRACE( "xkb_group %u, xkb_layout %s\n", xkb_group, xkb_layout ); + TRACE( "lang %04x, xkb_group %u, xkb_layout %s\n", lang, xkb_group, xkb_layout );
LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) { if (!strcmp( layout->xkb_layout, xkb_layout )) { - TRACE( "Found existing layout entry %p\n", layout ); + TRACE( "Found existing layout entry %p, hkl %04x%04x id %04x\n", + layout, layout->index, layout->lang, layout->layout_id ); layout->xkb_group = xkb_group; return; } + if (layout->lang == lang) index++; }
if (!(layout = calloc( 1, sizeof(*layout) + strlen( xkb_layout ) + 1 ))) @@ -108,7 +297,32 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout ) layout->xkb_group = xkb_group; layout->xkb_layout = strcpy( (char *)(layout + 1), xkb_layout );
- TRACE( "Created layout entry %p\n", layout ); + layout->lang = lang; + layout->index = index; + if (index) layout->layout_id = next_layout_id++; + + if ((hkey = reg_create_key( NULL, keyboard_layoutsW, ARRAY_SIZE(keyboard_layoutsW), 0, NULL ))) + { + WCHAR bufferW[MAX_PATH]; + char buffer[MAX_PATH]; + + sprintf( buffer, "%04x%04x", layout->index, layout->lang ); + len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR); + + if ((subkey = reg_create_key( hkey, bufferW, len, REG_OPTION_VOLATILE, NULL ))) + { + if (layout->layout_id) + { + sprintf( buffer, "%04x", layout->layout_id ); + set_reg_ascii_value( subkey, "Layout Id", buffer ); + } + NtClose( subkey ); + } + + NtClose( hkey ); + } + + TRACE( "Created layout entry %p, hkl %04x%04x id %04x\n", layout, layout->index, layout->lang, layout->layout_id ); }
/* Keyboard translation tables */ @@ -1605,6 +1819,7 @@ static void init_xkb_layouts( Display *display ) const char *next_layout = strchr( layout, ',' ), *next_variant = strchr( variant, ',' ); int layout_len, variant_len; char buffer[1024]; + LANGID lang;
if (!next_layout) next_layout = layout + strlen( layout ); if (!next_variant) next_variant = variant + strlen( variant ); @@ -1612,8 +1827,9 @@ static void init_xkb_layouts( Display *display ) layout_len = next_layout - layout; variant_len = next_variant - variant;
+ lang = langid_from_xkb_layout( layout, layout_len ); snprintf( buffer, ARRAY_SIZE(buffer), "%.*s:%.*s:%s", layout_len, layout, variant_len, variant, options ); - create_layout_from_xkb( i, buffer ); + create_layout_from_xkb( i, buffer, lang );
layout = *next_layout ? next_layout + 1 : layouts; variant = *next_layout ? (*next_variant ? next_variant + 1 : next_variant) : variants;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=130620
Your paranoid android.
=== w7u_el (32 bit report) ===
user32: input.c:2287: Test failed: GetRawInputData succeeded input.c:2288: Test failed: GetRawInputData returned deadbeef
v2: Keep todo_wine for CJK locale where winex11 fakes an IME layout.
This merge request was closed by Rémi Bernon.
Closing this for now, I think it does the right thing but it could be done more like winewayland, and I will get back to it later.