[PATCH v3 0/8] MR11367: winex11: Keyboard layouts rework, part 4
-- v3: user32/tests: Add a test for WM_INPUTLANGCHANGE to a child window. win32u: Don't send WM_INPUTLANGCHANGE messages to the desktop window. winex11: Handle XkbMapNotify event. winex11: Handle XkbNewKeyboardNotify event. winex11: Handle XkbStateNotify event to detect keyboard layout changes. winex11: Activate initial keyboard layout. winex11: Get rid of the obsolete global layout selector. winex11: Use static scan code to vkey mapping tables. https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 656 +++++++++++++-------------------- dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 3 +- 3 files changed, 251 insertions(+), 409 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index e909e0a56ec..0cdf6220337 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -84,14 +84,94 @@ struct layout LANGID lang; DWORD klid; WORD layout_id; + + const USHORT *scan2vk; }; static const unsigned int ControlMask = 1 << 2; static int min_keycode, max_keycode, keysyms_per_keycode; -static WORD keyc2vkey[256], default_keyc2scan[0x100]; +static WORD default_keyc2scan[0x100]; static BOOL use_xkb; +#define EXTRA_SCAN2VK \ + T36 | KBDEXT, T37 | KBDMULTIVK, \ + T38, T39, T3A, T3B, T3C, T3D, T3E, T3F, \ + T40, T41, T42, T43, T44, T45 | KBDEXT | KBDMULTIVK, T46 | KBDMULTIVK, T47 | KBDNUMPAD | KBDSPECIAL, \ + T48 | KBDNUMPAD | KBDSPECIAL, T49 | KBDNUMPAD | KBDSPECIAL, T4A, T4B | KBDNUMPAD | KBDSPECIAL, \ + T4C | KBDNUMPAD | KBDSPECIAL, T4D | KBDNUMPAD | KBDSPECIAL, T4E, T4F | KBDNUMPAD | KBDSPECIAL, \ + T50 | KBDNUMPAD | KBDSPECIAL, T51 | KBDNUMPAD | KBDSPECIAL, T52 | KBDNUMPAD | KBDSPECIAL, \ + T53 | KBDNUMPAD | KBDSPECIAL, T54, T55, T56, T57, \ + T58, T59, T5A, T5B, T5C, T5D, T5E, T5F, \ + T60, T61, T62, T63, T64, T65, T66, T67, \ + T68, T69, T6A, T6B, T6C, T6D, T6E, T6F, \ + T70, T71, T72, T73, T74, T75, T76, T77, \ + T78, T79, T7A, T7B, T7C, T7D, T7E, \ + [0x110] = X10 | KBDEXT, [0x119] = X19 | KBDEXT, [0x11d] = X1D | KBDEXT, [0x120] = X20 | KBDEXT, \ + [0x121] = X21 | KBDEXT, [0x122] = X22 | KBDEXT, [0x124] = X24 | KBDEXT, [0x12e] = X2E | KBDEXT, \ + [0x130] = X30 | KBDEXT, [0x132] = X32 | KBDEXT, [0x135] = X35 | KBDEXT, [0x137] = X37 | KBDEXT, \ + [0x138] = X38 | KBDEXT, [0x147] = X47 | KBDEXT, [0x148] = X48 | KBDEXT, [0x149] = X49 | KBDEXT, \ + [0x14b] = X4B | KBDEXT, [0x14d] = X4D | KBDEXT, [0x14f] = X4F | KBDEXT, [0x150] = X50 | KBDEXT, \ + [0x151] = X51 | KBDEXT, [0x152] = X52 | KBDEXT, [0x153] = X53 | KBDEXT, [0x15b] = X5B | KBDEXT, \ + [0x15c] = X5C | KBDEXT, [0x15d] = X5D | KBDEXT, [0x15f] = X5F | KBDEXT, [0x165] = X65 | KBDEXT, \ + [0x166] = X66 | KBDEXT, [0x167] = X67 | KBDEXT, [0x168] = X68 | KBDEXT, [0x169] = X69 | KBDEXT, \ + [0x16a] = X6A | KBDEXT, [0x16b] = X6B | KBDEXT, [0x16c] = X6C | KBDEXT, [0x16d] = X6D | KBDEXT, \ + [0x11c] = X1C | KBDEXT, [0x146] = X46 | KBDEXT, [0x21d] = Y1D, + +static const USHORT scan2vk_qwerty[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, T0C, T0D, T0E, + T0F, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T1A, T1B, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T2A, T2B, T2C, T2D, T2E, T2F, T30, T31, T32, T33, T34, T35, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_qwerty_sw[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_PLUS, VK_OEM_4, T0E, + T0F, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, VK_OEM_6, VK_OEM_1, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, VK_OEM_3, T28, VK_OEM_5, + T2A, VK_OEM_2, T2C, T2D, T2E, T2F, T30, T31, T32, T33, T34, VK_OEM_MINUS, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_qwerty_jp106[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, T0C, VK_OEM_7, T0E, + T0F, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, VK_OEM_3, VK_OEM_4, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, VK_OEM_PLUS, VK_OEM_1, VK_OEM_AUTO, + T2A, VK_OEM_6, T2C, T2D, T2E, T2F, T30, T31, T32, T33, T34, T35, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_azerty[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, T0D, T0E, + T0F, 'A', 'Z', T12, T13, T14, T15, T16, T17, T18, T19, VK_OEM_6, VK_OEM_1, T1C, + T1D, 'Q', T1F, T20, T21, T22, T23, T24, T25, T26, 'M', VK_OEM_3, VK_OEM_7, + T2A, T2B, 'W', T2D, T2E, T2F, T30, T31, VK_OEM_COMMA, VK_OEM_PERIOD, VK_OEM_2, VK_OEM_8, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_qwertz[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, VK_OEM_6, T0E, + T0F, T10, T11, T12, T13, T14, 'Z', T16, T17, T18, T19, VK_OEM_1, VK_OEM_3, T1C, + T1D, T1E, T1F, T20, T21, T22, T23, T24, T25, T26, VK_OEM_7, VK_OEM_5, VK_OEM_2, + T2A, VK_OEM_8, 'Y', T2D, T2E, T2F, T30, T31, T32, T33, T34, VK_OEM_MINUS, + EXTRA_SCAN2VK +}; + +static const USHORT scan2vk_dvorak[0x280] = +{ + T00, T01, T02, T03, T04, T05, T06, T07, T08, T09, T0A, T0B, VK_OEM_4, VK_OEM_6, T0E, + T0F, VK_OEM_7, VK_OEM_COMMA, VK_OEM_PERIOD, 'P', 'Y', 'F', 'G', 'C', 'R', 'L', VK_OEM_2, VK_OEM_PLUS, T1C, + T1D, T1E, 'O', 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S', VK_OEM_MINUS, T29, + T2A, T2B, VK_OEM_1, 'Q', 'J', 'K', 'X', 'B', 'M', 'W', 'V', 'Z', + EXTRA_SCAN2VK +}; + static void init_default_keyc2scan( Display *display ) { static const struct @@ -326,80 +406,22 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGI layout->klid = !klid ? MAKELONG(lang, 0) : (klid == -1) ? MAKELONG(lang, index + 0x20) : klid; layout->layout_id = get_layout_id_from_klid( klid ); + switch (lang) + { + case MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT): layout->scan2vk = scan2vk_azerty; break; + case MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT): layout->scan2vk = scan2vk_qwertz; break; + case MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS): layout->scan2vk = scan2vk_qwertz; break; + case MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT): layout->scan2vk = scan2vk_qwerty_sw; break; + case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): layout->scan2vk = scan2vk_qwerty_jp106; break; + default: layout->scan2vk = scan2vk_qwerty; + } + if (klid == 0x00010409) layout->scan2vk = scan2vk_dvorak; + TRACE( "Created layout entry %p, lang %04x, klid %08x, layout_id %04x\n", layout, layout->lang, layout->klid, layout->layout_id ); } /* Keyboard translation tables */ #define MAIN_LEN 49 -static const WORD main_key_vkey_qwerty[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_3,'1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS, - 'Q','W','E','R','T','Y','U','I','O','P',VK_OEM_4,VK_OEM_6, - 'A','S','D','F','G','H','J','K','L',VK_OEM_1,VK_OEM_7,VK_OEM_5, - 'Z','X','C','V','B','N','M',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2, - VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_vkey_qwerty_jp106[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - '1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_7,VK_OEM_5, - 'Q','W','E','R','T','Y','U','I','O','P',VK_OEM_3,VK_OEM_4, - 'A','S','D','F','G','H','J','K','L',VK_OEM_PLUS,VK_OEM_1,VK_OEM_6, - 'Z','X','C','V','B','N','M',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2, - VK_OEM_102 /* the 102nd key (actually to the left of r-shift) */ -}; - -static const WORD main_key_vkey_qwerty_v2[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_5,'1','2','3','4','5','6','7','8','9','0',VK_OEM_PLUS,VK_OEM_4, - 'Q','W','E','R','T','Y','U','I','O','P',VK_OEM_6,VK_OEM_1, - 'A','S','D','F','G','H','J','K','L',VK_OEM_3,VK_OEM_7,VK_OEM_2, - 'Z','X','C','V','B','N','M',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_MINUS, - VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_vkey_qwertz[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_3,'1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS, - 'Q','W','E','R','T','Z','U','I','O','P',VK_OEM_4,VK_OEM_6, - 'A','S','D','F','G','H','J','K','L',VK_OEM_1,VK_OEM_7,VK_OEM_5, - 'Y','X','C','V','B','N','M',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2, - VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_vkey_abnt_qwerty[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_3,'1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS, - 'Q','W','E','R','T','Y','U','I','O','P',VK_OEM_4,VK_OEM_6, - 'A','S','D','F','G','H','J','K','L',VK_OEM_1,VK_OEM_8,VK_OEM_5, - VK_OEM_7,'Z','X','C','V','B','N','M',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2, - VK_OEM_102, /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_vkey_azerty[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_7,'1','2','3','4','5','6','7','8','9','0',VK_OEM_4,VK_OEM_PLUS, - 'A','Z','E','R','T','Y','U','I','O','P',VK_OEM_6,VK_OEM_1, - 'Q','S','D','F','G','H','J','K','L','M',VK_OEM_3,VK_OEM_5, - 'W','X','C','V','B','N',VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_8, - VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */ -}; - -static const WORD main_key_vkey_dvorak[MAIN_LEN] = -{ -/* NOTE: this layout must concur with the scan codes layout above */ - VK_OEM_3,'1','2','3','4','5','6','7','8','9','0',VK_OEM_4,VK_OEM_6, - VK_OEM_7,VK_OEM_COMMA,VK_OEM_PERIOD,'P','Y','F','G','C','R','L',VK_OEM_2,VK_OEM_PLUS, - 'A','O','E','U','I','D','H','T','N','S',VK_OEM_MINUS,VK_OEM_5, - VK_OEM_1,'Q','J','K','X','B','M','W','V','Z', - VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */ -}; /*** DEFINE YOUR NEW LANGUAGE-SPECIFIC MAPPINGS BELOW, SEE EXISTING TABLES */ @@ -1048,13 +1070,6 @@ static const char main_key_th[MAIN_LEN][4] = }; /*** VNC keyboard layout */ -static const WORD main_key_vkey_vnc[MAIN_LEN] = -{ - '1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS,VK_OEM_4,VK_OEM_6,VK_OEM_1,VK_OEM_7,VK_OEM_3,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_5, - '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', - VK_OEM_102 -}; - static const char main_key_vnc[MAIN_LEN][4] = { "1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+","[{","]}",";:","'\"","`~",",<",".>","/?","\\|", @@ -1079,74 +1094,73 @@ static const struct { in the appropriate dlls/kernel/nls/.nls file */ const char *comment; const char (*key)[MAIN_LEN][4]; - const WORD (*vkey)[MAIN_LEN]; /* virtual key codes mapping */ } main_key_tab[]={ - {0x0409, "United States keyboard layout", &main_key_US, &main_key_vkey_qwerty}, - {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_vkey_qwerty}, - {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_vkey_dvorak}, - {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_vkey_dvorak}, - {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_vkey_dvorak}, - {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_vkey_qwerty}, - {0x0809, "British keyboard layout", &main_key_UK, &main_key_vkey_qwerty}, - {0x0407, "German keyboard layout", &main_key_DE, &main_key_vkey_qwertz}, - {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_vkey_qwertz}, - {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_vkey_qwertz}, - {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_vkey_qwerty_v2}, - {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_vkey_qwerty}, - {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_vkey_qwerty}, - {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_vkey_qwerty}, - {0x040c, "French keyboard layout", &main_key_FR, &main_key_vkey_azerty}, - {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_vkey_qwerty}, - {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_vkey_qwerty}, - {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_vkey_qwerty}, - {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_vkey_azerty}, - {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_vkey_qwerty}, - {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_vkey_abnt_qwerty}, - {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_vkey_abnt_qwerty}, - {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_vkey_qwerty}, - {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_vkey_qwerty}, - {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_vkey_qwerty}, - {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_vkey_qwerty}, - {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_vkey_qwerty}, - {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_vkey_qwerty}, - {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_vkey_qwerty}, - {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_vkey_qwerty}, - {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_vkey_qwerty}, - {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_vkey_qwerty}, - {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_vkey_qwerty}, - {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_vkey_qwertz}, - {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_vkey_qwerty}, - {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_vkey_qwertz}, - {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ - {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ - {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_vkey_qwertz}, - {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_vkey_qwerty}, - {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_vkey_qwerty_jp106}, - {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp, &main_key_vkey_qwerty_jp106}, - {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_vkey_qwerty}, - {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_vkey_qwerty}, - {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_vkey_qwerty}, - {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_vkey_qwerty}, - {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_vkey_qwertz}, - {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_vkey_qwerty}, - {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_vkey_qwerty}, - {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_vkey_qwerty}, - {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_vkey_qwerty}, - {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_vkey_qwerty}, - {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_vkey_qwerty}, - {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_vkey_qwerty}, - {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_vkey_vnc}, - {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_vkey_qwerty}, - {0x041e, "Thai (Kedmanee) keyboard layout", &main_key_th, &main_key_vkey_qwerty}, - {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_vkey_qwerty}, - - {0, NULL, NULL, NULL} /* sentinel */ + {0x0409, "United States keyboard layout", &main_key_US}, + {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom}, + {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak}, + {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak}, + {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom}, + {0x0409, "United States International keyboard layout", &main_key_US_intl}, + {0x0809, "British keyboard layout", &main_key_UK}, + {0x0407, "German keyboard layout", &main_key_DE}, + {0x0807, "Swiss German keyboard layout", &main_key_SG}, + {0x100c, "Swiss French keyboard layout", &main_key_SF}, + {0x041d, "Swedish keyboard layout", &main_key_SE}, + {0x0425, "Estonian keyboard layout", &main_key_ET}, + {0x0414, "Norwegian keyboard layout", &main_key_NO}, + {0x0406, "Danish keyboard layout", &main_key_DA}, + {0x040c, "French keyboard layout", &main_key_FR}, + {0x0c0c, "Canadian French keyboard layout", &main_key_CF}, + {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr}, + {0x0c0c, "Canadian keyboard layout", &main_key_CA}, + {0x080c, "Belgian keyboard layout", &main_key_BE}, + {0x0816, "Portuguese keyboard layout", &main_key_PT}, + {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br}, + {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr}, + {0x040b, "Finnish keyboard layout", &main_key_FI}, + {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds}, + {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic}, + {0x0423, "Belarusian keyboard layout", &main_key_BY}, + {0x0419, "Russian keyboard layout", &main_key_RU}, + {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom}, + {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r}, + {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251}, + {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic}, + {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA}, + {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std}, + {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std}, + {0x040a, "Spanish keyboard layout", &main_key_ES}, + {0x0410, "Italian keyboard layout", &main_key_IT}, + {0x040f, "Icelandic keyboard layout", &main_key_IS}, + {0x040e, "Hungarian keyboard layout", &main_key_HU}, + {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL}, + {0x0424, "Slovenian keyboard layout", &main_key_SI}, + {0x0c1a, "Serbian keyboard layout sr", &main_key_SR}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ + {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */ + {0x041a, "Croatian keyboard layout", &main_key_HR}, + {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly}, + {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106}, + {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp}, + {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1}, + {0x041b, "Slovak keyboard layout", &main_key_SK}, + {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog}, + {0x0405, "Czech keyboard layout", &main_key_CS}, + {0x0405, "Czech keyboard layout cz", &main_key_CZ}, + {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty}, + {0x040a, "Latin American keyboard layout", &main_key_LA}, + {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B}, + {0x041f, "Turkish keyboard layout", &main_key_TK}, + {0x041f, "Turkish keyboard layout tr", &main_key_TR}, + {0x041f, "Turkish keyboard layout trf", &main_key_TR_F}, + {0x040d, "Israelian keyboard layout", &main_key_IL}, + {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic}, + {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon}, + {0x0409, "VNC keyboard layout", &main_key_vnc}, + {0x0408, "Greek keyboard layout", &main_key_EL}, + {0x041e, "Thai (Kedmanee) keyboard layout", &main_key_th}, + {0x0413, "Dutch keyboard layout", &main_key_NL}, + + {0, NULL, NULL} /* sentinel */ }; static unsigned kbd_layout=0; /* index into above table of layouts */ #ifdef SONAME_LIBXKBREGISTRY @@ -1230,53 +1244,64 @@ static const WORD nonchar_key_vkey[256] = 0, 0, 0, 0, 0, 0, 0, VK_DELETE /* FFF8 */ }; -static const WORD xfree86_vendor_key_vkey[256] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF00 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF08 */ - 0, VK_VOLUME_DOWN, VK_VOLUME_MUTE, VK_VOLUME_UP, /* 1008FF10 */ - VK_MEDIA_PLAY_PAUSE, VK_MEDIA_STOP, - VK_MEDIA_PREV_TRACK, VK_MEDIA_NEXT_TRACK, - 0, VK_LAUNCH_MAIL, 0, VK_BROWSER_SEARCH, /* 1008FF18 */ - 0, 0, 0, VK_BROWSER_HOME, - 0, 0, 0, 0, 0, 0, VK_BROWSER_BACK, VK_BROWSER_FORWARD, /* 1008FF20 */ - VK_BROWSER_STOP, VK_BROWSER_REFRESH, 0, 0, 0, 0, 0, 0, /* 1008FF28 */ - VK_BROWSER_FAVORITES, 0, VK_LAUNCH_MEDIA_SELECT, 0, /* 1008FF30 */ - 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF38 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF40 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF48 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF50 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF58 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF60 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF68 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF70 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF78 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF80 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF88 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF90 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FF98 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFA0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFA8 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFB0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFB8 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFC0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFC8 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFD0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFD8 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFE0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFE8 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 1008FFF0 */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 1008FFF8 */ -}; +/* Needs to be called while holding kbd_mutex. */ +static struct layout *get_layout_from_hkl_unlocked( HKL hkl ) +{ + struct layout *layout; + + TRACE( "hkl %p\n", hkl ); + + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + { + if (!layout->layout_id && layout->lang == HIWORD(hkl)) break; + if (layout->layout_id && layout->layout_id == HIWORD(hkl)) break; + } + if (&layout->entry == &xkb_layouts) layout = NULL; + return layout; +} + +static struct layout *get_layout_from_hkl( HKL hkl ) +{ + struct layout *layout; + + TRACE( "hkl %p\n", hkl ); + + pthread_mutex_lock( &kbd_mutex ); + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + { + if (!layout->layout_id && layout->lang == HIWORD(hkl)) break; + if (layout->layout_id && layout->layout_id == HIWORD(hkl)) break; + } + if (&layout->entry == &xkb_layouts) layout = NULL; + pthread_mutex_unlock( &kbd_mutex ); + return layout; +} + +/* Needs to be called while holding kbd_mutex. */ +static struct layout *get_current_layout_unlocked( void ) +{ + HKL hkl = NtUserGetKeyboardLayout( 0 ); + + return get_layout_from_hkl_unlocked( hkl ); +} + +static struct layout *get_current_layout( void ) +{ + HKL hkl = NtUserGetKeyboardLayout( 0 ); + + return get_layout_from_hkl( hkl ); +} /* Returns the Windows virtual key code associated with the X event <e> */ /* kbd_section must be held */ static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e) { + const struct layout *layout = get_current_layout_unlocked(); + const USHORT *scan2vk = layout->scan2vk; KeySym keysym = 0; Status status; char buf[24]; + WORD scan; /* Clients should pass only KeyPress events to XmbLookupString */ if (xic && e->type == KeyPress) @@ -1298,7 +1323,8 @@ static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e) TRACE_(key)("e->keycode = %u\n", e->keycode); - return keyc2vkey[e->keycode]; + scan = keyc2scan( e->keycode, e->state ); + return scan2vk[scan]; } @@ -1367,6 +1393,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) WORD vkey; DWORD flags; KeyCode keycode; + WORD scan; HWND keymapnotify_hwnd; BOOL changed = FALSE; struct { @@ -1375,6 +1402,8 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) WORD pressed; } keys[256]; struct x11drv_thread_data *thread_data = x11drv_thread_data(); + const struct layout *layout = get_current_layout(); + const USHORT *scan2vk = layout->scan2vk; keymapnotify_hwnd = thread_data->keymapnotify_hwnd; thread_data->keymapnotify_hwnd = NULL; @@ -1393,7 +1422,8 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) for (j = 0; j < 8; j++) { keycode = (i * 8) + j; - vkey = keyc2vkey[keycode]; + scan = keyc2scan( keycode, 0 ); + vkey = scan2vk[scan]; /* If multiple keys map to the same vkey, we want to report it as * pressed iff any of them are pressed. */ @@ -1728,7 +1758,7 @@ static LANGID langid_from_xkb_layout( const char *layout ) FIXME( "Unknown layout %s\n", debugstr_a(layout) ); return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); -}; +} static const char *xkb_layout_from_langid( LANGID langid ) { @@ -1899,216 +1929,6 @@ static unsigned int detect_keyboard_layout( Display *display, XModifierKeymap *m } -/* initialize keyc2vkey */ -static void init_keycode_mappings( Display *display ) -{ - KeySym keysym; - XKeyEvent e2; - WORD vkey; - int keyc, i, keyn, syms; - char ckey[4]={0,0,0,0}; - const char (*lkey)[MAIN_LEN][4]; - char vkey_used[256] = { 0 }; - - /* Ranges of OEM, function key, and character virtual key codes. - * Don't include those handled specially in X11DRV_ToUnicodeEx and - * X11DRV_MapVirtualKeyEx, like VK_NUMPAD0 - VK_DIVIDE. */ - static const struct { - WORD first, last; - } vkey_ranges[] = { - { VK_OEM_1, VK_OEM_3 }, - { VK_OEM_4, VK_OEM_8 }, - { VK_OEM_AX, VK_ICO_00 }, - { 0xe6, 0xe6 }, - { 0xe9, 0xf5 }, - { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL }, - { VK_F1, VK_F24 }, - { 0x30, 0x39 }, /* VK_0 - VK_9 */ - { 0x41, 0x5a }, /* VK_A - VK_Z */ - { 0, 0 } - }; - int vkey_range; - - lkey = main_key_tab[kbd_layout].key; - syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode; - - /* Now build two conversion arrays : - * keycode -> vkey + scancode + extended - * vkey + extended -> keycode */ - - e2.display = display; - e2.state = 0; - e2.type = KeyPress; - - memset(keyc2vkey, 0, sizeof(keyc2vkey)); - for (keyc = min_keycode; keyc <= max_keycode; keyc++) - { - char buf[30]; - int have_chars; - - keysym = 0; - e2.keycode = (KeyCode)keyc; - have_chars = XLookupString(&e2, buf, sizeof(buf), &keysym, NULL); - vkey = 0; - if (keysym) /* otherwise, keycode not used */ - { - if ((keysym >> 8) == 0xFF) /* non-character key */ - { - vkey = nonchar_key_vkey[keysym & 0xff]; - } else if ((keysym >> 8) == 0x1008FF) { /* XFree86 vendor keys */ - vkey = xfree86_vendor_key_vkey[keysym & 0xff]; - /* All vendor keys are extended with a scan code of 0 per testing on WinXP */ - vkey |= 0x100; - } else if (keysym == 0x20) { /* Spacebar */ - vkey = VK_SPACE; - } else if (have_chars) { - /* we seem to need to search the layout-dependent scancodes */ - int maxlen=0,maxval=-1,ok; - for (i=0; i<syms; i++) { - keysym = XkbKeycodeToKeysym( display, keyc, 0, i ); - if ((keysym<0x8000) && (keysym!=' ')) - { - if (!XkbTranslateKeySym(display, &keysym, 0, &ckey[i], 1, NULL)) - { - /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent - * with appropriate ShiftMask and Mode_switch, use XLookupString - * to get character in the local encoding. - */ - ckey[i] = (keysym <= 0x7F) ? keysym : 0; - } - } else { - ckey[i] = KEYBOARD_MapDeadKeysym(keysym); - } - } - /* find key with longest match streak */ - for (keyn=0; keyn<MAIN_LEN; keyn++) { - for (ok=(*lkey)[keyn][i=0]; ok&&(i<4); i++) - if ((*lkey)[keyn][i] && (*lkey)[keyn][i]!=ckey[i]) ok=0; - if (!ok) i--; /* we overshot */ - if (ok||(i>maxlen)) { - maxlen=i; maxval=keyn; - } - if (ok) break; - } - if (maxval>=0) { - /* got it */ - const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey; - vkey = (*lvkey)[maxval]; - } - } - } - TRACE("keycode %u => vkey %04X\n", e2.keycode, vkey); - keyc2vkey[e2.keycode] = vkey; - if ((vkey & 0xff) && vkey_used[(vkey & 0xff)]) - WARN("vkey %04X is being used by more than one keycode\n", vkey); - vkey_used[(vkey & 0xff)] = 1; - } /* for */ - -#define VKEY_IF_NOT_USED(vkey) (vkey_used[(vkey)] ? 0 : (vkey_used[(vkey)] = 1, (vkey))) - for (keyc = min_keycode; keyc <= max_keycode; keyc++) - { - vkey = keyc2vkey[keyc] & 0xff; - if (vkey) - continue; - - e2.keycode = (KeyCode)keyc; - keysym = XLookupKeysym(&e2, 0); - if (!keysym) - continue; - - /* find a suitable layout-dependent VK code */ - /* (most Winelib apps ought to be able to work without layout tables!) */ - for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++) - { - keysym = XLookupKeysym(&e2, i); - if ((keysym >= XK_0 && keysym <= XK_9) - || (keysym >= XK_A && keysym <= XK_Z)) { - vkey = VKEY_IF_NOT_USED(keysym); - } - } - - for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++) - { - keysym = XLookupKeysym(&e2, i); - switch (keysym) - { - case ';': vkey = VKEY_IF_NOT_USED(VK_OEM_1); break; - case '/': vkey = VKEY_IF_NOT_USED(VK_OEM_2); break; - case '`': vkey = VKEY_IF_NOT_USED(VK_OEM_3); break; - case '[': vkey = VKEY_IF_NOT_USED(VK_OEM_4); break; - case '\\': vkey = VKEY_IF_NOT_USED(VK_OEM_5); break; - case ']': vkey = VKEY_IF_NOT_USED(VK_OEM_6); break; - case '\'': vkey = VKEY_IF_NOT_USED(VK_OEM_7); break; - case ',': vkey = VKEY_IF_NOT_USED(VK_OEM_COMMA); break; - case '.': vkey = VKEY_IF_NOT_USED(VK_OEM_PERIOD); break; - case '-': vkey = VKEY_IF_NOT_USED(VK_OEM_MINUS); break; - case '+': vkey = VKEY_IF_NOT_USED(VK_OEM_PLUS); break; - } - } - - if (vkey) - { - TRACE("keycode %u => vkey %04X\n", e2.keycode, vkey); - keyc2vkey[e2.keycode] = vkey; - } - } /* for */ - - /* For any keycodes which still don't have a vkey, assign any spare - * character, function key, or OEM virtual key code. */ - vkey_range = 0; - vkey = vkey_ranges[vkey_range].first; - for (keyc = min_keycode; keyc <= max_keycode; keyc++) - { - if (keyc2vkey[keyc] & 0xff) - continue; - - e2.keycode = (KeyCode)keyc; - keysym = XLookupKeysym(&e2, 0); - if (!keysym) - continue; - - while (vkey && vkey_used[vkey]) - { - if (vkey == vkey_ranges[vkey_range].last) - { - vkey_range++; - vkey = vkey_ranges[vkey_range].first; - } - else - vkey++; - } - - if (!vkey) - { - WARN("No more vkeys available!\n"); - break; - } - - if (TRACE_ON(keyboard)) - { - TRACE("spare virtual key %04X assigned to keycode %u:\n", - vkey, e2.keycode); - TRACE("("); - for (i = 0; i < keysyms_per_keycode; i += 1) - { - const char *ksname; - - keysym = XLookupKeysym(&e2, i); - ksname = XKeysymToString(keysym); - if (!ksname) - ksname = "NoSymbol"; - TRACE( "%lx (%s) ", keysym, ksname); - } - TRACE(")\n"); - } - - TRACE("keycode %u => vkey %04X\n", e2.keycode, vkey); - keyc2vkey[e2.keycode] = vkey; - vkey_used[vkey] = 1; - } /* for */ -#undef VKEY_IF_NOT_USED -} - static void find_xkb_layout_variant( Display *display, XModifierKeymap *mmp, int group, const char *name, const char **layout, const char **variant ) { @@ -2233,12 +2053,9 @@ void init_keyboard_layouts( Display *display ) WARN( "Xkb langid %04x differs from detected langid %04x\n", xkb_lang, main_key_tab[kbd_layout].lcid ); - init_keycode_mappings( display ); - pthread_mutex_unlock( &kbd_mutex ); } - /*********************************************************************** * ActivateKeyboardLayout (X11DRV.@) */ @@ -2256,6 +2073,19 @@ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) return TRUE; } +void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) +{ + unsigned int xkb_group; + XkbStateRec xkb_state; + Status status; + + XkbUseExtension( data->display, NULL, NULL ); + XkbSetDetectableAutoRepeat( data->display, True, NULL ); + init_keyboard_layouts( data->display ); + status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); + xkb_group = status ? 0 : xkb_state.group; + TRACE( "current group %u (status %#x)\n", xkb_group, status ); +} /*********************************************************************** * X11DRV_MappingNotify @@ -2325,6 +2155,8 @@ xkbregistry_init_done: SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) { Display *display = thread_init_display(); + const struct layout *layout = get_current_layout(); + const USHORT *scan2vk = layout->scan2vk; KeyCode keycode; KeySym keysym; int index; @@ -2365,8 +2197,8 @@ SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) pthread_mutex_lock( &kbd_mutex ); - /* keycode -> (keyc2vkey) vkey */ - ret = keyc2vkey[keycode]; + /* keycode -> vkey */ + ret = scan2vk[keyc2scan( keycode, 0 )]; if (!ret) { pthread_mutex_unlock( &kbd_mutex ); @@ -2406,6 +2238,8 @@ SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) */ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) { + const struct layout *layout = get_current_layout(); + const USHORT *scan2vk = layout->scan2vk; UINT ret = 0; int keyc; Display *display = thread_init_display(); @@ -2428,9 +2262,10 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) /* let's do vkey -> keycode -> scan */ for (keyc = min_keycode; keyc <= max_keycode; keyc++) { - if ((keyc2vkey[keyc] & 0xFF) == wCode) + WORD scan = keyc2scan( keyc, 0 ); + if ((scan2vk[scan] & 0xFF) == wCode) { - ret = keyc2scan( keyc, 0 ) & 0xFF; + ret = scan & 0xFF; break; } } @@ -2446,14 +2281,17 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) /* let's do scan -> keycode -> vkey */ for (keyc = min_keycode; keyc <= max_keycode; keyc++) - if ((keyc2scan( keyc, 0 ) & 0xFF) == (wCode & 0xFF)) + { + WORD scan = keyc2scan( keyc, 0 ); + if ((scan & 0xFF) == (wCode & 0xFF)) { - ret = keyc2vkey[keyc] & 0xFF; + ret = scan2vk[scan] & 0xFF; /* Only stop if it's not a numpad vkey; otherwise keep looking for a potential better vkey. */ if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret)) break; } + } if (wMapType == MAPVK_VSC_TO_VK) switch (ret) @@ -2493,7 +2331,8 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) /* We exit on the first keycode found, to speed up the thing. */ for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++) { /* Find a keycode that could have generated this virtual key */ - if ((keyc2vkey[keyc] & 0xFF) == wCode) + WORD scan = keyc2scan( keyc, 0 ); + if ((scan2vk[scan] & 0xFF) == wCode) { /* We filter the extended bit, we don't know it */ e.keycode = keyc; /* Store it temporarily */ if ((EVENT_event_to_vkey(0,&e) & 0xFF) != wCode) { @@ -2761,6 +2600,8 @@ INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl ) { Display *display = thread_init_display(); + const struct layout *layout = get_current_layout(); + const USHORT *scan2vk = layout->scan2vk; XKeyEvent e; KeySym keysym = 0; INT ret; @@ -2831,7 +2672,8 @@ INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, /* We exit on the first keycode found, to speed up the thing. */ for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++) { /* Find a keycode that could have generated this virtual key */ - if ((keyc2vkey[keyc] & 0xFF) == virtKey) + WORD scan = keyc2scan( keyc, 0 ); + if ((scan2vk[scan] & 0xFF) == virtKey) { /* We filter the extended bit, we don't know it */ e.keycode = keyc; /* Store it temporarily */ if ((EVENT_event_to_vkey(xic,&e) & 0xFF) != virtKey) { diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index a91c0e74c02..6e2204b7490 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -771,6 +771,7 @@ extern void reapply_cursor_clipping(void); extern void ungrab_clipping_window(void); extern void move_resize_window( HWND hwnd, int dir, POINT pos ); extern void x11drv_init_keyboard( Display *display ); +extern void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); typedef int (*x11drv_error_callback)( Display *display, XErrorEvent *event, void *arg ); diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 39b23ffe0ec..ac0cda51fe0 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -752,14 +752,13 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */ - XkbUseExtension( data->display, NULL, NULL ); - XkbSetDetectableAutoRepeat( data->display, True, NULL ); if (TRACE_ON(synchronous)) XSynchronize( data->display, True ); set_queue_display_fd( data->display ); pthread_setspecific( x11drv_thread_data_key, data ); XSelectInput( data->display, DefaultRootWindow( data->display ), PropertyChangeMask ); + x11drv_keyboard_init_thread( data ); if (use_xim) xim_thread_attach( data ); x11drv_xinput2_init( data ); net_supported_init( data ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> Also clean up comments on the legacy layout tables, now only used by the fallback layout detection code. --- dlls/winex11.drv/keyboard.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 0cdf6220337..77f8edd4cfd 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -423,20 +423,8 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGI /* Keyboard translation tables */ #define MAIN_LEN 49 -/*** DEFINE YOUR NEW LANGUAGE-SPECIFIC MAPPINGS BELOW, SEE EXISTING TABLES */ - -/* the VK mappings for the main keyboard will be auto-assigned as before, - so what we have here is just the character tables */ +/* Legacy keyboard layout detection keysym tables */ /* order: Normal, Shift, AltGr, Shift-AltGr */ -/* We recommend you write just what is guaranteed to be correct (i.e. what's - written on the keycaps), not the bunch of special characters behind AltGr - and Shift-AltGr if it can vary among different X servers */ -/* These tables serve to guess the keyboard type and scancode mapping. - Complete modeling is not important, identification/discrimination is. */ -/* Remember that your 102nd key (to the right of l-shift) should be on a - separate line, see existing tables */ -/* If Wine fails to match your new table, use WINEDEBUG=+key to find out why */ -/* Remember to also add your new table to the layout index table far below! */ /*** United States keyboard layout (mostly contributed by Uwe Bonnes) */ static const char main_key_US[MAIN_LEN][4] = @@ -1087,8 +1075,7 @@ static const char main_key_NL[MAIN_LEN][4] = }; - -/*** Layout table. Add your keyboard mappings to this list */ +/* Legacy layout table */ static const struct { LCID lcid; /* input locale identifier, look for LOCALE_ILANGUAGE in the appropriate dlls/kernel/nls/.nls file */ @@ -1162,7 +1149,6 @@ static const struct { {0, NULL, NULL} /* sentinel */ }; -static unsigned kbd_layout=0; /* index into above table of layouts */ #ifdef SONAME_LIBXKBREGISTRY static struct rxkb_context *rxkb_context; @@ -1972,7 +1958,6 @@ void init_keyboard_layouts( Display *display ) XModifierKeymap *mmp; XkbDescRec *xkb_desc; struct layout *entry; - LANGID xkb_lang = 0; Status status; KeyCode *kcp; int count; @@ -2035,7 +2020,6 @@ void init_keyboard_layouts( Display *display ) find_xkb_layout_variant( display, mmp, i, names[i], &layout, &variant ); lang = langid_from_xkb_layout( layout ); klid = klid_from_xkb_layout( layout, variant ); - if (i == xkb_group) xkb_lang = lang; TRACE( "Found group %u with name %s -> layout %s:%s, lang %04x, klid %08x\n", i, debugstr_a(names[i]), debugstr_a(layout), debugstr_a(variant), lang, klid ); @@ -2045,14 +2029,8 @@ void init_keyboard_layouts( Display *display ) if (names[i]) XFree( names[i] ); } - - kbd_layout = detect_keyboard_layout( display, mmp, xkb_group ); XFreeModifiermap( mmp ); - if (xkb_lang && xkb_lang != main_key_tab[kbd_layout].lcid) - WARN( "Xkb langid %04x differs from detected langid %04x\n", - xkb_lang, main_key_tab[kbd_layout].lcid ); - pthread_mutex_unlock( &kbd_mutex ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 41 +++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 2 ++ dlls/winex11.drv/xim.c | 2 +- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 77f8edd4cfd..3f44db9b746 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1746,6 +1746,17 @@ static LANGID langid_from_xkb_layout( const char *layout ) return MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED); } +static struct layout *get_layout_from_xkb_group( int xkb_group ) +{ + struct layout *layout; + + LIST_FOR_EACH_ENTRY( layout, &xkb_layouts, struct layout, entry ) + if (layout->xkb_group == xkb_group) return layout; + + WARN( "Failed to find layout for Xkb group %d\n", xkb_group ); + return NULL; +} + static const char *xkb_layout_from_langid( LANGID langid ) { for (int i = 0; i < ARRAY_SIZE(layout_ids); i++) @@ -2034,13 +2045,36 @@ void init_keyboard_layouts( Display *display ) pthread_mutex_unlock( &kbd_mutex ); } +static HKL get_hkl( LANGID langid, WORD layout_id ) +{ + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); + + TRACE( "langid %04x, layout_id %04x\n", langid, layout_id ); + + if (layout_id) return ULongToHandle( MAKELONG(locale, 0xf000 | layout_id) ); + return ULongToHandle( MAKELONG(locale, langid) ); +} + /*********************************************************************** * ActivateKeyboardLayout (X11DRV.@) */ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) { + struct layout *layout = get_layout_from_hkl( hkl ); + WARN("%p, %04x: semi-stub!\n", hkl, flags); + if (layout == get_current_layout()) return TRUE; + if (!layout) + { + if (!is_ime_hkl(hkl)) + { + WARN( "HKL %p not found, returning FALSE\n", hkl ); + return FALSE; + } + layout = get_current_layout(); + } + if (flags & KLF_SETFORPROCESS) { RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED ); @@ -2055,7 +2089,9 @@ void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) { unsigned int xkb_group; XkbStateRec xkb_state; + struct layout *layout; Status status; + HKL hkl; XkbUseExtension( data->display, NULL, NULL ); XkbSetDetectableAutoRepeat( data->display, True, NULL ); @@ -2063,6 +2099,11 @@ void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); xkb_group = status ? 0 : xkb_state.group; TRACE( "current group %u (status %#x)\n", xkb_group, status ); + + layout = get_layout_from_xkb_group( xkb_group ); + + hkl = get_hkl( layout->lang, layout->layout_id ); + NtUserActivateKeyboardLayout( hkl, 0 ); } /*********************************************************************** diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 6e2204b7490..e406666d5d6 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -774,6 +774,8 @@ extern void x11drv_init_keyboard( Display *display ); extern void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); +extern BOOL is_ime_hkl( HKL hkl ); + typedef int (*x11drv_error_callback)( Display *display, XErrorEvent *event, void *arg ); extern void X11DRV_expect_error( Display *display, x11drv_error_callback callback, void *arg ); diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 2c2049ac763..f8066f93c84 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -74,7 +74,7 @@ BOOL xim_in_compose_mode(void) return !!ime_comp_buf; } -static BOOL is_ime_hkl( HKL hkl ) +BOOL is_ime_hkl( HKL hkl ) { /* See https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windo... */ switch (HIWORD(hkl)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 3f44db9b746..bad48f9feab 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1149,6 +1149,7 @@ static const struct { {0, NULL, NULL} /* sentinel */ }; +static int xkb_event_base, xkb_error_base; #ifdef SONAME_LIBXKBREGISTRY static struct rxkb_context *rxkb_context; @@ -2085,6 +2086,21 @@ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) return TRUE; } +static void switch_current_xkb_group( int xkb_group ) +{ + struct layout *layout; + HWND hwnd; + HKL hkl; + + hwnd = get_focus(); + if (!hwnd) hwnd = get_active_window(); + if (!hwnd) return; + layout = get_layout_from_xkb_group( xkb_group ); + TRACE( "sending layout change message to hwnd %p\n", hwnd ); + hkl = get_hkl( layout->lang, layout->layout_id ); + NtUserPostMessage( hwnd, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)hkl ); +} + void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) { unsigned int xkb_group; @@ -2094,6 +2110,8 @@ void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) HKL hkl; XkbUseExtension( data->display, NULL, NULL ); + XkbSelectEvents( data->display, XkbUseCoreKbd, XkbStateNotifyMask, + XkbStateNotifyMask ); XkbSetDetectableAutoRepeat( data->display, True, NULL ); init_keyboard_layouts( data->display ); status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); @@ -2123,6 +2141,22 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) return TRUE; } +BOOL x11drv_xkb_event_handler( HWND dummy, XEvent *event ) +{ + XkbEvent *e = (XkbEvent *)event; + + switch (e->any.xkb_type) + { + case XkbStateNotify: + TRACE( "Received XkbStateNotify event, changed %#x, group %u\n", e->state.changed, e->state.group ); + if (!(e->state.changed & XkbGroupStateMask)) + return TRUE; + TRACE( "Switching to group %u\n", e->state.group ); + switch_current_xkb_group( e->state.group ); + break; + } + return TRUE; +} /*********************************************************************** * x11drv_init_keyboard @@ -2130,7 +2164,10 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event ) void x11drv_init_keyboard( Display *display ) { use_xkb = XkbUseExtension( display, NULL, NULL ); - TRACE( "XkbUseExtension returned %#x\n", use_xkb ); + XkbQueryExtension( display, 0, &xkb_event_base, &xkb_error_base, 0, 0 ); + TRACE( "XkbUseExtension returned %#x, xkb_event_base %u, xkb_error_base %u\n", + use_xkb, xkb_event_base, xkb_error_base ); + X11DRV_register_event_handler( xkb_event_base, x11drv_xkb_event_handler, "Xkb" ); #ifdef SONAME_LIBXKBREGISTRY if (!(xkbregistry_handle = dlopen( SONAME_LIBXKBREGISTRY, RTLD_NOW ))) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> We get this event when a new keymap is set, usually when switching to a layout not represented by the previous keymap. --- dlls/winex11.drv/keyboard.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index bad48f9feab..a1a7bd852ac 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1150,6 +1150,7 @@ static const struct { {0, NULL, NULL} /* sentinel */ }; static int xkb_event_base, xkb_error_base; +static unsigned short xkb_device_spec; #ifdef SONAME_LIBXKBREGISTRY static struct rxkb_context *rxkb_context; @@ -2012,6 +2013,7 @@ void init_keyboard_layouts( Display *display ) if ((xkb_desc = XkbGetMap( display, XkbAllClientInfoMask, XkbUseCoreKbd ))) { + xkb_device_spec = xkb_desc->device_spec; XkbGetNames( display, XkbGroupNamesMask, xkb_desc ); for (count = 0; count < ARRAY_SIZE(xkb_desc->names->groups); count++) if (!xkb_desc->names->groups[count]) break; @@ -2101,6 +2103,16 @@ static void switch_current_xkb_group( int xkb_group ) NtUserPostMessage( hwnd, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)hkl ); } +static void x11drv_update_input_lang( Display *display ) +{ + XkbStateRec xkb_state; + Status status; + + init_keyboard_layouts( display ); + status = XkbGetState( display, XkbUseCoreKbd, &xkb_state ); + switch_current_xkb_group( status ? 0 : xkb_state.group ); +} + void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) { unsigned int xkb_group; @@ -2110,8 +2122,8 @@ void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) HKL hkl; XkbUseExtension( data->display, NULL, NULL ); - XkbSelectEvents( data->display, XkbUseCoreKbd, XkbStateNotifyMask, - XkbStateNotifyMask ); + XkbSelectEvents( data->display, XkbUseCoreKbd, XkbStateNotifyMask | XkbNewKeyboardNotifyMask, + XkbStateNotifyMask | XkbNewKeyboardNotifyMask ); XkbSetDetectableAutoRepeat( data->display, True, NULL ); init_keyboard_layouts( data->display ); status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); @@ -2154,6 +2166,13 @@ BOOL x11drv_xkb_event_handler( HWND dummy, XEvent *event ) TRACE( "Switching to group %u\n", e->state.group ); switch_current_xkb_group( e->state.group ); break; + case XkbNewKeyboardNotify: + TRACE( "Received XkbNewKeyboardNotify event, changed %#x, device %u\n", + e->new_kbd.changed, e->new_kbd.device ); + if ( !xkb_device_spec || e->new_kbd.device != xkb_device_spec ) + return TRUE; + x11drv_update_input_lang( e->new_kbd.display ); + break; } return TRUE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index a1a7bd852ac..1dbad186385 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -2122,8 +2122,9 @@ void x11drv_keyboard_init_thread( struct x11drv_thread_data *data ) HKL hkl; XkbUseExtension( data->display, NULL, NULL ); - XkbSelectEvents( data->display, XkbUseCoreKbd, XkbStateNotifyMask | XkbNewKeyboardNotifyMask, - XkbStateNotifyMask | XkbNewKeyboardNotifyMask ); + XkbSelectEvents( data->display, XkbUseCoreKbd, + XkbStateNotifyMask | XkbNewKeyboardNotifyMask | XkbMapNotifyMask, + XkbStateNotifyMask | XkbNewKeyboardNotifyMask | XkbMapNotifyMask ); XkbSetDetectableAutoRepeat( data->display, True, NULL ); init_keyboard_layouts( data->display ); status = XkbGetState( data->display, XkbUseCoreKbd, &xkb_state ); @@ -2173,6 +2174,11 @@ BOOL x11drv_xkb_event_handler( HWND dummy, XEvent *event ) return TRUE; x11drv_update_input_lang( e->new_kbd.display ); break; + case XkbMapNotify: + TRACE( "Received XkbMapNotify event, changed %#x\n", e->map.changed ); + XkbRefreshKeyboardMapping( &e->map ); + x11drv_update_input_lang( e->map.display ); + break; } return TRUE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> Otherwise upon receiving them it will pass them along to its children, again. --- dlls/win32u/input.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 896b0d42753..a626dfcdd13 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1462,8 +1462,9 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_HKL_ACTIVATE, HandleToUlong(layout) ); - if ((focus = get_focus()) && get_window_thread( focus, NULL ) == GetCurrentThreadId()) - send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout ); + if ((focus = get_focus()) && NtUserGetAncestor( focus, GA_ROOT ) != NtUserGetDesktopWindow() + && get_window_thread( focus, NULL ) == GetCurrentThreadId()) + send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout ); } if (!old_layout) return get_locale_kbd_layout(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
From: Matteo Bruni <mbruni@codeweavers.com> --- dlls/user32/tests/input.c | 78 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 7e555f2aa70..62d8345d14c 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3766,6 +3766,7 @@ static void test_keyboard_layout_name(void) free(layouts_preload); } +static HWND expect_hwnd; static HKL expect_hkl; static HKL change_hkl; static int got_setfocus; @@ -3774,7 +3775,11 @@ static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT { ok( msg != WM_INPUTLANGCHANGEREQUEST, "got WM_INPUTLANGCHANGEREQUEST\n" ); - if (msg == WM_SETFOCUS) got_setfocus = 1; + if (msg == WM_SETFOCUS) + { + got_setfocus = 1; + if (expect_hwnd) ok( hwnd == expect_hwnd, "got hwnd %p\n", hwnd ); + } if (msg == WM_INPUTLANGCHANGE) { HKL layout = GetKeyboardLayout( 0 ); @@ -3783,6 +3788,8 @@ static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT UINT codepage; LCID lcid; + if (expect_hwnd) ok( hwnd == expect_hwnd, "got hwnd %p\n", hwnd ); + /* get keyboard layout lcid from its name, as the HKL might be aliased */ GetKeyboardLayoutNameW( klidW ); swscanf( klidW, L"%x", &lcid ); @@ -3814,7 +3821,7 @@ static DWORD CALLBACK test_ActivateKeyboardLayout_thread_proc( void *arg ) static void test_ActivateKeyboardLayout( char **argv ) { HKL layout, tmp_layout, *layouts; - HWND hwnd1, hwnd2; + HWND hwnd1, hwnd2, focus_hwnd; HANDLE thread; UINT i, count; DWORD ret; @@ -3850,6 +3857,7 @@ static void test_ActivateKeyboardLayout( char **argv ) /* test WM_INPUTLANGCHANGE message */ + expect_hwnd = 0; change_hkl = 0; expect_hkl = other_layout; got_setfocus = 0; @@ -3862,6 +3870,72 @@ static void test_ActivateKeyboardLayout( char **argv ) todo_wine_if(layout != other_layout) ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + /* the message is sent to the focus window, whether it's a top level window or not */ + hwnd2 = CreateWindowA( "static", "static", WS_VISIBLE | WS_CHILD, + 0, 0, 100, 100, hwnd1, NULL, NULL, NULL ); + ok( !!hwnd2, "CreateWindow failed, error %lu\n", GetLastError() ); + trace("hwnd1 %p, hwnd2 %p\n", hwnd1, hwnd2); + + SetWindowLongPtrA( hwnd2, GWLP_WNDPROC, (LONG_PTR)test_ActivateKeyboardLayout_window_proc ); + + focus_hwnd = GetFocus(); + ok( focus_hwnd == hwnd1, "got focus window %p\n", focus_hwnd ); + + expect_hkl = layout; + got_setfocus = 0; + SetFocus( hwnd2 ); + + focus_hwnd = GetFocus(); + ok( focus_hwnd == hwnd2, "got focus window %p\n", focus_hwnd ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + expect_hwnd = hwnd2; + change_hkl = 0; + expect_hkl = layout; + got_setfocus = 0; + ActivateKeyboardLayout( layout, 0 ); + empty_message_queue(); + 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 ); + ok( !got_setfocus, "got got_setfocus %d\n", got_setfocus ); + expect_hwnd = 0; + 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 ); + + expect_hkl = layout; + got_setfocus = 0; + SetFocus( hwnd1 ); + empty_message_queue(); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == layout, "got tmp_layout %p\n", tmp_layout ); + + if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + else todo_wine ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + change_hkl = expect_hkl = 0; + + DestroyWindow( hwnd2 ); + empty_message_queue(); + + change_hkl = 0; + expect_hkl = other_layout; + got_setfocus = 0; + ActivateKeyboardLayout( other_layout, 0 ); + empty_message_queue(); + 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 ); + 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 */ thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11367
On Tue Aug 25 10:37:02 2026 +0000, Rémi Bernon wrote:
Is it really supposed to be sent to the toplevel window? Indeed it's not, thanks. I added a test showing that.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11367#note_149815
On Tue Aug 25 10:46:02 2026 +0000, Rémi Bernon wrote:
Fwiw I'm still not really convinced we need to use the Xkb extension events. We don't NEED the Xkb events but I think they offer some advantages and seem to work fine in my testing.
The main one is if the user switches to a different layout when the window is not in focus, we get an event as soon as we get the focus back, while otherwise I think we need to wait for a key press to notice the change. IIUC this is essentially the same as what's tested in https://gitlab.winehq.org/wine/wine/-/blob/111e5197390aa008789b002222024229f... but for a user-initiated layout change. If you want I can drop these changes though and do it more like winewayland, triggering layout changes on MappingNotify or group changes from the KeyPress state. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11367#note_149817
Thanks for the comments, I took care of the ones I didn't directly reply to. I also noticed some issues with the compatibility keycode -> scancode mapping for Xvnc we introduced recently, which I'll take care of separately. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11367#note_149818
There seems to be something broken in this version of the patches, marking draft for now. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11367#note_149823
participants (3)
-
Matteo Bruni -
Matteo Bruni (@Mystral) -
Rémi Bernon