From acf5db32f197d35624c1dd0ea446c28e4cdc16d1 Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Sun, 9 Jan 2022 08:12:55 +0100 Subject: [PATCH 1/5] gdkkeys-win32: Fix capslock handling Previously, we treated CapsLock and KanaLock as part of the global keyboard state, much like NumLock and ScrollLock, rather than using the supplied modifier mask. This was because GDK does not have a modifier mask for KanaLock, only for CapsLock, so it would not have been possible to properly support it. However, this approach ended up causing problems, with certain keyboard shortcuts not registering when capslock was active. This was first observed in Inkscape [0] and appears to affect shortcuts consisting of a single key (like 'a') with no additional modifiers (wheareas shortcuts like 'ctrl+a' work). So now we are using the supplied GDK_LOCK_MASK instead, and dropped support for KanaLock, which we probably don't need anyway (since regular text input should be handled by the IME input module -- the keymap is mainly for shortcuts and keybindings, where you don't really want KanaLock). [0] https://gitlab.com/inkscape/inkscape/-/issues/3082 --- gdk/win32/gdkkeys-win32-impl.c | 50 +++++++++++++------------- gdk/win32/gdkkeys-win32.c | 64 ++++++++++++++-------------------- gdk/win32/gdkkeys-win32.h | 4 +-- 3 files changed, 54 insertions(+), 64 deletions(-) diff --git a/gdk/win32/gdkkeys-win32-impl.c b/gdk/win32/gdkkeys-win32-impl.c index f792c4e24b..a6124f943d 100644 --- a/gdk/win32/gdkkeys-win32-impl.c +++ b/gdk/win32/gdkkeys-win32-impl.c @@ -213,7 +213,7 @@ modbits_to_level (GdkWin32KeymapLayoutInfo *info, /* * vk_to_char_fuzzy: * - * For a given key and keystate, return the best-fit character and the + * For a given key and modifier state, return the best-fit character and the * modifiers used to produce it. Note that not all modifiers need to be used, * because some modifier combination aren't actually mapped in the keyboard * layout (for example the Ctrl key typically has no effect, unless used in @@ -221,26 +221,32 @@ modbits_to_level (GdkWin32KeymapLayoutInfo *info, * * 'Best-fit' means 'consume as many modifiers as possibe'. * - * For example (assuming a neutral keystate): + * For example (assuming a neutral lock state): * + * - a -> 'a', consumed_mod_bits: [] * - Shift + a -> 'A', consumed_mod_bits: [Shift] * - Ctrl + a -> 'a', consumed_mod_bits: [] * - Ctrl + Shift + a -> 'A', consumed_mod_bits: [Shift] * * If capslock is active, the result could be: * - * - Shift + a -> 'a', consumed_mod_bits: [Shift] + * - a -> 'A', consumed_mod_bits: [Shift] + * - Shift + a -> 'a', consumed_mod_bits: [] + * - Ctrl + a -> 'a', consumed_mod_bits: [] + * - Ctrl + Shift + a -> 'A', consumed_mod_bits: [Shift] * - * The caller can supply additional modifiers to be added to the - * keystate in `extra_mod_bits`. + * The held down modifiers are supplied in `mod_bits` as a bitmask of KBDSHIFT, + * KBDCTRL, KBDALT etc. + * + * The toggled modifiers are supplied in `lock_state` as a bitmask of CAPLOK and KANALOK. * * If the key combination results in a dead key, `is_dead` will be set to TRUE, * otherwise it will be set to FALSE. */ static WCHAR vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, - const BYTE keystate[256], - BYTE extra_mod_bits, + BYTE mod_bits, + BYTE lock_bits, BYTE *consumed_mod_bits, gboolean *is_dead, BYTE vk) @@ -282,18 +288,12 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, if (entry->VirtualKey == vk) { - BYTE modbits; WCHAR best_char = WCH_NONE; BYTE best_modifiers = 0; int best_score = -1; gboolean best_is_dead = FALSE; int level; - /* Add modbits of currently pressed keys. */ - modbits = keystate_to_modbits (info, keystate); - /* Add modbits supplied by caller. */ - modbits |= extra_mod_bits; - /* Take toggled keys into account. For example, capslock normally inverts the * state of KBDSHIFT (with some exceptions). */ @@ -302,27 +302,27 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, /* Ignore capslock if any modifiers other than shift are pressed. * E.g. on the German layout, CapsLock + AltGr + q is the same as * AltGr + q ('@'), but NOT the same as Shift + AltGr + q (not mapped). */ - !(modbits & ~KBDSHIFT) && - (keystate[VK_CAPITAL] & 0x01)) - modbits ^= KBDSHIFT; + !(mod_bits & ~KBDSHIFT) && + (lock_bits & CAPLOK)) + mod_bits ^= KBDSHIFT; /* Key supporting combination of capslock + altgr */ if ((entry->Attributes & CAPLOKALTGR) && - (modbits & KBDALTGR) && - (keystate[VK_CAPITAL] & 0x01)) - modbits ^= KBDSHIFT; + (mod_bits & KBDALTGR) && + (lock_bits & CAPLOK)) + mod_bits ^= KBDSHIFT; /* In the Swiss German layout, CapsLock + key is different from Shift + key * for some keys. For such keys, Capslock toggles the KBDCTRL bit. */ if ((entry->Attributes & SGCAPS) && - (keystate[VK_CAPITAL] & 0x01)) - modbits ^= KBDCTRL; + (lock_bits & CAPLOK)) + mod_bits ^= KBDCTRL; /* I'm not totally sure how kanalok behaves, for now I assume that there * aren't any special cases. */ if ((entry->Attributes & KANALOK) && - (keystate[VK_KANA] & 0x01)) - modbits ^= KBDKANA; + (lock_bits & KANALOK)) + mod_bits ^= KBDKANA; /* We try to find the entry with the most matching modifiers */ for (level = 0; level < n_levels; ++level) @@ -332,7 +332,7 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, WCHAR c; int score; - if (candidate_modbits & ~modbits) + if (candidate_modbits & ~mod_bits) continue; c = entry->wch[level]; @@ -349,7 +349,7 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, if (c == WCH_DEAD || c == WCH_LGTR || c == WCH_NONE) continue; - score = POPCOUNT (candidate_modbits & modbits); + score = POPCOUNT (candidate_modbits & mod_bits); if (score > best_score) { best_score = score; diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index 18a6752278..19bc721ce0 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -156,13 +156,13 @@ modbits_to_level (GdkWin32Keymap *keymap, static WCHAR vk_to_char_fuzzy (GdkWin32Keymap *keymap, GdkWin32KeymapLayoutInfo *info, - const BYTE keystate[256], - BYTE extra_mod_bits, + BYTE mod_bits, + BYTE lock_bits, BYTE *consumed_mod_bits, gboolean *is_dead, BYTE vk) { - return keymap->gdkwin32_keymap_impl->vk_to_char_fuzzy (info, keystate, extra_mod_bits, + return keymap->gdkwin32_keymap_impl->vk_to_char_fuzzy (info, mod_bits, lock_bits, consumed_mod_bits, is_dead, vk); } @@ -347,8 +347,8 @@ static guint vk_and_mod_bits_to_gdk_keysym (GdkWin32Keymap *keymap, GdkWin32KeymapLayoutInfo *info, guint vk, - const BYTE keystate[256], BYTE mod_bits, + BYTE lock_bits, BYTE *consumed_mod_bits) { @@ -384,7 +384,7 @@ vk_and_mod_bits_to_gdk_keysym (GdkWin32Keymap *keymap, } /* Handle regular keys (including dead keys) */ - c = vk_to_char_fuzzy (keymap, info, keystate, mod_bits, + c = vk_to_char_fuzzy (keymap, info, mod_bits, lock_bits, consumed_mod_bits, &is_dead, vk); if (c == WCH_NONE) @@ -497,26 +497,6 @@ gdk_mod_mask_to_mod_bits (GdkModifierType mod_mask) return result; } -static void -get_lock_state (BYTE lock_state[256]) -{ - static const guint mode_keys[] = - { - VK_CAPITAL, - VK_KANA, VK_HANGUL, VK_JUNJA, VK_FINAL, VK_HANJA, VK_KANJI, /* Is this correct? */ - VK_NUMLOCK, VK_SCROLL - }; - - BYTE keystate[256] = {0}; - guint i; - - GetKeyboardState (keystate); - - /* Copy over some keystates like numlock and capslock */ - for (i = 0; i < G_N_ELEMENTS(mode_keys); ++i) - lock_state[mode_keys[i]] = keystate[mode_keys[i]] & 0x1; -} - /* keypad decimal mark depends on active keyboard layout * return current decimal mark as unicode character @@ -766,7 +746,6 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap *gdk_keymap, { GdkWin32Keymap *keymap; GArray *retval; - BYTE keystate[256] = {0}; gint group; g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE); @@ -822,8 +801,7 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap *gdk_keymap, /* Check if the additional modifiers change the semantics. * If they do not, add them. */ sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, entry->vk, - keystate, modbits, - NULL); + modbits, 0, NULL); if (sym == keyval || sym == GDK_KEY_VoidSymbol) { gdk_key.keycode = entry->vk; @@ -864,7 +842,6 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap *gdk_keymap, GArray *key_array; GArray *keyval_array; gint group; - BYTE keystate[256] = {0}; BYTE vk; g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE); @@ -901,7 +878,7 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap *gdk_keymap, GdkKeymapKey key = {0}; guint keyval; - keyval = vk_and_mod_bits_to_gdk_keysym (keymap, info, vk, keystate, modbits, &consumed_modbits); + keyval = vk_and_mod_bits_to_gdk_keysym (keymap, info, vk, modbits, 0, &consumed_modbits); if (keyval == GDK_KEY_VoidSymbol || consumed_modbits != modbits) continue; @@ -936,7 +913,6 @@ gdk_win32_keymap_lookup_key (GdkKeymap *gdk_keymap, GdkWin32Keymap *keymap; GdkWin32KeymapLayoutInfo *info; - BYTE keystate[256] = {0}; BYTE modbits; guint sym; @@ -954,7 +930,7 @@ gdk_win32_keymap_lookup_key (GdkKeymap *gdk_keymap, return 0; modbits = info->level_to_modbits[key->level]; - sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, key->keycode, keystate, modbits, NULL); + sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, key->keycode, modbits, 0, NULL); if (sym == GDK_KEY_VoidSymbol) return 0; @@ -981,7 +957,7 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap *gdk_keymap, GdkWin32KeymapLayoutInfo *layout_info; guint vk; BYTE mod_bits; - BYTE keystate[256] = {0}; + BYTE lock_bits = 0; g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE); @@ -1005,11 +981,25 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap *gdk_keymap, if (vk == VK_RMENU) mod_bits &= ~KBDALTGR; - /* We need to query the existing keyboard state for NumLock, CapsLock etc. */ - get_lock_state (keystate); + /* Translate lock state + * + * Right now the only locking modifier is CAPSLOCK. We don't handle KANALOK + * because GDK doesn't have an equivalent modifier mask to my knowledge (On + * X11, I believe the same effect is achieved by shifting to a different + * group. It's just a different concept, that doesn't translate to Windows). + * But since KANALOK is only used on far-eastern keyboards, which require IME + * anyway, this is probably fine. The IME input module has actually been the + * default for all languages (not just far-eastern) for a while now, which + * means that the keymap is now only used for things like accelerators and + * keybindings, where you probably don't even want KANALOK to affect the + * translation. + */ - tmp_keyval = vk_and_mod_bits_to_gdk_keysym (keymap, layout_info, vk, keystate, - mod_bits, &consumed_mod_bits); + if (state & GDK_LOCK_MASK) + lock_bits |= CAPLOK; + + tmp_keyval = vk_and_mod_bits_to_gdk_keysym (keymap, layout_info, vk, mod_bits, + lock_bits, &consumed_mod_bits); tmp_effective_group = group; tmp_level = modbits_to_level (keymap, layout_info, consumed_mod_bits); diff --git a/gdk/win32/gdkkeys-win32.h b/gdk/win32/gdkkeys-win32.h index 17b7d7125c..f128996de1 100644 --- a/gdk/win32/gdkkeys-win32.h +++ b/gdk/win32/gdkkeys-win32.h @@ -160,8 +160,8 @@ typedef struct BYTE (*modbits_to_level) (GdkWin32KeymapLayoutInfo *info, BYTE modbits); WCHAR (*vk_to_char_fuzzy) (GdkWin32KeymapLayoutInfo *info, - const BYTE keystate[256], - BYTE extra_mod_bits, + BYTE mod_bits, + BYTE lock_bits, BYTE *consumed_mod_bits, gboolean *is_dead, BYTE vk); From 404229ad758561c8c0ac313fc211a60b739cb2bd Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Sun, 9 Jan 2022 09:09:35 +0100 Subject: [PATCH 2/5] gdkkeys-win32: Add keysym mapping for capslock key Add missing mapping between VK_CAPITAL and GDK_KEY_Caps_Lock, so applications get a meaningful keyval rather than ffffff. --- gdk/win32/gdkkeys-win32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index 19bc721ce0..85bcce224b 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -320,7 +320,8 @@ clear_keyboard_layout_info (gpointer data) map (VK_SCROLL, GDK_KEY_Scroll_Lock) \ map (VK_RSHIFT, GDK_KEY_Shift_R) \ map (VK_RCONTROL, GDK_KEY_Control_R) \ - map (VK_RMENU, GDK_KEY_Alt_R) + map (VK_RMENU, GDK_KEY_Alt_R) \ + map (VK_CAPITAL, GDK_KEY_Caps_Lock) #define DEFINE_DEAD(map) \ From 8b1c334c4ae67363f2d5acd583b757c50d681097 Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Sun, 9 Jan 2022 11:05:28 +0100 Subject: [PATCH 3/5] gdkkeys-win32: Fix crash when keyboard DLL failed to load DLL loading failures should not happen under normal circumstances, but we should at least try not to crash and and print better diagnostic messages if they do happen. See #4610 --- gdk/win32/gdkkeys-win32-impl.c | 9 ++++-- gdk/win32/gdkkeys-win32.c | 58 ++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/gdk/win32/gdkkeys-win32-impl.c b/gdk/win32/gdkkeys-win32-impl.c index a6124f943d..4539989c13 100644 --- a/gdk/win32/gdkkeys-win32-impl.c +++ b/gdk/win32/gdkkeys-win32-impl.c @@ -180,7 +180,8 @@ keystate_to_modbits (GdkWin32KeymapLayoutInfo *info, BYTE result = 0; int i; - g_return_val_if_fail (tables != NULL, 0); + if (tables == NULL) + return 0; vk_to_bit = tables->pCharModifiers.ptr->pVkToBit.ptr; @@ -198,7 +199,8 @@ modbits_to_level (GdkWin32KeymapLayoutInfo *info, PKBDTABLES tables = (PKBDTABLES) info->tables; PMODIFIERS modifiers; - g_return_val_if_fail (tables != NULL, 0); + if (tables == NULL) + return 0; modifiers = tables->pCharModifiers.ptr; if (modbits > modifiers->wMaxModBits) @@ -268,7 +270,8 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, if (is_dead) *is_dead = FALSE; - g_return_val_if_fail (tables != NULL, WCH_NONE); + if (tables == NULL) + return WCH_NONE; wch_tables = tables->pVkToWcharTable.ptr; diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index 85bcce224b..d835b44c67 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -179,6 +179,7 @@ get_keyboard_layout_file (const char *layout_name) DWORD file_name_len = 0; int dir_len = 0; int buf_len = 0; + LSTATUS status; static const char prefix[] = "SYSTEM\\CurrentControlSet\\Control\\" "Keyboard Layouts\\"; @@ -187,18 +188,32 @@ get_keyboard_layout_file (const char *layout_name) g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s", prefix, layout_name); - if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0, - KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) - goto fail1; + + status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0, + KEY_QUERY_VALUE, &hkey); + if (status != ERROR_SUCCESS) + { + g_warning("Could not open registry key '%s'. Error code: %d", + kbdKeyPath, (int)status); + goto fail1; + } /* Get sizes */ - if (RegQueryValueExA (hkey, "Layout File", 0, &var_type, 0, - &file_name_len) != ERROR_SUCCESS) - goto fail2; + status = RegQueryValueExA (hkey, "Layout File", 0, &var_type, 0, + &file_name_len); + if (status != ERROR_SUCCESS) + { + g_warning("Could not query registry key '%s\\Layout File'. Error code: %d", + kbdKeyPath, (int)status); + goto fail2; + } dir_len = GetSystemDirectoryA (0, 0); /* includes \0 */ if (dir_len == 0) - goto fail2; + { + g_warning("GetSystemDirectoryA failed. Error: %d", (int)GetLastError()); + goto fail2; + } /* Allocate buffer */ buf_len = dir_len + (int) strlen ("\\") + file_name_len; @@ -212,10 +227,12 @@ get_keyboard_layout_file (const char *layout_name) result[dir_len - 1] = '\\'; /* Append file name */ - if (RegQueryValueExA (hkey, "Layout File", 0, &var_type, - (LPBYTE) &result[dir_len], &file_name_len) - != ERROR_SUCCESS) - goto fail3; + status = RegQueryValueExA (hkey, "Layout File", 0, &var_type, + (LPBYTE) &result[dir_len], &file_name_len); + if (status != ERROR_SUCCESS) + { + goto fail3; + } result[dir_len + file_name_len] = '\0'; @@ -413,6 +430,9 @@ gdk_keysym_to_key_entry_index (GdkWin32KeymapLayoutInfo *info, gunichar c; gintptr index; + if (info->reverse_lookup_table == NULL) + return -1; + /* Special cases */ if (sym == GDK_KEY_Tab) return VK_TAB; @@ -437,8 +457,6 @@ gdk_keysym_to_key_entry_index (GdkWin32KeymapLayoutInfo *info, /* Try converting to Unicode and back */ c = gdk_keyval_to_unicode (sym); - g_return_val_if_fail (info->reverse_lookup_table != NULL, -1); - index = -1; if (g_hash_table_lookup_extended (info->reverse_lookup_table, GINT_TO_POINTER (c), @@ -555,7 +573,7 @@ update_keymap (GdkWin32Keymap *keymap) info->file = get_keyboard_layout_file (info->name); - if (load_layout_dll (keymap, info->file, info)) + if (info->file != NULL && load_layout_dll (keymap, info->file, info)) { info->key_entries = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeymapKeyEntry)); @@ -564,6 +582,11 @@ update_keymap (GdkWin32Keymap *keymap) g_direct_equal); init_vk_lookup_table (keymap, info); } + else + { + g_warning("Failed to load keyboard layout DLL for layout %s: %s", + info->name, info->file); + } } if (info->handle == current_layout) @@ -1015,6 +1038,13 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap *gdk_keymap, if (consumed_modifiers) *consumed_modifiers = mod_bits_to_gdk_mod_mask (consumed_mod_bits); + /* Just a diagnostic message to inform the user why their keypresses aren't working. + * Shouldn't happen under normal circumstances. */ + if (tmp_keyval == GDK_KEY_VoidSymbol && layout_info->tables == NULL) + g_warning("Failed to translate keypress (keycode: %u) for group %d (%s) because " + "we could not load the layout.", + hardware_keycode, group, layout_info->name); + return tmp_keyval != GDK_KEY_VoidSymbol; } From 4e9ddaf086fe315169a655db6e70a80bcd23e57b Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Sun, 9 Jan 2022 11:33:40 +0100 Subject: [PATCH 4/5] gdkkeys-win32: Fix handling of SGCAPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contrary to what you can read on the internet, SGCAPS keys don't work by having capslock toggle the KBDCTRL bit, they actually have two consecutive table entries, the first of which is for the normal version and the second of which is for the capslocked version. Background: SGCAPS is short for Swiss German caps because Swiss German was the first layout to use this feature. For keys with the SGCAPS flag, capslock has a different effect than pressing shift. For example: Shift + ü = è, CapsLock + ü = Ü, CapsLock + Shift + ü = È --- gdk/win32/gdkkeys-win32-impl.c | 23 +++++++++++++++-------- gdk/win32/gdkkeys-win32.c | 11 ----------- gdk/win32/gdkprivate-win32.h | 1 - 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/gdk/win32/gdkkeys-win32-impl.c b/gdk/win32/gdkkeys-win32-impl.c index 4539989c13..368f3f6506 100644 --- a/gdk/win32/gdkkeys-win32-impl.c +++ b/gdk/win32/gdkkeys-win32-impl.c @@ -291,6 +291,7 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, if (entry->VirtualKey == vk) { + gboolean have_sgcaps = FALSE; WCHAR best_char = WCH_NONE; BYTE best_modifiers = 0; int best_score = -1; @@ -316,10 +317,11 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, mod_bits ^= KBDSHIFT; /* In the Swiss German layout, CapsLock + key is different from Shift + key - * for some keys. For such keys, Capslock toggles the KBDCTRL bit. */ + * for some keys. For such keys, the characters for active capslock are + * in the next entry. */ if ((entry->Attributes & SGCAPS) && (lock_bits & CAPLOK)) - mod_bits ^= KBDCTRL; + have_sgcaps = TRUE; /* I'm not totally sure how kanalok behaves, for now I assume that there * aren't any special cases. */ @@ -336,12 +338,12 @@ vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info, int score; if (candidate_modbits & ~mod_bits) - continue; + continue; c = entry->wch[level]; - if (c == WCH_DEAD) + if (c == WCH_DEAD || have_sgcaps) { - /* Next entry contains the undead keys */ + /* Next entry contains the undead/capslocked keys */ PVK_TO_WCHARS next_entry; next_entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr + entry_size * (entry_index + 1)); @@ -458,8 +460,13 @@ init_vk_lookup_table (GdkWin32KeymapLayoutInfo *info) /* Lookup table to find entry for a VK in O(1). */ - info->vk_lookup_table[entry->VirtualKey].table = table_idx; - info->vk_lookup_table[entry->VirtualKey].index = entry_idx; + /* Only add the first entry, as some layouts (Swiss German) contain + * multiple successive entries for the same VK (SGCAPS). */ + if (info->vk_lookup_table[entry->VirtualKey].table < 0) + { + info->vk_lookup_table[entry->VirtualKey].table = table_idx; + info->vk_lookup_table[entry->VirtualKey].index = entry_idx; + } /* Create reverse lookup entries to find a VK+modifier combinations * that results in a given character. */ @@ -497,7 +504,7 @@ init_vk_lookup_table (GdkWin32KeymapLayoutInfo *info) g_hash_table_insert (info->reverse_lookup_table, GINT_TO_POINTER (c), GINT_TO_POINTER (inserted_idx)); - } + } } } } diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index d835b44c67..4cb1e176a1 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -620,17 +620,6 @@ _gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap, } } -gboolean -_gdk_win32_keymap_has_altgr (GdkWin32Keymap *keymap) -{ - /* We just return FALSE, since it doesn't really matter because AltGr - * is the same as Ctrl + Alt. Hence, we will never get a GDK_MOD2_MASK, - * rather we will just get GDK_CONTROL_MASK | GDK_MOD1_MASK. I don't - * think there is any clean way to distinguish from - * on Windows. */ - return FALSE; -} - guint8 _gdk_win32_keymap_get_active_group (GdkWin32Keymap *keymap) { diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index e06aafc820..5173cf6af3 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -446,7 +446,6 @@ gint _gdk_win32_display_text_property_to_utf8_list (GdkDisplay *display, gchar ***list); gchar *_gdk_win32_display_utf8_to_string_target (GdkDisplay *display, const gchar *str); -gboolean _gdk_win32_keymap_has_altgr (GdkWin32Keymap *keymap); guint8 _gdk_win32_keymap_get_active_group (GdkWin32Keymap *keymap); guint8 _gdk_win32_keymap_get_rshift_scancode (GdkWin32Keymap *keymap); void _gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap, From cc47262ccbea9c9660b0ffeaf1b4d12e5908a505 Mon Sep 17 00:00:00 2001 From: David Hogan Date: Mon, 10 Jan 2022 03:34:59 +0100 Subject: [PATCH 5/5] gdkkeys-win32: Perform keyboard layout substitution For some users, GetKeyboardLayoutNameA() returns an alias instead of the fully resolved keyboard layout identifier. In that case, we have to query the registry to resolve the alias before we can look up the DLL path. See comments under #4610 --- gdk/win32/gdkkeys-win32.c | 97 +++++++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index 4cb1e176a1..84aa381902 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -166,6 +166,67 @@ vk_to_char_fuzzy (GdkWin32Keymap *keymap, consumed_mod_bits, is_dead, vk); } +/* + * Return the keyboard layout according to the user's keyboard layout + * substitution preferences. + * + * The result is heap-allocated and should be freed with g_free(). + */ +static char* +get_keyboard_layout_substituted_name (const char *layout_name) +{ + HKEY hkey = 0; + DWORD var_type = REG_SZ; + char *result = NULL; + DWORD buf_len = 0; + LSTATUS status; + + static const char *substitute_path = "Keyboard Layout\\Substitutes"; + + status = RegOpenKeyExA (HKEY_CURRENT_USER, substitute_path, 0, + KEY_QUERY_VALUE, &hkey); + if (status != ERROR_SUCCESS) + { + /* No substitute set for this value, not sure if this is a normal case */ + g_warning("Could not open registry key '%s'. Error code: %d", + substitute_path, (int)status); + + goto fail1; + } + + status = RegQueryValueExA (hkey, layout_name, 0, &var_type, 0, &buf_len); + if (status != ERROR_SUCCESS) + { + g_debug("Could not query registry key '%s\\%s'. Error code: %d", + substitute_path, layout_name, (int)status); + goto fail2; + } + + /* Allocate buffer */ + result = (char*) g_malloc (buf_len); + + /* Retrieve substitute name */ + status = RegQueryValueExA (hkey, layout_name, 0, &var_type, + (LPBYTE) result, &buf_len); + if (status != ERROR_SUCCESS) + { + g_warning("Could not obtain registry value at key '%s\\%s'. " + "Error code: %d", + substitute_path, layout_name, (int)status); + goto fail3; + } + + RegCloseKey (hkey); + return result; + +fail3: + g_free (result); +fail2: + RegCloseKey (hkey); +fail1: + return NULL; +} + /* * Get the file path of the keyboard layout dll. * The result is heap-allocated and should be freed with g_free(). @@ -173,21 +234,37 @@ vk_to_char_fuzzy (GdkWin32Keymap *keymap, static char* get_keyboard_layout_file (const char *layout_name) { - HKEY hkey = 0; - DWORD var_type = REG_SZ; - char *result = NULL; - DWORD file_name_len = 0; - int dir_len = 0; - int buf_len = 0; - LSTATUS status; + char *final_layout_name = NULL; + HKEY hkey = 0; + DWORD var_type = REG_SZ; + char *result = NULL; + DWORD file_name_len = 0; + int dir_len = 0; + int buf_len = 0; + LSTATUS status; static const char prefix[] = "SYSTEM\\CurrentControlSet\\Control\\" "Keyboard Layouts\\"; char kbdKeyPath[sizeof (prefix) + KL_NAMELENGTH]; - g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s", prefix, - layout_name); - + /* The user may have a keyboard substitute configured */ + final_layout_name = get_keyboard_layout_substituted_name (layout_name); + if (final_layout_name != NULL) + { + g_debug ("Substituting keyboard layout name from '%s' to '%s'", + layout_name, final_layout_name); + g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s", + prefix, final_layout_name); + g_free (final_layout_name); + final_layout_name = NULL; + } + else + { + g_debug ("Could not get substitute keyboard layout name for '%s', " + "will use '%s' directly", layout_name, layout_name); + g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s", + prefix, layout_name); + } status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hkey);