[gtk/gtk-3-24: 1/8] Rewrite GdkWin32Keymap (load table directly from layout DLL)
- From: Luca Bacci <lbacci src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/gtk-3-24: 1/8] Rewrite GdkWin32Keymap (load table directly from layout DLL)
- Date: Wed, 15 Dec 2021 11:15:29 +0000 (UTC)
commit 4039153ca793d5708d9dc9929b8cab29427f4bb7
Author: Philip Zander <philip zander gmail com>
Date: Tue Jul 28 20:22:30 2020 +0200
Rewrite GdkWin32Keymap (load table directly from layout DLL)
The old code used repeated calls to `ToUnicodeEx` to populate
the translation table, which is slow and buggy. The new code
directly loads the layout driver DLLs from Windows.
Associated issues: #2055 #1033
Merge request: !1051
GdkWin32Keymap cleanup
Conform to C89, improve comments, whitespace
gdk/win32/Makefile.am | 2 +
gdk/win32/gdkevents-win32.c | 27 +-
gdk/win32/gdkkeys-win32-impl-wow64.c | 4 +
gdk/win32/gdkkeys-win32-impl.c | 516 ++++++++++
gdk/win32/gdkkeys-win32.c | 1891 ++++++++++++----------------------
gdk/win32/gdkkeys-win32.h | 146 +++
gdk/win32/gdkprivate-win32.h | 1 +
gdk/win32/makefile.msc | 2 +
gdk/win32/meson.build | 2 +
9 files changed, 1309 insertions(+), 1282 deletions(-)
---
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 21f4579192..03abcd3f26 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -57,6 +57,8 @@ libgdk_win32_la_SOURCES = \
gdkglcontext-win32.h \
gdkglobals-win32.c \
gdkkeys-win32.c \
+ gdkkeys-win32-impl.c \
+ gdkkeys-win32-impl-wow64.c \
gdkmain-win32.c \
gdkmonitor-win32.c \
gdkmonitor-win32.h \
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 95c72da7c8..b4c78abe18 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -19,7 +19,7 @@
*/
/*
- * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2020. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
@@ -655,11 +655,9 @@ build_key_event_state (GdkEvent *event,
BYTE *key_state)
{
GdkWin32Keymap *keymap;
+ keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
- event->key.state = 0;
-
- if (key_state[VK_SHIFT] & 0x80)
- event->key.state |= GDK_SHIFT_MASK;
+ event->key.state = _gdk_win32_keymap_get_mod_mask (keymap);
if (key_state[VK_CAPITAL] & 0x01)
event->key.state |= GDK_LOCK_MASK;
@@ -675,26 +673,7 @@ build_key_event_state (GdkEvent *event,
if (key_state[VK_XBUTTON2] & 0x80)
event->key.state |= GDK_BUTTON5_MASK;
- keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
event->key.group = _gdk_win32_keymap_get_active_group (keymap);
-
- if (_gdk_win32_keymap_has_altgr (keymap) &&
- (key_state[VK_LCONTROL] & 0x80) &&
- (key_state[VK_RMENU] & 0x80))
- {
- event->key.state |= GDK_MOD2_MASK;
- if (key_state[VK_RCONTROL] & 0x80)
- event->key.state |= GDK_CONTROL_MASK;
- if (key_state[VK_LMENU] & 0x80)
- event->key.state |= GDK_MOD1_MASK;
- }
- else
- {
- if (key_state[VK_CONTROL] & 0x80)
- event->key.state |= GDK_CONTROL_MASK;
- if (key_state[VK_MENU] & 0x80)
- event->key.state |= GDK_MOD1_MASK;
- }
}
static gint
diff --git a/gdk/win32/gdkkeys-win32-impl-wow64.c b/gdk/win32/gdkkeys-win32-impl-wow64.c
new file mode 100644
index 0000000000..8f606e4b11
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32-impl-wow64.c
@@ -0,0 +1,4 @@
+#ifndef _WIN64
+#define GDK_WIN32_COMPILE_FOR_WOW64 1
+#include "gdkkeys-win32-impl.c"
+#endif
diff --git a/gdk/win32/gdkkeys-win32-impl.c b/gdk/win32/gdkkeys-win32-impl.c
new file mode 100644
index 0000000000..239045193e
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32-impl.c
@@ -0,0 +1,516 @@
+/* NOTE: When compiling the 32-bit version of the library, in addition to being
+ * compiled as a regular source file, this file is also included by
+ * gdkkeys-win32-impl-wow64.c to generate an alternate version of the code
+ * intended for running on a 64-bit kernel. Because of the way keyboard layout
+ * DLLs work on Windows, we have to generate two versions and decide at runtime
+ * which code path to execute. You can read more about the specifics below, in
+ * the section about KBD_LONG_POINTER. */
+
+#include "gdkkeys-win32.h"
+
+#ifndef GDK_WIN32_COMPILE_FOR_WOW64
+ #define GDK_WIN32_COMPILE_FOR_WOW64 0
+#endif
+
+/* This is our equivalent of the KBD_LONG_POINTER macro in Microsoft's kbd.h.
+ *
+ * A KBD_LONG_POINTER represents a pointer native to the *host*.
+ * I.e. 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.
+ *
+ * This is *not* the same as the the bitness of the application, since it is
+ * possible to execute 32-bit binaries on either a 32-bit or a 64-bit host.
+ * On a 64-bit host, KBD_LONG_PTR will be 64-bits, even if the application
+ * itself is 32-bit. (Whereas on a 32-bit host, it will be 32-bit.)
+ *
+ * For clarity, here is an overview of the bit-size of KBD_LONG_POINTER on all
+ * possible host & app combinations:
+ *
+ * Host 32 64
+ * App +-----------
+ * 32 | 32 64
+ * 64 | - 64
+ *
+ * In the official MS headers, KBD_LONG_POINTER is implemented via a macro
+ * which expands to the attribute `__ptr64` if the keyboard driver is
+ * compiled for a 64 bit host. Unfortunately, `__ptr64` is only
+ * supported by MSVC. We use a union here as a workaround.
+ *
+ * For all KBD_LONG_POINTERs, we define an alias starting with "KLP".
+ * Our naming schema (inspired by the Windows headers) is thus the following:
+ * - FOO: The type FOO itself
+ * - PFOO: Regular pointer to the type FOO
+ * - KLPFOO: Keyboard Long Pointer to the type FOO
+ */
+
+#if GDK_WIN32_COMPILE_FOR_WOW64
+ #define DEFINE_KBD_LONG_POINTER(type) \
+ typedef union { \
+ P##type ptr; \
+ UINT64 _align; \
+ } KLP##type
+#else
+ #define DEFINE_KBD_LONG_POINTER(type) \
+ typedef union { \
+ P##type ptr; \
+ } KLP##type
+#endif
+
+DEFINE_KBD_LONG_POINTER (USHORT);
+DEFINE_KBD_LONG_POINTER (VOID);
+
+/* Driver definitions
+ * Adapted from ReactOS's kbd.h:
+ * See https://github.com/reactos/reactos/blob/master/sdk/include/ndk/kbd.h
+ */
+
+typedef struct
+{
+ BYTE Vk;
+ BYTE ModBits;
+} VK_TO_BIT, *PVK_TO_BIT;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_BIT);
+
+typedef struct
+{
+ KLPVK_TO_BIT pVkToBit;
+ WORD wMaxModBits;
+ BYTE ModNumber[1];
+} MODIFIERS, *PMODIFIERS;
+
+DEFINE_KBD_LONG_POINTER (MODIFIERS);
+
+typedef struct
+{
+ BYTE Vsc;
+ USHORT Vk;
+} VSC_VK, *PVSC_VK;
+
+DEFINE_KBD_LONG_POINTER (VSC_VK);
+
+typedef struct
+{
+ BYTE Vk;
+ BYTE Vsc;
+} VK_VSC, *PVK_VSC;
+
+DEFINE_KBD_LONG_POINTER (VK_VSC);
+
+typedef struct
+{
+ BYTE VirtualKey;
+ BYTE Attributes;
+ WCHAR wch[1];
+} VK_TO_WCHARS, *PVK_TO_WCHARS;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_WCHARS);
+
+typedef struct
+{
+ KLPVK_TO_WCHARS pVkToWchars;
+ BYTE nModifications;
+ BYTE cbSize;
+} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_WCHAR_TABLE);
+
+typedef struct
+{
+ DWORD dwBoth;
+ WCHAR wchComposed;
+ USHORT uFlags;
+} DEADKEY, *PDEADKEY;
+
+DEFINE_KBD_LONG_POINTER (DEADKEY);
+
+typedef struct
+{
+ KLPMODIFIERS pCharModifiers;
+ KLPVK_TO_WCHAR_TABLE pVkToWcharTable;
+ KLPDEADKEY pDeadKey;
+ KLPVOID pKeyNames;
+ KLPVOID pKeyNamesExt;
+ KLPVOID pKeyNamesDead;
+ KLPUSHORT pusVSCtoVK;
+ BYTE bMaxVSCtoVK;
+ KLPVSC_VK pVSCtoVK_E0;
+ KLPVSC_VK pVSCtoVK_E1;
+ DWORD fLocaleFlags;
+ BYTE nLgMaxd;
+ BYTE cbLgEntry;
+ KLPVOID pLigature;
+} KBDTABLES, *PKBDTABLES;
+
+DEFINE_KBD_LONG_POINTER (KBDTABLES);
+
+/* End of declarations */
+
+static BYTE
+keystate_to_modbits (GdkWin32KeymapLayoutInfo *info,
+ const BYTE keystate[256])
+{
+ PKBDTABLES tables = (PKBDTABLES) info->tables;
+ PVK_TO_BIT vk_to_bit;
+ BYTE result = 0;
+ int i;
+
+ g_return_val_if_fail (tables != NULL, 0);
+
+ vk_to_bit = tables->pCharModifiers.ptr->pVkToBit.ptr;
+
+ for (i = 0; vk_to_bit[i].Vk != 0; ++i)
+ if (keystate[vk_to_bit[i].Vk] & 0x80)
+ result |= vk_to_bit[i].ModBits;
+
+ return result;
+}
+
+static BYTE
+modbits_to_level (GdkWin32KeymapLayoutInfo *info,
+ BYTE modbits)
+{
+ PKBDTABLES tables = (PKBDTABLES) info->tables;
+ PMODIFIERS modifiers;
+
+ g_return_val_if_fail (tables != NULL, 0);
+
+ modifiers = tables->pCharModifiers.ptr;
+ if (modbits > modifiers->wMaxModBits)
+ return 0;
+
+ return modifiers->ModNumber[modbits];
+}
+
+#define POPCOUNT(b) (!!(b & 0x01) + !!(b & 0x02) + !!(b & 0x04) + !!(b & 0x08) + \
+ !!(b & 0x10) + !!(b & 0x20) + !!(b & 0x40) + !!(b & 0x80))
+
+/*
+ * vk_to_char_fuzzy:
+ *
+ * For a given key and keystate, 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
+ * combination with Alt). Such modifiers will not be consumed.
+ *
+ * 'Best-fit' means 'consume as many modifiers as possibe'.
+ *
+ * For example (assuming a neutral keystate):
+ *
+ * - 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]
+ *
+ * The caller can supply additional modifiers to be added to the
+ * keystate in `extra_mod_bits`.
+ *
+ * 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 *consumed_mod_bits,
+ gboolean *is_dead,
+ BYTE vk)
+{
+ PKBDTABLES tables = (PKBDTABLES) info->tables;
+
+ PVK_TO_WCHAR_TABLE wch_tables;
+ PVK_TO_WCHAR_TABLE wch_table;
+ PVK_TO_WCHARS entry;
+
+ int table_index;
+ int entry_index;
+ int n_levels;
+ int entry_size;
+
+ g_return_val_if_fail (tables != NULL, WCH_NONE);
+
+ wch_tables = tables->pVkToWcharTable.ptr;
+
+ table_index = info->vk_lookup_table[vk].table;
+ entry_index = info->vk_lookup_table[vk].index;
+
+ if (table_index == -1 || entry_index == -1)
+ return WCH_NONE;
+
+ wch_table = &wch_tables[table_index];
+
+ n_levels = wch_table->nModifications;
+ entry_size = wch_table->cbSize;
+
+ entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+ + entry_size*entry_index);
+
+ 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). */
+
+ /* Key supporting capslock */
+ if ((entry->Attributes & CAPLOK) &&
+ /* 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;
+
+ /* Key supporting combination of capslock + altgr */
+ if ((entry->Attributes & CAPLOKALTGR) &&
+ (modbits & KBDALTGR) &&
+ (keystate[VK_CAPITAL] & 0x01))
+ modbits ^= 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;
+
+ /* 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;
+
+ /* We try to find the entry with the most matching modifiers */
+ for (level = 0; level < n_levels; ++level)
+ {
+ BYTE candidate_modbits = info->level_to_modbits[level];
+ gboolean candidate_is_dead = FALSE;
+ WCHAR c;
+ int score;
+
+ if (candidate_modbits & ~modbits)
+ continue;
+
+ c = entry->wch[level];
+ if (c == WCH_DEAD)
+ {
+ /* Next entry contains the undead keys */
+ PVK_TO_WCHARS next_entry;
+ next_entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+ + entry_size * (entry_index + 1));
+ c = next_entry->wch[level];
+ candidate_is_dead = TRUE;
+ }
+
+ if (c == WCH_DEAD || c == WCH_LGTR || c == WCH_NONE)
+ continue;
+
+ score = POPCOUNT (candidate_modbits & modbits);
+ if (score > best_score)
+ {
+ best_score = score;
+ best_char = c;
+ best_modifiers = candidate_modbits;
+ best_is_dead = candidate_is_dead;
+ }
+ }
+
+ if (consumed_mod_bits)
+ *consumed_mod_bits = best_modifiers;
+
+ if (is_dead)
+ *is_dead = best_is_dead;
+
+ return best_char;
+ }
+
+ return WCH_NONE;
+}
+
+static void
+init_vk_lookup_table (GdkWin32KeymapLayoutInfo *info)
+{
+ PKBDTABLES tables = (PKBDTABLES) info->tables;
+ PVK_TO_WCHAR_TABLE wch_tables;
+ PMODIFIERS modifiers;
+ int i, vk, table_idx;
+
+ g_return_if_fail (tables != NULL);
+
+ wch_tables = tables->pVkToWcharTable.ptr;
+
+ /* Initialize empty table */
+ memset (info->vk_lookup_table, -1, sizeof (info->vk_lookup_table));
+
+ /* Initialize level -> modbits lookup table */
+ memset (info->level_to_modbits, 0, sizeof(info->level_to_modbits));
+ info->max_level = 0;
+
+ modifiers = tables->pCharModifiers.ptr;
+ for (i = 0; i <= modifiers->wMaxModBits; ++i)
+ {
+ if (modifiers->ModNumber[i] != SHFT_INVALID &&
+ modifiers->ModNumber[i] != 0 /* Workaround for buggy layouts*/)
+ {
+ if (modifiers->ModNumber[i] > info->max_level)
+ info->max_level = modifiers->ModNumber[i];
+ info->level_to_modbits[modifiers->ModNumber[i]] = i;
+ }
+ }
+
+ info->max_modbit_value = modifiers->wMaxModBits;
+
+ /* For convenience, we add 256 identity-mapped entries corresponding to the VKs.
+ * This allows us to return a pointer to them from the `gdk_keysym_to_key_entry`
+ * function.
+ */
+
+ for (vk = 0; vk < 256; ++vk)
+ {
+ GdkWin32KeymapKeyEntry key_entry = {0};
+ key_entry.vk = vk;
+ key_entry.mod_bits = 0;
+ key_entry.next = -1;
+ g_array_append_val (info->key_entries, key_entry);
+ }
+
+ /* Special entry for ISO_Left_Tab */
+ {
+ GdkWin32KeymapKeyEntry key_entry = {0};
+ key_entry.vk = VK_TAB;
+ key_entry.mod_bits = KBDSHIFT;
+ key_entry.next = -1;
+ g_array_append_val (info->key_entries, key_entry);
+ }
+
+ /* Initialize generic vk <-> char tables */
+
+ for (table_idx = 0; ; ++table_idx)
+ {
+ PVK_TO_WCHAR_TABLE wch_table = &wch_tables[table_idx];
+ int entry_size;
+ int n_levels;
+ int entry_idx;
+
+ if (wch_table->pVkToWchars.ptr == NULL)
+ break;
+
+ entry_size = wch_table->cbSize;
+ n_levels = wch_table->nModifications;
+
+ for (entry_idx = 0; ; ++entry_idx)
+ {
+ PVK_TO_WCHARS entry;
+ int level;
+
+ entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+ + entry_size * entry_idx);
+
+ if (entry->VirtualKey == 0)
+ break;
+
+ /* 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;
+
+ /* Create reverse lookup entries to find a VK+modifier combinations
+ * that results in a given character. */
+ for (level = 0; level < n_levels; ++level)
+ {
+ GdkWin32KeymapKeyEntry key_entry = {0};
+ WCHAR c = entry->wch[level];
+ int inserted_idx;
+ gintptr next_idx;
+
+ key_entry.vk = entry->VirtualKey;
+ key_entry.mod_bits = info->level_to_modbits[level];
+
+ /* There can be multiple combinations that produce the same character.
+ * We store all of them in a linked list.
+ * Check if we already have an entry for the character, so we can chain
+ * them together. */
+ if (g_hash_table_lookup_extended (info->reverse_lookup_table,
+ GINT_TO_POINTER (c),
+ NULL, (gpointer*)&next_idx))
+ {
+ key_entry.next = next_idx;
+ }
+ else
+ {
+ key_entry.next = -1;
+ }
+
+ /* We store the KeyEntry in an array. In the hash table we only store
+ * the index. */
+
+ g_array_append_val (info->key_entries, key_entry);
+ inserted_idx = info->key_entries->len - 1;
+
+ g_hash_table_insert (info->reverse_lookup_table,
+ GINT_TO_POINTER (c),
+ GINT_TO_POINTER (inserted_idx));
+ }
+ }
+ }
+}
+
+static gboolean
+load_layout_dll (const char *dll,
+ GdkWin32KeymapLayoutInfo *info)
+{
+ typedef KLPKBDTABLES (*KbdLayerDescriptor)(VOID);
+
+ HMODULE lib;
+ KbdLayerDescriptor func;
+ KLPKBDTABLES tables;
+
+ g_return_val_if_fail (dll != NULL, FALSE);
+
+ lib = LoadLibraryA (dll);
+ if (lib == NULL)
+ goto fail1;
+
+ func = (KbdLayerDescriptor) GetProcAddress (lib, "KbdLayerDescriptor");
+ if (func == NULL)
+ goto fail2;
+
+ tables = func();
+
+ info->lib = lib;
+ info->tables = (PKBDTABLES) tables.ptr;
+
+ return TRUE;
+
+fail2:
+ FreeLibrary (lib);
+fail1:
+ return FALSE;
+}
+
+#if GDK_WIN32_COMPILE_FOR_WOW64
+ #define GDK_WIN32_KEYMAP_IMPL_NAME gdkwin32_keymap_impl_wow64
+#else
+ #define GDK_WIN32_KEYMAP_IMPL_NAME gdkwin32_keymap_impl
+#endif
+
+const GdkWin32KeymapImpl GDK_WIN32_KEYMAP_IMPL_NAME =
+ {
+ load_layout_dll,
+ init_vk_lookup_table,
+ keystate_to_modbits,
+ modbits_to_level,
+ vk_to_char_fuzzy
+ };
diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c
index 4ef8c4b102..50e6e3893d 100644
--- a/gdk/win32/gdkkeys-win32.c
+++ b/gdk/win32/gdkkeys-win32.c
@@ -16,7 +16,7 @@
*/
/*
- * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2020. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
@@ -38,106 +38,9 @@
#include "gdkkeysprivate.h"
#include "gdkwin32keys.h"
-enum _GdkWin32KeyLevelState
-{
- GDK_WIN32_LEVEL_NONE = 0,
- GDK_WIN32_LEVEL_SHIFT,
- GDK_WIN32_LEVEL_CAPSLOCK,
- GDK_WIN32_LEVEL_SHIFT_CAPSLOCK,
- GDK_WIN32_LEVEL_ALTGR,
- GDK_WIN32_LEVEL_SHIFT_ALTGR,
- GDK_WIN32_LEVEL_CAPSLOCK_ALTGR,
- GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR,
- GDK_WIN32_LEVEL_COUNT
-};
-
-typedef enum _GdkWin32KeyLevelState GdkWin32KeyLevelState;
-
-struct _GdkWin32KeyNode
-{
- /* Non-spacing version of the dead key */
- guint undead_gdk_keycode;
-
- /* Virtual key code */
- guint8 vk;
-
- /* Level for which this virtual key code produces this gdk_keycode */
- GdkWin32KeyLevelState level;
-
- /* GDK (X11) code for this key */
- guint gdk_keycode;
-
- /* Array of GdkWin32KeyNode should be sorted by gdk_keycode, then by level */
- GArray *combinations;
-};
+#include "gdkkeys-win32.h"
-typedef struct _GdkWin32KeyNode GdkWin32KeyNode;
-
-/*
-Example:
- GdkWin32KeyNode
- {
- undead_gdk_keycode = 0x0b4 GDK_KEY_acute (')
- vk = 0xde VK_OEM_7
- level = GDK_WIN32_LEVEL_NONE
- gdk_keycode = 0xfe51 GDK_KEY_dead_acute
- combinations =
- {
- GdkWin32KeyNode
- {
- undead_gdk_keycode = 0x061 GDK_KEY_a (a)
- level = GDK_WIN32_LEVEL_NONE
- vk = 0x41 VK_A
- gdk_keycode = 0xe1 GDK_KEY_aacute á
- combinations = NULL
- },
- GdkWin32KeyNode
- {
- unicode_char = 0x041 GDK_KEY_A (A)
- level = GDK_WIN32_LEVEL_SHIFT
- vk = 0x41 VK_A
- gdk_keycode = 0x0c1 GDK_KEY_Aacute Á
- combinations = NULL
- },
- { ... }
- }
- }
-
-Thus:
-
-GDK_KEY_dead_acute + GDK_KEY_a
-= GDK_KEY_aacute
-
-GDK_KEY_dead_acute + GDK_KEY_A
-= GDK_KEY_Aacute
-
-GDK_KEY_dead_acute + GDK_KEY_s
-matches partially
-(GDK_KEY_dead_acute is a known dead key, but does not combine with GDK_KEY_s)
-and resolves into:
-GDK_KEY_acute + GDK_KEY_s
-
-GDK_KEY_dead_somethingelse + GDK_KEY_anything
-does not match at all
-(W32 API did not provide any deadkey info for GDK_KEY_dead_somethingelse)
-and the caller will try other matching mechanisms for compose_buffer
-*/
-
-struct _GdkWin32KeyGroupOptions
-{
- /* character that should be used as the decimal separator */
- wchar_t decimal_mark;
-
- /* Scancode for the VK_RSHIFT */
- guint scancode_rshift;
-
- /* TRUE if Ctrl+Alt emulates AltGr */
- gboolean has_altgr;
-
- GArray *dead_keys;
-};
-
-typedef struct _GdkWin32KeyGroupOptions GdkWin32KeyGroupOptions;
+/* GdkWin32Keymap */
struct _GdkWin32KeymapClass
{
@@ -148,63 +51,82 @@ struct _GdkWin32Keymap
{
GdkKeymap parent_instance;
- /* length = what GetKeyboardLayoutList() returns, type = HKL.
- * When it changes, recreate the keymap and repopulate the options.
- */
+ /* Array of HKL */
GArray *layout_handles;
- /* VirtualKeyCode -> gdk_keyval table
- * length = 256 * length(layout_handles) * 2 * 4
- * 256 is the number of virtual key codes,
- * 2x4 is the number of Shift/AltGr/CapsLock combinations (level),
- * length(layout_handles) is the number of layout handles (group).
- */
- guint *keysym_tab;
-
- /* length = length(layout_handles), type = GdkWin32KeyGroupOptions
- * Kept separate from layout_handles because layout_handles is
- * populated by W32 API.
- */
- GArray *options;
+ /* Array of GdkWin32KeymapLayoutInfo */
+ GArray *layout_infos;
/* Index of a handle in layout_handles,
* at any point it should be the same handle as GetKeyboardLayout(0) returns,
* but GDK caches it to avoid calling GetKeyboardLayout (0) every time.
*/
guint8 active_layout;
+
+ guint current_serial;
+
+ /* Pointer to the implementation to be used. See comment in gdkkeys-win32.h.
+ * (we will dynamically choose at runtime for 32-bit builds based on whether
+ * we are running under WOW64)
+ */
+ const GdkWin32KeymapImpl *gdkwin32_keymap_impl;
};
G_DEFINE_TYPE (GdkWin32Keymap, gdk_win32_keymap, GDK_TYPE_KEYMAP)
guint _gdk_keymap_serial = 0;
-
static GdkKeymap *default_keymap = NULL;
-#define KEY_STATE_SIZE 256
-
-static void update_keymap (GdkKeymap *gdk_keymap);
+static void update_keymap (GdkWin32Keymap *gdk_keymap);
+static void clear_keyboard_layout_info (gpointer data);
-static void
-gdk_win32_key_group_options_clear (GdkWin32KeyGroupOptions *options)
+#ifndef _WIN64
+static BOOL
+is_wow64 (void)
{
- g_clear_pointer (&options->dead_keys, g_array_unref);
-}
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
-static void
-gdk_win32_key_node_clear (GdkWin32KeyNode *node)
-{
- g_clear_pointer (&node->combinations, g_array_unref);
+ BOOL bIsWow64 = FALSE;
+ LPFN_ISWOW64PROCESS fnIsWow64Process;
+
+ fnIsWow64Process = (LPFN_ISWOW64PROCESS)
+ GetProcAddress (GetModuleHandle (TEXT("kernel32")),
+ "IsWow64Process");
+
+ if (fnIsWow64Process != NULL)
+ fnIsWow64Process (GetCurrentProcess (), &bIsWow64);
+
+ return bIsWow64;
}
+#endif
static void
gdk_win32_keymap_init (GdkWin32Keymap *keymap)
{
- keymap->layout_handles = g_array_new (FALSE, FALSE, sizeof (HKL));
- keymap->options = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyGroupOptions));
- g_array_set_clear_func (keymap->options, (GDestroyNotify) gdk_win32_key_group_options_clear);
- keymap->keysym_tab = NULL;
+ /* Regular implementation (32 bit & 64 bit) */
+ extern const GdkWin32KeymapImpl gdkwin32_keymap_impl;
+ /* Implementation for 32 bit applications running on a 64 bit host (WOW64). */
+#ifndef _WIN64
+ extern const GdkWin32KeymapImpl gdkwin32_keymap_impl_wow64;
+#endif
+
+ keymap->layout_infos = g_array_new (FALSE, TRUE,
+ sizeof (GdkWin32KeymapLayoutInfo));
+ g_array_set_clear_func (keymap->layout_infos,
+ clear_keyboard_layout_info);
+
+ keymap->layout_handles = g_array_new (FALSE, FALSE,
+ sizeof (GdkWin32KeymapLayoutInfo));
+
keymap->active_layout = 0;
- update_keymap (GDK_KEYMAP (keymap));
+
+ keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl;
+#ifndef _WIN64
+ if (is_wow64())
+ keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl_wow64;
+#endif
+
+ update_keymap (keymap);
}
static void
@@ -212,933 +134,488 @@ gdk_win32_keymap_finalize (GObject *object)
{
GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (object);
- g_clear_pointer (&keymap->keysym_tab, g_free);
g_clear_pointer (&keymap->layout_handles, g_array_unref);
- g_clear_pointer (&keymap->options, g_array_unref);
+ g_clear_pointer (&keymap->layout_infos, g_array_unref);
G_OBJECT_CLASS (gdk_win32_keymap_parent_class)->finalize (object);
}
-#ifdef G_ENABLE_DEBUG
-static void
-print_keysym_tab (GdkWin32Keymap *keymap)
-{
- gint li;
- GdkWin32KeyGroupOptions *options;
- gint vk;
- GdkWin32KeyLevelState level;
- gint group_size = keymap->layout_handles->len;
-
- for (li = 0; li < group_size; li++)
- {
- options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, li);
- g_print ("keymap %d (0x%p):%s\n",
- li, g_array_index (keymap->layout_handles, HKL, li),
- options->has_altgr ? " (uses AltGr)" : "");
-
- for (vk = 0; vk < KEY_STATE_SIZE; vk++)
- {
- g_print ("%#.02x: ", vk);
-
- for (level = 0; level < GDK_WIN32_LEVEL_COUNT; level++)
- {
- gchar *name = gdk_keyval_name (keymap->keysym_tab[vk * group_size * GDK_WIN32_LEVEL_COUNT +
level]);
-
- g_print ("%s ", name ? name : "(none)");
- }
-
- g_print ("\n");
- }
- }
-}
-#endif
-
-static void
-handle_special (guint vk,
- guint *ksymp,
- gint shift)
+/* Convenience wrapper functions */
+static gboolean
+load_layout_dll (GdkWin32Keymap *keymap,
+ const char *dll,
+ GdkWin32KeymapLayoutInfo *info)
{
- switch (vk)
- {
- case VK_CANCEL:
- *ksymp = GDK_KEY_Cancel; break;
- case VK_BACK:
- *ksymp = GDK_KEY_BackSpace; break;
- case VK_TAB:
- if (shift & 0x1)
- *ksymp = GDK_KEY_ISO_Left_Tab;
- else
- *ksymp = GDK_KEY_Tab;
- break;
- case VK_CLEAR:
- *ksymp = GDK_KEY_Clear; break;
- case VK_RETURN:
- *ksymp = GDK_KEY_Return; break;
- case VK_SHIFT:
- case VK_LSHIFT:
- *ksymp = GDK_KEY_Shift_L; break;
- case VK_CONTROL:
- case VK_LCONTROL:
- *ksymp = GDK_KEY_Control_L; break;
- case VK_MENU:
- case VK_LMENU:
- *ksymp = GDK_KEY_Alt_L; break;
- case VK_PAUSE:
- *ksymp = GDK_KEY_Pause; break;
- case VK_ESCAPE:
- *ksymp = GDK_KEY_Escape; break;
- case VK_PRIOR:
- *ksymp = GDK_KEY_Prior; break;
- case VK_NEXT:
- *ksymp = GDK_KEY_Next; break;
- case VK_END:
- *ksymp = GDK_KEY_End; break;
- case VK_HOME:
- *ksymp = GDK_KEY_Home; break;
- case VK_LEFT:
- *ksymp = GDK_KEY_Left; break;
- case VK_UP:
- *ksymp = GDK_KEY_Up; break;
- case VK_RIGHT:
- *ksymp = GDK_KEY_Right; break;
- case VK_DOWN:
- *ksymp = GDK_KEY_Down; break;
- case VK_SELECT:
- *ksymp = GDK_KEY_Select; break;
- case VK_PRINT:
- *ksymp = GDK_KEY_Print; break;
- case VK_SNAPSHOT:
- *ksymp = GDK_KEY_Print; break;
- case VK_EXECUTE:
- *ksymp = GDK_KEY_Execute; break;
- case VK_INSERT:
- *ksymp = GDK_KEY_Insert; break;
- case VK_DELETE:
- *ksymp = GDK_KEY_Delete; break;
- case VK_HELP:
- *ksymp = GDK_KEY_Help; break;
- case VK_LWIN:
- *ksymp = GDK_KEY_Meta_L; break;
- case VK_RWIN:
- *ksymp = GDK_KEY_Meta_R; break;
- case VK_APPS:
- *ksymp = GDK_KEY_Menu; break;
- case VK_DECIMAL:
- *ksymp = GDK_KEY_KP_Decimal; break;
- case VK_MULTIPLY:
- *ksymp = GDK_KEY_KP_Multiply; break;
- case VK_ADD:
- *ksymp = GDK_KEY_KP_Add; break;
- case VK_SEPARATOR:
- *ksymp = GDK_KEY_KP_Separator; break;
- case VK_SUBTRACT:
- *ksymp = GDK_KEY_KP_Subtract; break;
- case VK_DIVIDE:
- *ksymp = GDK_KEY_KP_Divide; break;
- case VK_NUMPAD0:
- *ksymp = GDK_KEY_KP_0; break;
- case VK_NUMPAD1:
- *ksymp = GDK_KEY_KP_1; break;
- case VK_NUMPAD2:
- *ksymp = GDK_KEY_KP_2; break;
- case VK_NUMPAD3:
- *ksymp = GDK_KEY_KP_3; break;
- case VK_NUMPAD4:
- *ksymp = GDK_KEY_KP_4; break;
- case VK_NUMPAD5:
- *ksymp = GDK_KEY_KP_5; break;
- case VK_NUMPAD6:
- *ksymp = GDK_KEY_KP_6; break;
- case VK_NUMPAD7:
- *ksymp = GDK_KEY_KP_7; break;
- case VK_NUMPAD8:
- *ksymp = GDK_KEY_KP_8; break;
- case VK_NUMPAD9:
- *ksymp = GDK_KEY_KP_9; break;
- case VK_F1:
- *ksymp = GDK_KEY_F1; break;
- case VK_F2:
- *ksymp = GDK_KEY_F2; break;
- case VK_F3:
- *ksymp = GDK_KEY_F3; break;
- case VK_F4:
- *ksymp = GDK_KEY_F4; break;
- case VK_F5:
- *ksymp = GDK_KEY_F5; break;
- case VK_F6:
- *ksymp = GDK_KEY_F6; break;
- case VK_F7:
- *ksymp = GDK_KEY_F7; break;
- case VK_F8:
- *ksymp = GDK_KEY_F8; break;
- case VK_F9:
- *ksymp = GDK_KEY_F9; break;
- case VK_F10:
- *ksymp = GDK_KEY_F10; break;
- case VK_F11:
- *ksymp = GDK_KEY_F11; break;
- case VK_F12:
- *ksymp = GDK_KEY_F12; break;
- case VK_F13:
- *ksymp = GDK_KEY_F13; break;
- case VK_F14:
- *ksymp = GDK_KEY_F14; break;
- case VK_F15:
- *ksymp = GDK_KEY_F15; break;
- case VK_F16:
- *ksymp = GDK_KEY_F16; break;
- case VK_F17:
- *ksymp = GDK_KEY_F17; break;
- case VK_F18:
- *ksymp = GDK_KEY_F18; break;
- case VK_F19:
- *ksymp = GDK_KEY_F19; break;
- case VK_F20:
- *ksymp = GDK_KEY_F20; break;
- case VK_F21:
- *ksymp = GDK_KEY_F21; break;
- case VK_F22:
- *ksymp = GDK_KEY_F22; break;
- case VK_F23:
- *ksymp = GDK_KEY_F23; break;
- case VK_F24:
- *ksymp = GDK_KEY_F24; break;
- case VK_NUMLOCK:
- *ksymp = GDK_KEY_Num_Lock; break;
- case VK_SCROLL:
- *ksymp = GDK_KEY_Scroll_Lock; break;
- case VK_RSHIFT:
- *ksymp = GDK_KEY_Shift_R; break;
- case VK_RCONTROL:
- *ksymp = GDK_KEY_Control_R; break;
- case VK_RMENU:
- *ksymp = GDK_KEY_Alt_R; break;
- }
+ return keymap->gdkwin32_keymap_impl->load_layout_dll (dll, info);
}
static void
-set_level_vks (guchar *key_state,
- GdkWin32KeyLevelState level)
+init_vk_lookup_table (GdkWin32Keymap *keymap,
+ GdkWin32KeymapLayoutInfo *info)
{
- switch (level)
- {
- case GDK_WIN32_LEVEL_NONE:
- key_state[VK_SHIFT] = 0;
- key_state[VK_CAPITAL] = 0;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
- break;
- case GDK_WIN32_LEVEL_SHIFT:
- key_state[VK_SHIFT] = 0x80;
- key_state[VK_CAPITAL] = 0;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
- break;
- case GDK_WIN32_LEVEL_CAPSLOCK:
- key_state[VK_SHIFT] = 0;
- key_state[VK_CAPITAL] = 0x01;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
- break;
- case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK:
- key_state[VK_SHIFT] = 0x80;
- key_state[VK_CAPITAL] = 0x01;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
- break;
- case GDK_WIN32_LEVEL_ALTGR:
- key_state[VK_SHIFT] = 0;
- key_state[VK_CAPITAL] = 0;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
- break;
- case GDK_WIN32_LEVEL_SHIFT_ALTGR:
- key_state[VK_SHIFT] = 0x80;
- key_state[VK_CAPITAL] = 0;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
- break;
- case GDK_WIN32_LEVEL_CAPSLOCK_ALTGR:
- key_state[VK_SHIFT] = 0;
- key_state[VK_CAPITAL] = 0x01;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
- break;
- case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR:
- key_state[VK_SHIFT] = 0x80;
- key_state[VK_CAPITAL] = 0x01;
- key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
- break;
- case GDK_WIN32_LEVEL_COUNT:
- g_assert_not_reached ();
- break;
- }
+ keymap->gdkwin32_keymap_impl->init_vk_lookup_table (info);
}
-static void
-reset_after_dead (guchar key_state[KEY_STATE_SIZE],
- HKL handle)
+static BYTE
+keystate_to_modbits (GdkWin32Keymap *keymap,
+ GdkWin32KeymapLayoutInfo *info,
+ const BYTE keystate[256])
{
- guchar temp_key_state[KEY_STATE_SIZE];
- wchar_t wcs[2];
-
- memmove (temp_key_state, key_state, KEY_STATE_SIZE);
-
- temp_key_state[VK_SHIFT] =
- temp_key_state[VK_CONTROL] =
- temp_key_state[VK_CAPITAL] =
- temp_key_state[VK_MENU] = 0;
-
- ToUnicodeEx (VK_SPACE, MapVirtualKey (VK_SPACE, 0),
- temp_key_state, wcs, G_N_ELEMENTS (wcs),
- 0, handle);
+ return keymap->gdkwin32_keymap_impl->keystate_to_modbits (info, keystate);
}
-static void
-handle_dead (guint keysym,
- guint *ksymp)
+static BYTE
+modbits_to_level (GdkWin32Keymap *keymap,
+ GdkWin32KeymapLayoutInfo *info,
+ BYTE modbits)
{
- switch (keysym)
- {
- case '"': /* 0x022 */
- *ksymp = GDK_KEY_dead_diaeresis; break;
- case '\'': /* 0x027 */
- *ksymp = GDK_KEY_dead_acute; break;
- case GDK_KEY_asciicircum: /* 0x05e */
- *ksymp = GDK_KEY_dead_circumflex; break;
- case GDK_KEY_grave: /* 0x060 */
- *ksymp = GDK_KEY_dead_grave; break;
- case GDK_KEY_asciitilde: /* 0x07e */
- *ksymp = GDK_KEY_dead_tilde; break;
- case GDK_KEY_diaeresis: /* 0x0a8 */
- *ksymp = GDK_KEY_dead_diaeresis; break;
- case GDK_KEY_degree: /* 0x0b0 */
- *ksymp = GDK_KEY_dead_abovering; break;
- case GDK_KEY_acute: /* 0x0b4 */
- *ksymp = GDK_KEY_dead_acute; break;
- case GDK_KEY_periodcentered: /* 0x0b7 */
- *ksymp = GDK_KEY_dead_abovedot; break;
- case GDK_KEY_cedilla: /* 0x0b8 */
- *ksymp = GDK_KEY_dead_cedilla; break;
- case GDK_KEY_breve: /* 0x1a2 */
- *ksymp = GDK_KEY_dead_breve; break;
- case GDK_KEY_ogonek: /* 0x1b2 */
- *ksymp = GDK_KEY_dead_ogonek; break;
- case GDK_KEY_caron: /* 0x1b7 */
- *ksymp = GDK_KEY_dead_caron; break;
- case GDK_KEY_doubleacute: /* 0x1bd */
- *ksymp = GDK_KEY_dead_doubleacute; break;
- case GDK_KEY_abovedot: /* 0x1ff */
- *ksymp = GDK_KEY_dead_abovedot; break;
- case 0x1000384: /* Greek tonos */
- *ksymp = GDK_KEY_dead_acute; break;
- case GDK_KEY_Greek_accentdieresis: /* 0x7ae */
- *ksymp = GDK_KEY_Greek_accentdieresis; break;
- default:
- /* By default use the keysym as such. This takes care of for
- * instance the dead U+09CD (BENGALI VIRAMA) on the ekushey
- * Bengali layout.
- */
- *ksymp = keysym; break;
- }
+ return keymap->gdkwin32_keymap_impl->modbits_to_level (info, modbits);
}
-/* keypad decimal mark depends on active keyboard layout
- * return current decimal mark as unicode character
- */
-guint32
-_gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap)
+static WCHAR
+vk_to_char_fuzzy (GdkWin32Keymap *keymap,
+ GdkWin32KeymapLayoutInfo *info,
+ const BYTE keystate[256],
+ BYTE extra_mod_bits,
+ BYTE *consumed_mod_bits,
+ gboolean *is_dead,
+ BYTE vk)
{
- if (keymap != NULL &&
- keymap->layout_handles->len > 0 &&
- g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).decimal_mark)
- return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).decimal_mark;
-
- return (guint32) '.';
+ return keymap->gdkwin32_keymap_impl->vk_to_char_fuzzy (info, keystate, extra_mod_bits,
+ consumed_mod_bits, is_dead, vk);
}
-static gboolean
-layouts_are_the_same (GArray *array, HKL *hkls, gint hkls_len)
+/*
+ * Get the file path of the keyboard layout dll.
+ * The result is heap-allocated and should be freed with g_free().
+ */
+static char*
+get_keyboard_layout_file (const char *layout_name)
{
- gint i;
-
- if (hkls_len != array->len)
- return FALSE;
-
- for (i = 0; i < hkls_len; i++)
- if (hkls[i] != g_array_index (array, HKL, i))
- return FALSE;
-
- return TRUE;
+ HKEY hkey = 0;
+ DWORD var_type = REG_SZ;
+ char *result = NULL;
+ DWORD file_name_len = 0;
+ int dir_len = 0;
+ int buf_len = 0;
+
+ 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);
+
+ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0,
+ KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
+ goto fail1;
+
+ /* Get sizes */
+ if (RegQueryValueExA (hkey, "Layout File", 0, &var_type, 0,
+ &file_name_len) != ERROR_SUCCESS)
+ goto fail2;
+
+ dir_len = GetSystemDirectoryA (0, 0); /* includes \0 */
+ if (dir_len == 0)
+ goto fail2;
+
+ /* Allocate buffer */
+ buf_len = dir_len + (int) strlen ("\\") + file_name_len;
+ result = (char*) g_malloc (buf_len);
+
+ /* Append system directory. The -1 is because dir_len includes \0 */
+ if (GetSystemDirectoryA (&result[0], dir_len) != dir_len - 1)
+ goto fail3;
+
+ /* Append directory separator */
+ 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;
+
+ result[dir_len + file_name_len] = '\0';
+
+ RegCloseKey (hkey);
+ return result;
+
+fail3:
+ g_free (result);
+fail2:
+ RegCloseKey (hkey);
+fail1:
+ return NULL;
}
static void
-check_that_active_layout_is_in_sync (GdkWin32Keymap *keymap)
+clear_keyboard_layout_info (gpointer data)
{
- HKL hkl;
- HKL cached_hkl;
- wchar_t hkl_name[KL_NAMELENGTH];
+ GdkWin32KeymapLayoutInfo *layout_info = (GdkWin32KeymapLayoutInfo*) data;
- if (keymap->layout_handles->len <= 0)
- return;
+ g_free (layout_info->file);
- hkl = GetKeyboardLayout (0);
- cached_hkl = g_array_index (keymap->layout_handles, HKL, keymap->active_layout);
+ if (layout_info->key_entries != NULL)
+ g_array_unref (layout_info->key_entries);
- if (hkl != cached_hkl)
- {
- if (!GetKeyboardLayoutNameW (hkl_name))
- wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
+ if (layout_info->reverse_lookup_table != NULL)
+ g_hash_table_destroy (layout_info->reverse_lookup_table);
- g_warning ("Cached active layout #%d (0x%p) does not match actual layout %S, 0x%p",
- keymap->active_layout, cached_hkl, hkl_name, hkl);
- }
-}
+ if (layout_info->lib != NULL)
+ FreeLibrary (layout_info->lib);
-static gint
-sort_key_nodes_by_gdk_keyval (gconstpointer a,
- gconstpointer b)
-{
- const GdkWin32KeyNode *one = a;
- const GdkWin32KeyNode *two = b;
+ memset (layout_info, 0, sizeof (GdkWin32KeymapLayoutInfo));
+}
- if (one->gdk_keycode < two->gdk_keycode)
- return -1;
- else if (one->gdk_keycode > two->gdk_keycode)
- return 1;
+#define DEFINE_SPECIAL(map) \
+ map (VK_CANCEL, GDK_KEY_Cancel) \
+ map (VK_BACK, GDK_KEY_BackSpace) \
+ map (VK_CLEAR, GDK_KEY_Clear) \
+ map (VK_RETURN, GDK_KEY_Return) \
+ map (VK_LSHIFT, GDK_KEY_Shift_L) \
+ map (VK_LCONTROL, GDK_KEY_Control_L) \
+ map (VK_LMENU, GDK_KEY_Alt_L) \
+ map (VK_PAUSE, GDK_KEY_Pause) \
+ map (VK_ESCAPE, GDK_KEY_Escape) \
+ map (VK_PRIOR, GDK_KEY_Prior) \
+ map (VK_NEXT, GDK_KEY_Next) \
+ map (VK_END, GDK_KEY_End) \
+ map (VK_HOME, GDK_KEY_Home) \
+ map (VK_LEFT, GDK_KEY_Left) \
+ map (VK_UP, GDK_KEY_Up) \
+ map (VK_RIGHT, GDK_KEY_Right) \
+ map (VK_DOWN, GDK_KEY_Down) \
+ map (VK_SELECT, GDK_KEY_Select) \
+ map (VK_PRINT, GDK_KEY_Print) \
+ map (VK_EXECUTE, GDK_KEY_Execute) \
+ map (VK_INSERT, GDK_KEY_Insert) \
+ map (VK_DELETE, GDK_KEY_Delete) \
+ map (VK_HELP, GDK_KEY_Help) \
+ map (VK_LWIN, GDK_KEY_Meta_L) \
+ map (VK_RWIN, GDK_KEY_Meta_R) \
+ map (VK_APPS, GDK_KEY_Menu) \
+ map (VK_DECIMAL, GDK_KEY_KP_Decimal) \
+ map (VK_MULTIPLY, GDK_KEY_KP_Multiply) \
+ map (VK_ADD, GDK_KEY_KP_Add) \
+ map (VK_SEPARATOR, GDK_KEY_KP_Separator) \
+ map (VK_SUBTRACT, GDK_KEY_KP_Subtract) \
+ map (VK_DIVIDE, GDK_KEY_KP_Divide) \
+ map (VK_NUMPAD0, GDK_KEY_KP_0) \
+ map (VK_NUMPAD1, GDK_KEY_KP_1) \
+ map (VK_NUMPAD2, GDK_KEY_KP_2) \
+ map (VK_NUMPAD3, GDK_KEY_KP_3) \
+ map (VK_NUMPAD4, GDK_KEY_KP_4) \
+ map (VK_NUMPAD5, GDK_KEY_KP_5) \
+ map (VK_NUMPAD6, GDK_KEY_KP_6) \
+ map (VK_NUMPAD7, GDK_KEY_KP_7) \
+ map (VK_NUMPAD8, GDK_KEY_KP_8) \
+ map (VK_NUMPAD9, GDK_KEY_KP_9) \
+ map (VK_F1, GDK_KEY_F1) \
+ map (VK_F2, GDK_KEY_F2) \
+ map (VK_F3, GDK_KEY_F3) \
+ map (VK_F4, GDK_KEY_F4) \
+ map (VK_F5, GDK_KEY_F5) \
+ map (VK_F6, GDK_KEY_F6) \
+ map (VK_F7, GDK_KEY_F7) \
+ map (VK_F8, GDK_KEY_F8) \
+ map (VK_F9, GDK_KEY_F9) \
+ map (VK_F10, GDK_KEY_F10) \
+ map (VK_F11, GDK_KEY_F11) \
+ map (VK_F12, GDK_KEY_F12) \
+ map (VK_F13, GDK_KEY_F13) \
+ map (VK_F14, GDK_KEY_F14) \
+ map (VK_F15, GDK_KEY_F15) \
+ map (VK_F16, GDK_KEY_F16) \
+ map (VK_F17, GDK_KEY_F17) \
+ map (VK_F18, GDK_KEY_F18) \
+ map (VK_F19, GDK_KEY_F19) \
+ map (VK_F20, GDK_KEY_F20) \
+ map (VK_F21, GDK_KEY_F21) \
+ map (VK_F22, GDK_KEY_F22) \
+ map (VK_F23, GDK_KEY_F23) \
+ map (VK_F24, GDK_KEY_F24) \
+ map (VK_NUMLOCK, GDK_KEY_Num_Lock) \
+ 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)
+
+
+#define DEFINE_DEAD(map) \
+ map ('"', /* 0x022 */ GDK_KEY_dead_diaeresis) \
+ map ('\'', /* 0x027 */ GDK_KEY_dead_acute) \
+ map (GDK_KEY_asciicircum, /* 0x05e */ GDK_KEY_dead_circumflex) \
+ map (GDK_KEY_grave, /* 0x060 */ GDK_KEY_dead_grave) \
+ map (GDK_KEY_asciitilde, /* 0x07e */ GDK_KEY_dead_tilde) \
+ map (GDK_KEY_diaeresis, /* 0x0a8 */ GDK_KEY_dead_diaeresis) \
+ map (GDK_KEY_degree, /* 0x0b0 */ GDK_KEY_dead_abovering) \
+ map (GDK_KEY_acute, /* 0x0b4 */ GDK_KEY_dead_acute) \
+ map (GDK_KEY_periodcentered, /* 0x0b7 */ GDK_KEY_dead_abovedot) \
+ map (GDK_KEY_cedilla, /* 0x0b8 */ GDK_KEY_dead_cedilla) \
+ map (GDK_KEY_breve, /* 0x1a2 */ GDK_KEY_dead_breve) \
+ map (GDK_KEY_ogonek, /* 0x1b2 */ GDK_KEY_dead_ogonek) \
+ map (GDK_KEY_caron, /* 0x1b7 */ GDK_KEY_dead_caron) \
+ map (GDK_KEY_doubleacute, /* 0x1bd */ GDK_KEY_dead_doubleacute) \
+ map (GDK_KEY_abovedot, /* 0x1ff */ GDK_KEY_dead_abovedot) \
+ map (0x1000384, /* Greek tonos */ GDK_KEY_dead_acute) \
+ map (GDK_KEY_Greek_accentdieresis, /* 0x7ae */ GDK_KEY_Greek_accentdieresis)
- if (one->level < two->level)
- return -1;
- else if (one->level > two->level)
- return 1;
- return 0;
-}
+static guint
+vk_and_mod_bits_to_gdk_keysym (GdkWin32Keymap *keymap,
+ GdkWin32KeymapLayoutInfo *info,
+ guint vk,
+ const BYTE keystate[256],
+ BYTE mod_bits,
+ BYTE *consumed_mod_bits)
-static void
-update_keymap (GdkKeymap *gdk_keymap)
{
- int hkls_len;
- static int hkls_size = 0;
- static HKL *hkls = NULL;
- gboolean no_list;
- static guint current_serial = 0;
- gint i, group;
- GdkWin32KeyLevelState level;
- GdkWin32KeyGroupOptions *options;
- GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (gdk_keymap);
- gint keysym_tab_size;
-
- guchar key_state[KEY_STATE_SIZE];
- guint scancode;
- guint vk;
- guint *keygroup;
-
- if (keymap->keysym_tab != NULL &&
- current_serial == _gdk_keymap_serial)
- return;
-
- no_list = FALSE;
- hkls_len = GetKeyboardLayoutList (0, NULL);
-
- if (hkls_len <= 0)
- {
- hkls_len = 1;
- no_list = TRUE;
- }
- else if (hkls_len > 255)
- {
- hkls_len = 255;
- }
+ gboolean is_dead = FALSE;
+ gunichar c;
+ guint sym;
- if (hkls_size < hkls_len)
- {
- hkls = g_renew (HKL, hkls, hkls_len);
- hkls_size = hkls_len;
- }
+ c = vk_to_char_fuzzy (keymap, info, keystate, mod_bits,
+ consumed_mod_bits, &is_dead, vk);
- if (hkls_len != GetKeyboardLayoutList (hkls_len, hkls))
+ if (!is_dead)
{
- if (!no_list)
- return;
-
- hkls[0] = GetKeyboardLayout (0);
- hkls_len = 1;
+ /* Special cases */
+ if (vk == VK_SHIFT)
+ return GDK_KEY_Shift_L;
+ if (vk == VK_CONTROL)
+ return GDK_KEY_Control_L;
+ if (vk == VK_MENU)
+ return GDK_KEY_Alt_L;
+ if (vk == VK_SNAPSHOT)
+ return GDK_KEY_Print;
+ if (vk == VK_TAB && !(mod_bits & KBDSHIFT))
+ return GDK_KEY_Tab;
+ if (vk == VK_TAB && (mod_bits & KBDSHIFT))
+ return GDK_KEY_ISO_Left_Tab;
+
+ /* Generic special keys */
+ switch (vk)
+ {
+ #define MAP(a_vk, a_gdk) case a_vk: return a_gdk;
+ DEFINE_SPECIAL (MAP)
+ #undef MAP
+ }
}
- if (layouts_are_the_same (keymap->layout_handles, hkls, hkls_len))
+ if (c == WCH_NONE)
{
- check_that_active_layout_is_in_sync (keymap);
- current_serial = _gdk_keymap_serial;
-
- return;
+ if (consumed_mod_bits)
+ *consumed_mod_bits = 0;
+ return GDK_KEY_VoidSymbol;
}
- GDK_NOTE (EVENTS, g_print ("\nHave %d keyboard layouts:", hkls_len));
+ sym = gdk_unicode_to_keyval (c);
- for (i = 0; i < hkls_len; i++)
+ if (is_dead)
{
- GDK_NOTE (EVENTS, g_print (" 0x%p", hkls[i]));
-
- if (GetKeyboardLayout (0) == hkls[i])
- {
- wchar_t hkl_name[KL_NAMELENGTH];
-
- if (!GetKeyboardLayoutNameW (hkl_name))
- wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
-
- GDK_NOTE (EVENTS, g_print ("(active, %S)", hkl_name));
- }
+ switch (sym)
+ {
+ #define MAP(a_nondead, a_dead) case a_nondead: return a_dead;
+ DEFINE_DEAD (MAP)
+ #undef MAP
+ }
}
- GDK_NOTE (EVENTS, g_print ("\n"));
-
- keysym_tab_size = hkls_len * 256 * 2 * 4;
+ return sym;
+}
- if (hkls_len != keymap->layout_handles->len)
- keymap->keysym_tab = g_renew (guint, keymap->keysym_tab, keysym_tab_size);
+static gint
+gdk_keysym_to_key_entry_index (GdkWin32KeymapLayoutInfo *info,
+ guint sym)
+{
+ gunichar c;
+ gintptr index;
- memset (key_state, 0, sizeof(key_state));
- memset (keymap->keysym_tab, 0, keysym_tab_size);
- g_array_set_size (keymap->layout_handles, hkls_len);
- g_array_set_size (keymap->options, hkls_len);
+ /* Special cases */
+ if (sym == GDK_KEY_Tab)
+ return VK_TAB;
+ if (sym == GDK_KEY_ISO_Left_Tab)
+ return 256;
- for (i = 0; i < hkls_len; i++)
+ /* Generic non-printable keys */
+ switch (sym)
{
- options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, i);
-
- options->decimal_mark = 0;
- options->scancode_rshift = 0;
- options->has_altgr = FALSE;
- options->dead_keys = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyNode));
- g_array_set_clear_func (options->dead_keys, (GDestroyNotify) gdk_win32_key_node_clear);
-
- g_array_index (keymap->layout_handles, HKL, i) = hkls[i];
-
- if (hkls[i] == _gdk_input_locale)
- keymap->active_layout = i;
+ #define MAP(a_vk, a_gdk) case a_gdk: return a_vk;
+ DEFINE_SPECIAL (MAP)
+ #undef MAP
}
- for (vk = 0; vk < KEY_STATE_SIZE; vk++)
- {
- for (group = 0; group < hkls_len; group++)
- {
- options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, group);
- scancode = MapVirtualKeyEx (vk, 0, hkls[group]);
- keygroup = &keymap->keysym_tab[(vk * hkls_len + group) * GDK_WIN32_LEVEL_COUNT];
-
- /* MapVirtualKeyEx() fails to produce a scancode for VK_DIVIDE and VK_PAUSE.
- * Ignore that, handle_special() will figure out a Gdk keyval for these
- * without needing a scancode.
- */
- if (scancode == 0 &&
- vk != VK_DIVIDE &&
- vk != VK_PAUSE)
- {
- for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
- keygroup[level] = GDK_KEY_VoidSymbol;
-
- continue;
- }
-
- if (vk == VK_RSHIFT)
- options->scancode_rshift = scancode;
-
- key_state[vk] = 0x80;
-
- for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
- {
- guint *ksymp = &keygroup[level];
-
- set_level_vks (key_state, level);
+ /* Fix up dead keys */
+ #define MAP(a_nondead, a_dead) \
+ if (sym == a_dead) \
+ sym = a_nondead;
+ DEFINE_DEAD (MAP)
+ #undef MAP
- *ksymp = 0;
+ /* Try converting to Unicode and back */
+ c = gdk_keyval_to_unicode (sym);
- /* First, handle those virtual keys that we always want
- * as special GDK_* keysyms, even if ToAsciiEx might
- * turn some them into a ASCII character (like TAB and
- * ESC).
- */
- handle_special (vk, ksymp, level);
-
- if ((*ksymp == 0) ||
- ((vk == VK_DECIMAL) && (level == GDK_WIN32_LEVEL_NONE)))
- {
- wchar_t wcs[10];
- gint k;
- guint keysym;
- GdkWin32KeyNode dead_key;
-
- wcs[0] = wcs[1] = 0;
- k = ToUnicodeEx (vk, scancode, key_state,
- wcs, G_N_ELEMENTS (wcs),
- 0, hkls[group]);
-#if 0
- g_print ("ToUnicodeEx(%#02x, %d: %d): %d, %04x %04x\n",
- vk, scancode, level, k,
- wcs[0], wcs[1]);
-#endif
- switch (k)
- {
- case 1:
- if ((vk == VK_DECIMAL) && (level == GDK_WIN32_LEVEL_NONE))
- options->decimal_mark = wcs[0];
- else
- *ksymp = gdk_unicode_to_keyval (wcs[0]);
- break;
- case -1:
- keysym = gdk_unicode_to_keyval (wcs[0]);
-
- /* It is a dead key, and it has been stored in
- * the keyboard layout's state by
- * ToAsciiEx()/ToUnicodeEx(). Yes, this is an
- * incredibly silly API! Make the keyboard
- * layout forget it by calling
- * ToAsciiEx()/ToUnicodeEx() once more, with the
- * virtual key code and scancode for the
- * spacebar, without shift or AltGr. Otherwise
- * the next call to ToAsciiEx() with a different
- * key would try to combine with the dead key.
- */
- reset_after_dead (key_state, hkls[group]);
-
- /* Use dead keysyms instead of "undead" ones */
- handle_dead (keysym, ksymp);
-
- dead_key.undead_gdk_keycode = keysym;
- dead_key.vk = vk;
- dead_key.level = level;
- dead_key.gdk_keycode = *ksymp;
- dead_key.combinations = NULL;
- g_array_append_val (options->dead_keys, dead_key);
- break;
- case 0:
- /* Seems to be necessary to "reset" the keyboard layout
- * in this case, too. Otherwise problems on NT4.
- */
- reset_after_dead (key_state, hkls[group]);
- break;
- default:
-#if 0
- GDK_NOTE (EVENTS,
- g_print ("ToUnicodeEx returns %d "
- "for vk:%02x, sc:%02x%s%s\n",
- k, vk, scancode,
- (shift&0x1 ? " shift" : ""),
- (shift&0x2 ? " altgr" : "")));
-#endif
- break;
- }
- }
+ g_return_val_if_fail (info->reverse_lookup_table != NULL, -1);
- if (*ksymp == 0)
- *ksymp = GDK_KEY_VoidSymbol;
- }
-
- key_state[vk] = 0;
-
- /* Check if keyboard has an AltGr key by checking if
- * the mapping with Control+Alt is different.
- * Don't test CapsLock here, as it does not seem to affect
- * dead keys themselves, only the results of dead key combinations.
- */
- if (!options->has_altgr)
- if ((keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol &&
- keygroup[GDK_WIN32_LEVEL_NONE] != keygroup[GDK_WIN32_LEVEL_ALTGR]) ||
- (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] != GDK_KEY_VoidSymbol &&
- keygroup[GDK_WIN32_LEVEL_SHIFT] != keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]))
- options->has_altgr = TRUE;
- }
+ index = -1;
+ if (g_hash_table_lookup_extended (info->reverse_lookup_table,
+ GINT_TO_POINTER (c),
+ NULL, (gpointer*) &index))
+ {
+ return index;
}
-
- scancode = 0x0;
-
- for (group = 0; group < hkls_len; group++)
+ else
{
- options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, group);
-
- for (i = 0; i < options->dead_keys->len; i++)
- {
- wchar_t wcs[10];
- gint k;
- GdkWin32KeyNode *dead_key;
- GdkWin32KeyNode combo;
-
- dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, i);
-
- for (vk = 0; vk < KEY_STATE_SIZE; vk++)
- {
- for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
- {
- /* Prime the ToUnicodeEx() internal state */
- wcs[0] = wcs[1] = 0;
- set_level_vks (key_state, dead_key->level);
- k = ToUnicodeEx (dead_key->vk, scancode, key_state,
- wcs, G_N_ELEMENTS (wcs),
- 0, hkls[group]);
- switch (k)
- {
- case -1:
- /* Okay */
- break;
- default:
- /* Expected a dead key, got something else */
- reset_after_dead (key_state, hkls[group]);
- continue;
- }
-
- /* Check how it combines with vk */
- wcs[0] = wcs[1] = 0;
- set_level_vks (key_state, level);
- k = ToUnicodeEx (vk, scancode, key_state,
- wcs, G_N_ELEMENTS (wcs),
- 0, hkls[group]);
-
- if (k == 0)
- {
- reset_after_dead (key_state, hkls[group]);
- }
- else if (k == -1)
- {
- /* Dead key chaining? TODO: support this (deeper tree?) */
- reset_after_dead (key_state, hkls[group]);
- }
- else if (k == 1)
- {
- combo.vk = vk;
- combo.level = level;
- combo.gdk_keycode = gdk_unicode_to_keyval (wcs[0]);
- combo.undead_gdk_keycode = combo.gdk_keycode;
- combo.combinations = NULL;
-
- if (dead_key->combinations == NULL)
- {
- dead_key->combinations = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyNode));
- g_array_set_clear_func (dead_key->combinations, (GDestroyNotify)
gdk_win32_key_node_clear);
- }
-
-#if 0
- {
- char *dead_key_undead_u8, *wcs_u8;
- wchar_t t = gdk_keyval_to_unicode (dead_key->undead_gdk_keycode);
- dead_key_undead_u8 = g_utf16_to_utf8 (&t, 1, NULL, NULL, NULL);
- wcs_u8 = g_utf16_to_utf8 (wcs, 1, NULL, NULL, NULL);
- g_fprintf (stdout, "%d %s%s%s0x%02x (%s) + %s%s%s0x%02x = 0x%04x (%s)\n", group,
- (dead_key->level == GDK_WIN32_LEVEL_SHIFT ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "SHIFT-" : "
",
- (dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
- dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "CAPSLOCK-" : "
",
- (dead_key->level == GDK_WIN32_LEVEL_ALTGR ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
- dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
- dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "ALTGR-" : "
",
- dead_key->vk,
- dead_key_undead_u8,
- (combo.level == GDK_WIN32_LEVEL_SHIFT ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "SHIFT-" : " ",
- (combo.level == GDK_WIN32_LEVEL_CAPSLOCK ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
- combo.level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "CAPSLOCK-" : "
",
- (combo.level == GDK_WIN32_LEVEL_ALTGR ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
- combo.level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
- combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "ALTGR-" : " ",
- vk,
- wcs[0],
- wcs_u8);
- g_free (dead_key_undead_u8);
- g_free (wcs_u8);
- }
-#endif
-
- g_array_append_val (dead_key->combinations, combo);
- }
- }
- }
- }
-
- g_array_sort (options->dead_keys, (GCompareFunc) sort_key_nodes_by_gdk_keyval);
+ return -1;
}
-
- GDK_NOTE (EVENTS, print_keysym_tab (keymap));
-
- check_that_active_layout_is_in_sync (keymap);
- current_serial = _gdk_keymap_serial;
}
-static gboolean
-find_deadkey_by_keyval (GArray *dead_keys, guint16 keyval, gsize *index)
+static GdkModifierType
+mod_bits_to_gdk_mod_mask (BYTE mod_bits)
{
- gsize deadkey_i;
- gsize deadkey_i_max;
-
- if (dead_keys->len == 0)
- return FALSE;
+ GdkModifierType result = 0;
+ if (mod_bits & KBDSHIFT)
+ result |= GDK_SHIFT_MASK;
+ if (mod_bits & KBDCTRL)
+ result |= GDK_CONTROL_MASK;
+ if (mod_bits & KBDALT)
+ result |= GDK_MOD1_MASK;
+ if ((mod_bits & KBDALTGR) == KBDALTGR)
+ result |= GDK_MOD2_MASK;
+ if (mod_bits & KBDKANA)
+ result |= GDK_MOD3_MASK;
+ if (mod_bits & KBDROYA)
+ result |= GDK_MOD4_MASK;
+ if (mod_bits & KBDLOYA)
+ result |= GDK_MODIFIER_RESERVED_13_MASK;
+ if (mod_bits & KBDGRPSELTAP)
+ result |= GDK_MODIFIER_RESERVED_14_MASK;
+ return result;
+}
- deadkey_i = 0;
- deadkey_i_max = dead_keys->len - 1;
+static BYTE
+gdk_mod_mask_to_mod_bits (GdkModifierType mod_mask)
+{
+ BYTE result = 0;
+ if (mod_mask & GDK_SHIFT_MASK)
+ result |= KBDSHIFT;
+ if (mod_mask & GDK_CONTROL_MASK)
+ result |= KBDCTRL;
+ if (mod_mask & GDK_MOD1_MASK)
+ result |= KBDALT;
+ if (mod_mask & GDK_MOD2_MASK)
+ result |= KBDALTGR;
+ if (mod_mask & GDK_MOD3_MASK)
+ result |= KBDKANA;
+ if (mod_mask & GDK_MOD4_MASK)
+ result |= KBDROYA;
+ if (mod_mask & GDK_MODIFIER_RESERVED_13_MASK)
+ result |= KBDLOYA;
+ if (mod_mask & GDK_MODIFIER_RESERVED_14_MASK)
+ result |= KBDGRPSELTAP;
+ return result;
+}
- while (deadkey_i != deadkey_i_max)
+static void
+get_lock_state (BYTE lock_state[256])
+{
+ static const guint mode_keys[] =
{
- GdkWin32KeyNode *dead_key;
- gsize middle;
+ VK_CAPITAL,
+ VK_KANA, VK_HANGUL, VK_JUNJA, VK_FINAL, VK_HANJA, VK_KANJI, /* Is this correct? */
+ VK_NUMLOCK, VK_SCROLL
+ };
- if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i).gdk_keycode == keyval)
- {
- break;
- }
- else if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i_max).gdk_keycode == keyval)
- {
- deadkey_i = deadkey_i_max;
- break;
- }
- else if (deadkey_i + 1 == deadkey_i_max)
- {
- break;
- }
+ BYTE keystate[256] = {0};
+ guint i;
- middle = deadkey_i + (deadkey_i_max - deadkey_i) / 2;
- dead_key = &g_array_index (dead_keys, GdkWin32KeyNode, middle);
+ GetKeyboardState (keystate);
- if (dead_key->gdk_keycode < keyval)
- deadkey_i = middle;
- else if (dead_key->gdk_keycode > keyval)
- deadkey_i_max = middle;
- else
- deadkey_i = deadkey_i_max = middle;
- }
-
- if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i).gdk_keycode == keyval)
- {
- *index = deadkey_i;
+ /* 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;
+}
- return TRUE;
- }
- return FALSE;
+/* keypad decimal mark depends on active keyboard layout
+ * return current decimal mark as unicode character
+ */
+guint32
+_gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap)
+{
+ guint32 c = MapVirtualKeyW (VK_DECIMAL, MAPVK_VK_TO_CHAR);
+ if (!c)
+ c = (guint32) '.';
+ return c;
}
-GdkWin32KeymapMatch
-gdk_win32_keymap_check_compose (GdkWin32Keymap *keymap,
- guint16 *compose_buffer,
- gsize compose_buffer_len,
- guint16 *output,
- gsize *output_len)
+static void
+update_keymap (GdkWin32Keymap *keymap)
{
- gint partial_match;
- guint8 active_group;
- gsize deadkey_i, node_i;
- GdkWin32KeyNode *dead_key;
- GdkWin32KeyGroupOptions *options;
- GdkWin32KeymapMatch match;
- gsize output_size;
-
- g_return_val_if_fail (output != NULL && output_len != NULL, GDK_WIN32_KEYMAP_MATCH_NONE);
+ HKL current_layout;
+ BOOL changed = FALSE;
+ int n_layouts;
+ int i;
- if (compose_buffer_len < 1)
- return GDK_WIN32_KEYMAP_MATCH_NONE;
+ if (keymap->current_serial == _gdk_keymap_serial &&
+ keymap->layout_handles->len > 0)
+ {
+ return;
+ }
- output_size = *output_len;
+ n_layouts = GetKeyboardLayoutList (0, 0);
+ g_array_set_size (keymap->layout_handles, n_layouts);
+ n_layouts = GetKeyboardLayoutList (n_layouts,
+ &g_array_index(keymap->layout_handles,
+ HKL, 0));
- active_group = _gdk_win32_keymap_get_active_group (keymap);
- options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, active_group);
+ g_array_set_size (keymap->layout_infos, n_layouts);
- partial_match = -1;
- match = GDK_WIN32_KEYMAP_MATCH_NONE;
+ current_layout = GetKeyboardLayout (0);
- if (find_deadkey_by_keyval (options->dead_keys, compose_buffer[0], &deadkey_i))
+ for (i = 0; i < n_layouts; ++i)
{
- while (deadkey_i > 0 &&
- g_array_index (options->dead_keys, GdkWin32KeyNode, deadkey_i - 1).gdk_keycode ==
compose_buffer[0])
- deadkey_i--;
-
- /* Hardcoded 2-tier tree here (dead key + non dead key = character).
- * TODO: support trees with arbitrary depth for dead key chaining.
- */
- dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, deadkey_i);
-
- /* "Partial match" means "matched the whole sequence except the last key"
- * (right now the sequence only has 2 keys, so this turns into "matched
- * at least the first key").
- * "last key" should be identified by having NULL further combinations.
- * As a heuristic, convert the buffer contents into keyvals and use
- * them as-is (normally there should be a separate unichar buffer for
- * each combination, but we do not store these).
- */
- partial_match = deadkey_i;
-
- if (compose_buffer_len < 2)
- match = GDK_WIN32_KEYMAP_MATCH_INCOMPLETE;
-
- for (node_i = 0;
- match != GDK_WIN32_KEYMAP_MATCH_INCOMPLETE &&
- node_i < dead_key->combinations->len;
- node_i++)
- {
- GdkWin32KeyNode *node;
-
- node = &g_array_index (dead_key->combinations, GdkWin32KeyNode, node_i);
-
- if (keymap->keysym_tab[(node->vk * keymap->layout_handles->len + active_group) *
GDK_WIN32_LEVEL_COUNT + node->level] == compose_buffer[1])
- {
- match = GDK_WIN32_KEYMAP_MATCH_EXACT;
- *output_len = 0;
+ GdkWin32KeymapLayoutInfo *info = &g_array_index(keymap->layout_infos,
+ GdkWin32KeymapLayoutInfo, i);
+ HKL hkl = g_array_index(keymap->layout_handles, HKL, i);
- if (*output_len < output_size && node->gdk_keycode != 0)
- output[(*output_len)++] = node->gdk_keycode;
+ if (info->handle != hkl)
+ {
+ changed = TRUE;
- break;
- }
- }
- }
+ /* Free old data */
+ clear_keyboard_layout_info (info);
- if (match == GDK_WIN32_KEYMAP_MATCH_EXACT ||
- match == GDK_WIN32_KEYMAP_MATCH_INCOMPLETE)
- {
- return match;
- }
+ /* Load new data */
+ info->handle = hkl;
+ ActivateKeyboardLayout (hkl, 0);
+ GetKeyboardLayoutNameA (info->name);
- if (partial_match >= 0)
- {
- if (compose_buffer_len == 2)
- {
- dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, partial_match);
- *output_len = 0;
+ info->file = get_keyboard_layout_file (info->name);
- if (output_size >= 1)
- output[(*output_len)++] = dead_key->undead_gdk_keycode;
+ info->key_entries = g_array_new (FALSE, FALSE, sizeof (KeyEntry));
+ sizeof (GdkWin32KeymapKeyEntry));
- if (output_size >= 2)
- {
- gsize second_deadkey_i;
-
- /* Special case for "deadkey + deadkey = space-version-of-deadkey, space-version-of-deadkey"
combinations.
- * Normally the result is a sequence of 2 unichars, but we do not store this.
- * For "deadkey + nondeadkey = space-version-of-deadkey, nondeadkey", we can use compose_buffer
- * contents as-is, but space version of a dead key need to be looked up separately.
- */
- if (find_deadkey_by_keyval (options->dead_keys, compose_buffer[1], &second_deadkey_i))
- output[(*output_len)++] = g_array_index (options->dead_keys, GdkWin32KeyNode,
second_deadkey_i).undead_gdk_keycode;
- else
- output[(*output_len)++] = compose_buffer[1];
- }
+ info->reverse_lookup_table = g_hash_table_new (g_direct_hash,
+ g_direct_equal);
+ load_layout_dll (info->file, info);
+ init_vk_lookup_table (info);
}
-
- return GDK_WIN32_KEYMAP_MATCH_PARTIAL;
+ if (info->handle == current_layout)
+ keymap->active_layout = i;
}
+ if (changed)
+ ActivateKeyboardLayout (current_layout, 0);
- return GDK_WIN32_KEYMAP_MATCH_NONE;
+ keymap->current_serial = _gdk_keymap_serial;
}
guint8
_gdk_win32_keymap_get_rshift_scancode (GdkWin32Keymap *keymap)
{
- if (keymap != NULL &&
- keymap->layout_handles->len > 0)
- return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).scancode_rshift;
-
- return 0;
+ return MapVirtualKey (VK_RSHIFT, MAPVK_VK_TO_VSC);
}
void
@@ -1159,10 +636,11 @@ _gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap,
gboolean
_gdk_win32_keymap_has_altgr (GdkWin32Keymap *keymap)
{
- if (keymap != NULL &&
- keymap->layout_handles->len > 0)
- return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).has_altgr;
-
+ /* 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 <Ctrl + Alt> from
+ * <AltGr> on Windows. */
return FALSE;
}
@@ -1187,6 +665,26 @@ _gdk_win32_display_get_keymap (GdkDisplay *display)
return default_keymap;
}
+
+GdkModifierType
+_gdk_win32_keymap_get_mod_mask (GdkWin32Keymap *keymap)
+{
+ GdkWin32KeymapLayoutInfo *layout_info;
+ BYTE keystate[256] = {0};
+ BYTE mod_bits;
+
+ update_keymap (keymap);
+
+ layout_info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo,
+ keymap->active_layout);
+
+ GetKeyboardState (keystate);
+
+ mod_bits = keystate_to_modbits (keymap, layout_info, keystate);
+
+ return mod_bits_to_gdk_mod_mask (mod_bits);
+}
+
static PangoDirection
get_hkl_direction (HKL hkl)
{
@@ -1209,20 +707,20 @@ get_hkl_direction (HKL hkl)
static PangoDirection
gdk_win32_keymap_get_direction (GdkKeymap *gdk_keymap)
{
- HKL active_hkl;
GdkWin32Keymap *keymap;
+ HKL active_hkl;
+
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), PANGO_DIRECTION_LTR);
- if (gdk_keymap == NULL || gdk_keymap != gdk_keymap_get_default ())
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- else
- keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
- update_keymap (GDK_KEYMAP (keymap));
+ update_keymap (keymap);
if (keymap->layout_handles->len <= 0)
active_hkl = GetKeyboardLayout (0);
else
- active_hkl = g_array_index (keymap->layout_handles, HKL, keymap->active_layout);
+ active_hkl = g_array_index (keymap->layout_handles, HKL,
+ keymap->active_layout);
return get_hkl_direction (active_hkl);
}
@@ -1234,17 +732,17 @@ gdk_win32_keymap_have_bidi_layouts (GdkKeymap *gdk_keymap)
gboolean have_rtl = FALSE;
gboolean have_ltr = FALSE;
gint group;
+
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
- if (gdk_keymap == NULL || gdk_keymap != gdk_keymap_get_default ())
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- else
- keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
- update_keymap (GDK_KEYMAP (keymap));
+ update_keymap (keymap);
for (group = 0; group < keymap->layout_handles->len; group++)
{
- if (get_hkl_direction (g_array_index (keymap->layout_handles, HKL, group)) == PANGO_DIRECTION_RTL)
+ if (get_hkl_direction (g_array_index (keymap->layout_handles, HKL,
+ group)) == PANGO_DIRECTION_RTL)
have_rtl = TRUE;
else
have_ltr = TRUE;
@@ -1283,71 +781,78 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap *gdk_keymap,
GdkKeymapKey **keys,
gint *n_keys)
{
- GArray *retval;
+ GdkWin32Keymap *keymap;
+ GArray *retval;
+ BYTE keystate[256] = {0};
+ gint group;
- g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
g_return_val_if_fail (keys != NULL, FALSE);
g_return_val_if_fail (n_keys != NULL, FALSE);
g_return_val_if_fail (keyval != 0, FALSE);
retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
- /* Accept only the default keymap */
- if (gdk_keymap == NULL || gdk_keymap == gdk_keymap_get_default ())
- {
- gint vk;
- GdkWin32Keymap *keymap;
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
- if (gdk_keymap == NULL)
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- else
- keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ update_keymap (keymap);
- update_keymap (gdk_keymap);
+ for (group = 0; group < keymap->layout_handles->len; group++)
+ {
+ GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
+ GdkWin32KeymapLayoutInfo,
+ group);
+ gint entry_index = gdk_keysym_to_key_entry_index (info, keyval);
- for (vk = 0; vk < KEY_STATE_SIZE; vk++)
+ while (entry_index >= 0)
{
- gint group;
-
- for (group = 0; group < keymap->layout_handles->len; group++)
+ GdkWin32KeymapKeyEntry *entry = &g_array_index (info->key_entries,
+ GdkWin32KeymapKeyEntry,
+ entry_index);
+ BYTE base_modbits = entry->mod_bits;
+ BYTE extra_modbits;
+ GdkKeymapKey gdk_key = {0};
+
+ /* Add original key combination */
+ gdk_key.keycode = entry->vk;
+ gdk_key.level = modbits_to_level (keymap, info, entry->mod_bits);
+ gdk_key.group = group;
+
+ g_array_append_val (retval, gdk_key);
+
+ /* Add combinations with modifiers that do not affect the translation */
+ for (extra_modbits = 0;
+ extra_modbits <= info->max_modbit_value;
+ ++extra_modbits)
{
- GdkWin32KeyLevelState level;
-
- for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+ BYTE modbits;
+ guint sym;
+
+ /* We are only interested in masks which are orthogonal to the
+ * original mask. */
+ if ((extra_modbits | base_modbits) == base_modbits ||
+ (extra_modbits & base_modbits) != 0)
+ continue;
+
+ modbits = base_modbits | extra_modbits;
+
+ /* 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);
+ if (sym == keyval || sym == GDK_KEY_VoidSymbol)
{
- guint *keygroup;
-
- keygroup = &keymap->keysym_tab[(vk * keymap->layout_handles->len + group) *
GDK_WIN32_LEVEL_COUNT];
-
- if (keygroup[level] == keyval)
- {
- GdkKeymapKey key;
-
- key.keycode = vk;
- key.group = group;
- key.level = level;
- g_array_append_val (retval, key);
- }
+ gdk_key.keycode = entry->vk;
+ gdk_key.level = modbits_to_level (keymap, info, modbits);
+ gdk_key.group = group;
+ g_array_append_val (retval, gdk_key);
}
}
- }
- }
-#ifdef G_ENABLE_DEBUG
- if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
- {
- guint i;
-
- g_print ("gdk_keymap_get_entries_for_keyval: %#.04x (%s):",
- keyval, gdk_keyval_name (keyval));
- for (i = 0; i < retval->len; i++)
- {
- GdkKeymapKey *entry = (GdkKeymapKey *) retval->data + i;
- g_print (" %#.02x %d %d", entry->keycode, entry->group, entry->level);
+ entry_index = entry->next;
}
- g_print ("\n");
- }
-#endif
+ }
if (retval->len > 0)
{
@@ -1372,91 +877,69 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap *gdk_keymap,
guint **keyvals,
gint *n_entries)
{
+ GdkWin32Keymap *keymap;
GArray *key_array;
GArray *keyval_array;
gint group;
- GdkWin32Keymap *keymap;
+ BYTE keystate[256] = {0};
+ BYTE vk;
- g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
g_return_val_if_fail (n_entries != NULL, FALSE);
- if (hardware_keycode <= 0 ||
- hardware_keycode >= KEY_STATE_SIZE ||
- (keys == NULL && keyvals == NULL) ||
- (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ()))
- {
- /* Wrong keycode or NULL output arrays or wrong keymap */
- if (keys)
- *keys = NULL;
- if (keyvals)
- *keyvals = NULL;
-
- *n_entries = 0;
- return FALSE;
- }
-
- if (keys)
+ if (keys != NULL)
key_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
else
key_array = NULL;
- if (keyvals)
+ if (keyvals != NULL)
keyval_array = g_array_new (FALSE, FALSE, sizeof (guint));
else
keyval_array = NULL;
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- update_keymap (GDK_KEYMAP (keymap));
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ update_keymap (keymap);
+
+ vk = hardware_keycode;
for (group = 0; group < keymap->layout_handles->len; group++)
{
- GdkWin32KeyLevelState level;
+ GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
+ GdkWin32KeymapLayoutInfo,
+ group);
+ int level;
- for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+ for (level = 0; level <= info->max_level; ++level)
{
- if (key_array)
- {
- GdkKeymapKey key;
+ BYTE modbits = info->level_to_modbits[level];
+ BYTE consumed_modbits = 0;
+ GdkKeymapKey key = {0};
+ guint keyval;
- key.keycode = hardware_keycode;
- key.group = group;
- key.level = level;
- g_array_append_val (key_array, key);
- }
+ keyval = vk_and_mod_bits_to_gdk_keysym (keymap, info, vk, keystate, modbits, &consumed_modbits);
- if (keyval_array)
- {
- guint keyval = keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) *
GDK_WIN32_LEVEL_COUNT + level];
+ if (keyval == GDK_KEY_VoidSymbol || consumed_modbits != modbits)
+ continue;
- g_array_append_val (keyval_array, keyval);
- }
- }
- }
+ key.keycode = vk;
+ key.group = group;
+ key.level = level;
- *n_entries = group * GDK_WIN32_LEVEL_COUNT;
-
- if ((key_array && key_array->len > 0) ||
- (keyval_array && keyval_array->len > 0))
- {
- if (keys)
- *keys = (GdkKeymapKey*) key_array->data;
+ if (key_array)
+ g_array_append_val (key_array, key);
- if (keyvals)
- *keyvals = (guint*) keyval_array->data;
- }
- else
- {
- if (keys)
- *keys = NULL;
+ if (keyval_array)
+ g_array_append_val (keyval_array, keyval);
- if (keyvals)
- *keyvals = NULL;
+ ++(*n_entries);
+ }
}
+
+ if (keys != NULL)
+ *keys = (GdkKeymapKey*) g_array_free (key_array, FALSE);
- if (key_array)
- g_array_free (key_array, key_array->len > 0 ? FALSE : TRUE);
- if (keyval_array)
- g_array_free (keyval_array, keyval_array->len > 0 ? FALSE : TRUE);
+ if (keyvals != NULL)
+ *keyvals = (guint*) g_array_free (keyval_array, FALSE);
return *n_entries > 0;
}
@@ -1465,25 +948,28 @@ static guint
gdk_win32_keymap_lookup_key (GdkKeymap *gdk_keymap,
const GdkKeymapKey *key)
{
- guint sym;
- GdkWin32Keymap *keymap;
+ GdkWin32Keymap *keymap;
+ GdkWin32KeymapLayoutInfo *info;
- g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), 0);
+ BYTE keystate[256] = {0};
+ BYTE modbits;
+ guint sym;
+
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), 0);
g_return_val_if_fail (key != NULL, 0);
- /* Accept only the default keymap */
- if (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ())
- return 0;
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ update_keymap (keymap);
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- update_keymap (GDK_KEYMAP (keymap));
+ info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo, key->group);
- if (key->keycode >= KEY_STATE_SIZE ||
- key->group < 0 || key->group >= keymap->layout_handles->len ||
- key->level < 0 || key->level >= GDK_WIN32_LEVEL_COUNT)
+ if (key->group < 0 || key->group >= keymap->layout_handles->len)
return 0;
-
- sym = keymap->keysym_tab[(key->keycode * keymap->layout_handles->len + key->group) * GDK_WIN32_LEVEL_COUNT
+ key->level];
+ if (key->level < 0 || key->level > info->max_level)
+ return 0;
+
+ modbits = info->level_to_modbits[key->level];
+ sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, key->keycode, keystate, modbits, NULL);
if (sym == GDK_KEY_VoidSymbol)
return 0;
@@ -1501,171 +987,60 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap *gdk_keymap,
gint *level,
GdkModifierType *consumed_modifiers)
{
- GdkWin32Keymap *keymap;
- guint tmp_keyval;
- guint *keygroup;
- GdkWin32KeyLevelState shift_level;
- GdkModifierType modifiers = GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD2_MASK;
-
- g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
-
-#if 0
- GDK_NOTE (EVENTS, g_print ("gdk_keymap_translate_keyboard_state: keycode=%#x state=%#x group=%d\n",
- hardware_keycode, state, group));
-#endif
- if (keyval)
- *keyval = 0;
- if (effective_group)
- *effective_group = 0;
- if (level)
- *level = 0;
- if (consumed_modifiers)
- *consumed_modifiers = 0;
-
- /* Accept only the default keymap */
- if (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ())
- return FALSE;
-
- if (hardware_keycode >= KEY_STATE_SIZE)
- return FALSE;
-
- keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
- update_keymap (GDK_KEYMAP (keymap));
-
- if (group < 0 || group >= keymap->layout_handles->len)
- return FALSE;
-
- keygroup = &keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) *
GDK_WIN32_LEVEL_COUNT];
-
- if ((state & (GDK_SHIFT_MASK | GDK_LOCK_MASK)) == (GDK_SHIFT_MASK | GDK_LOCK_MASK))
- shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK;
- else if (state & GDK_SHIFT_MASK)
- shift_level = GDK_WIN32_LEVEL_SHIFT;
- else if (state & GDK_LOCK_MASK)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
- else
- shift_level = GDK_WIN32_LEVEL_NONE;
-
- if (state & GDK_MOD2_MASK)
- {
- if (shift_level == GDK_WIN32_LEVEL_NONE)
- shift_level = GDK_WIN32_LEVEL_ALTGR;
- else if (shift_level == GDK_WIN32_LEVEL_SHIFT)
- shift_level = GDK_WIN32_LEVEL_SHIFT_ALTGR;
- else if (shift_level == GDK_WIN32_LEVEL_CAPSLOCK)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK_ALTGR;
- else
- shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR;
- }
-
- /* Drop altgr, capslock and shift if there are no keysymbols on
- * the key for those.
- */
- if (keygroup[shift_level] == GDK_KEY_VoidSymbol)
- {
- switch (shift_level)
- {
- case GDK_WIN32_LEVEL_NONE:
- case GDK_WIN32_LEVEL_ALTGR:
- case GDK_WIN32_LEVEL_SHIFT:
- case GDK_WIN32_LEVEL_CAPSLOCK:
- if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_NONE;
- break;
- case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK:
- if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
- else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_SHIFT;
- else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_NONE;
- break;
- case GDK_WIN32_LEVEL_CAPSLOCK_ALTGR:
- if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_ALTGR;
- else if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
- else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_NONE;
- break;
- case GDK_WIN32_LEVEL_SHIFT_ALTGR:
- if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_ALTGR;
- else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_SHIFT;
- else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_NONE;
- break;
- case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR:
- if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK_ALTGR;
- else if (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_SHIFT_ALTGR;
- else if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_ALTGR;
- else if (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK;
- else if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
- else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_SHIFT;
- else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
- shift_level = GDK_WIN32_LEVEL_NONE;
- break;
- case GDK_WIN32_LEVEL_COUNT:
- g_assert_not_reached ();
- }
- }
-
- /* See whether the shift level actually mattered
- * to know what to put in consumed_modifiers
- */
- if ((keygroup[GDK_WIN32_LEVEL_SHIFT] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_SHIFT]) &&
- (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_ALTGR] == keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]) &&
- (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK]))
- modifiers &= ~GDK_SHIFT_MASK;
-
- if ((keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK]) &&
- (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_ALTGR] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR]) &&
- (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_SHIFT] == keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK]))
- modifiers &= ~GDK_LOCK_MASK;
-
- if ((keygroup[GDK_WIN32_LEVEL_ALTGR] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_ALTGR]) &&
- (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_SHIFT] == keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]) &&
- (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] == GDK_KEY_VoidSymbol ||
- keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR]))
- modifiers &= ~GDK_MOD2_MASK;
-
- tmp_keyval = keygroup[shift_level];
-
+ GdkWin32Keymap *keymap;
+ guint tmp_keyval;
+ gint tmp_effective_group;
+ gint tmp_level;
+ BYTE consumed_mod_bits;
+
+ GdkWin32KeymapLayoutInfo *layout_info;
+ guint vk;
+ BYTE mod_bits;
+ BYTE keystate[256] = {0};
+
+ g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
+
+ keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+ update_keymap (keymap);
+
+ layout_info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo,
+ keymap->active_layout);
+
+ vk = hardware_keycode;
+ mod_bits = gdk_mod_mask_to_mod_bits (state);
+
+ if (vk == VK_SHIFT || vk == VK_LSHIFT || vk == VK_RSHIFT)
+ mod_bits &= ~KBDSHIFT;
+ if (vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL)
+ mod_bits &= ~KBDCTRL;
+ if (vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU)
+ mod_bits &= ~KBDALT;
+ if (vk == VK_RMENU)
+ mod_bits &= ~KBDALTGR;
+
+ /* We need to query the existing keyboard state for NumLock, CapsLock etc. */
+ get_lock_state (keystate);
+
+ tmp_keyval = vk_and_mod_bits_to_gdk_keysym (keymap, layout_info, vk, keystate,
+ mod_bits, &consumed_mod_bits);
+ tmp_effective_group = group;
+ tmp_level = modbits_to_level (keymap, layout_info, consumed_mod_bits);
+
+ /* Determine consumed modifiers */
+
if (keyval)
*keyval = tmp_keyval;
-
if (effective_group)
- *effective_group = group;
-
+ *effective_group = tmp_effective_group;
if (level)
- *level = shift_level;
-
+ *level = tmp_level;
if (consumed_modifiers)
- *consumed_modifiers = modifiers;
-
-#if 0
- GDK_NOTE (EVENTS, g_print ("... group=%d level=%d cmods=%#x keyval=%s\n",
- group, shift_level, modifiers, gdk_keyval_name (tmp_keyval)));
-#endif
+ *consumed_modifiers = mod_bits_to_gdk_mod_mask (consumed_mod_bits);
return tmp_keyval != GDK_KEY_VoidSymbol;
}
+
static void
gdk_win32_keymap_add_virtual_modifiers (GdkKeymap *keymap,
GdkModifierType *state)
@@ -1683,7 +1058,7 @@ gdk_win32_keymap_map_virtual_modifiers (GdkKeymap *keymap,
static void
gdk_win32_keymap_class_init (GdkWin32KeymapClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkKeymapClass *keymap_class = GDK_KEYMAP_CLASS (klass);
object_class->finalize = gdk_win32_keymap_finalize;
diff --git a/gdk/win32/gdkkeys-win32.h b/gdk/win32/gdkkeys-win32.h
new file mode 100644
index 0000000000..b5c5a2802f
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32.h
@@ -0,0 +1,146 @@
+#include <glib.h>
+#include <Windows.h>
+
+/* For lookup table VK -> chars */
+typedef struct
+{
+ int table;
+ int index;
+} GdkWin32KeymapTableAndIndex;
+
+/* For reverse lookup char -> VKs */
+typedef struct
+{
+ BYTE mod_bits;
+ BYTE vk;
+
+ /* Index of next KeyEntry. -1 if there is no next entry. */
+ int next;
+} GdkWin32KeymapKeyEntry;
+
+typedef struct
+{
+ HKL handle;
+
+ /* Keyboard layout identifier */
+ char name[KL_NAMELENGTH];
+
+ /* Path of the layout DLL */
+ char *file;
+
+ /* Handle of the layout DLL */
+ HINSTANCE lib;
+
+ /* The actual conversion tables provided by the layout DLL.
+ *
+ * This is a pointer to a KBDTABLES structure. The exact definition
+ * of this structure depends on the kernel on which the executable
+ * run and can in general only be determined at runtime. That's why
+ * we have to use a generic gpointer instead of the actual type here.
+ *
+ * See comment on GdkWin32KeymapImpl below for more information. */
+ gpointer tables;
+
+ /* VK -> chars lookup table so we don't have to do a linear scan
+ * every time we look up a key. */
+ GdkWin32KeymapTableAndIndex vk_lookup_table[256];
+
+ /* List of entries for reverse (char ->VKs) lookup. */
+ GArray *key_entries;
+
+ /* Reverse lookup table (char -> VKs). Key: Unichar. Value: int.
+ * The value is used to index into the key_entries array. The key_entries
+ * array can contain multiple consecutive entries for a given char.
+ * The end of the list for the char is marked by a key entry that has
+ * mod_bits and vk set to 0xFF. */
+ GHashTable *reverse_lookup_table;
+
+ /* Map level to modbits */
+ BYTE level_to_modbits[256];
+
+ /* Max Number of levels */
+ BYTE max_level;
+
+ /* Maximum possible value of a modbits bitset. */
+ BYTE max_modbit_value;
+
+} GdkWin32KeymapLayoutInfo;
+
+/* Some keyboard driver constants
+ * Adapted from ReactOS's kbd.h:
+ * See https://github.com/reactos/reactos/blob/master/sdk/include/ndk/kbd.h
+ */
+
+/* Modifier bits */
+#define KBDBASE 0x00
+#define KBDSHIFT 0x01
+#define KBDCTRL 0x02
+#define KBDALT 0x04
+#define KBDKANA 0x08
+#define KBDROYA 0x10
+#define KBDLOYA 0x20
+#define KBDGRPSELTAP 0x80
+
+#define KBDALTGR (KBDCTRL| KBDALT)
+
+/* */
+#define SHFT_INVALID 0x0F
+
+/* Char table constants */
+#define WCH_NONE 0xF000
+#define WCH_DEAD 0xF001
+#define WCH_LGTR 0xF002
+
+/* Char table flags */
+#define CAPLOK 0x01
+#define SGCAPS 0x02
+#define CAPLOKALTGR 0x04
+#define KANALOK 0x08
+#define GRPSELTAP 0x80
+
+/* IMPORTANT:
+ *
+ * Keyboard layout DLLs are dependent on the host architecture.
+ *
+ * - 32 bit systems have just one 32 bit DLL in System32.
+ * - 64 bit systems contain two versions of each layout DLL: One in System32
+ * for 64-bit applications, and one in SysWOW64 for 32-bit applications.
+ *
+ * Here comes the tricky part:
+ *
+ * The 32-bit DLL in SysWOW64 is *not* identical to the DLL you would find
+ * on a true 32 bit system, because all the pointers there are declared with
+ * the attribute `__ptr64` (which means they are 64 bits wide, but only the
+ * lower 32 bits are used).
+ *
+ * This leads to the following problems:
+ *
+ * (1) GCC does not support `__ptr64`
+ * (2) When compiling the 32-bit library, we need two versions of the same code
+ * and decide at run-time which one to execute, because we can't know at
+ * compile time whether we will be running on a true 32-bit system, or on
+ * WOW64.
+ *
+ * To solve this problem, we generate code for both cases (see
+ * gdkkeys-win32-impl.c + gdkkeys-win32-impl-wow64.c) and encapsulate
+ * the resulting functions in a struct of type GdkWin32KeymapImpl,
+ * allowing us to select the correct implementation at runtime.
+ *
+ */
+
+typedef struct
+{
+ gboolean (*load_layout_dll) (const char *dll,
+ GdkWin32KeymapLayoutInfo *info);
+ void (*init_vk_lookup_table) (GdkWin32KeymapLayoutInfo *info);
+ BYTE (*keystate_to_modbits) (GdkWin32KeymapLayoutInfo *info,
+ const BYTE keystate[256]);
+ BYTE (*modbits_to_level) (GdkWin32KeymapLayoutInfo *info,
+ BYTE modbits);
+ WCHAR (*vk_to_char_fuzzy) (GdkWin32KeymapLayoutInfo *info,
+ const BYTE keystate[256],
+ BYTE extra_mod_bits,
+ BYTE *consumed_mod_bits,
+ gboolean *is_dead,
+ BYTE vk);
+} GdkWin32KeymapImpl;
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index c9c6a98249..6667df5f1e 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -451,6 +451,7 @@ 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,
HKL hkl);
+GdkModifierType _gdk_win32_keymap_get_mod_mask (GdkWin32Keymap *keymap);
GdkKeymap *_gdk_win32_display_get_keymap (GdkDisplay *display);
diff --git a/gdk/win32/makefile.msc b/gdk/win32/makefile.msc
index a2e125c297..ab6e30eda2 100644
--- a/gdk/win32/makefile.msc
+++ b/gdk/win32/makefile.msc
@@ -47,6 +47,8 @@ gdk_win32_OBJECTS = \
gdkglobals-win32.obj \
gdkinput.obj \
gdkkeys-win32.obj \
+ gdkkeys-win32-impl.obj \
+ gdkkeys-win32-impl-wow64.obj \
gdkmain-win32.obj \
gdkproperty-win32.obj \
gdkscreen-win32.obj \
diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build
index 3c40f894b5..ce35090aec 100644
--- a/gdk/win32/meson.build
+++ b/gdk/win32/meson.build
@@ -13,6 +13,8 @@ gdk_win32_sources = files(
'gdkglcontext-win32.c',
'gdkglobals-win32.c',
'gdkkeys-win32.c',
+ 'gdkkeys-win32-impl.c',
+ 'gdkkeys-win32-impl-wow64.c',
'gdkmain-win32.c',
'gdkmonitor-win32.c',
'gdkproperty-win32.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]