 a40f9261e9
			
		
	
	a40f9261e9
	
	
	
		
			
			Some Windows keymaps have bogus mappings for the Ctrl modifier. !4423 attempted to fix this by ignoring the Ctrl layer, but that was not enough. We also need to ignore combinations of Ctrl with other modifiers, i.e. Ctrl + Shift. For example, Ctrl + Shift + 6 is mapped to the character 0x1E on a US keyboard (but it should be treated as Ctrl + ^). Basically, always ignore Ctrl unless it is used in conjunction with Alt, i.e. as part of AltGr. Related issue: #4667
		
			
				
	
	
		
			567 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			567 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021 Philip Zander
 | |
|  * Copyright (c) 2018 Microsoft
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in all
 | |
|  * copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /* 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
 | |
|  * See
 | |
|  *   https://github.com/microsoft/windows-rs/blob/0.28.0/crates/deps/sys/src/Windows/Win32/UI/Input/KeyboardAndMouse/mod.rs
 | |
|  *
 | |
|  * For more information on how these structures work, see also:
 | |
|  *   https://github.com/microsoft/Windows-driver-samples/tree/f0adcda012820b1cd44a8b3a1953baf478029738/input/layout
 | |
|  */
 | |
| 
 | |
| 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;
 | |
| 
 | |
|   if (tables == NULL)
 | |
|     return 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;
 | |
| 
 | |
|   if (tables == NULL)
 | |
|     return 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 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
 | |
|  * combination with Alt). Such modifiers will not be consumed.
 | |
|  *
 | |
|  * 'Best-fit' means 'consume as many modifiers as possibe'.
 | |
|  *
 | |
|  * 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:
 | |
|  *
 | |
|  * - 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 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,
 | |
|                   BYTE                      mod_bits,
 | |
|                   BYTE                      lock_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;
 | |
| 
 | |
|   /* Initialize with defaults */
 | |
|   if (consumed_mod_bits)
 | |
|     *consumed_mod_bits = 0;
 | |
|   if (is_dead)
 | |
|     *is_dead = FALSE;
 | |
| 
 | |
|   if (tables == NULL)
 | |
|     return 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)
 | |
|     {
 | |
|       gboolean have_sgcaps    = FALSE;
 | |
|       WCHAR    best_char      = WCH_NONE;
 | |
|       BYTE     best_modifiers = 0;
 | |
|       int      best_score     = -1;
 | |
|       gboolean best_is_dead   = FALSE;
 | |
|       int      level;
 | |
| 
 | |
|       /* 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). */
 | |
|           !(mod_bits & ~KBDSHIFT) &&
 | |
| 	  (lock_bits & CAPLOK))
 | |
|         mod_bits ^= KBDSHIFT;
 | |
| 
 | |
|       /* Key supporting combination of capslock + altgr */
 | |
|       if ((entry->Attributes & CAPLOKALTGR) &&
 | |
|           (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, the characters for active capslock are
 | |
|        * in the next entry. */
 | |
|       if ((entry->Attributes & SGCAPS) &&
 | |
|           (lock_bits & CAPLOK))
 | |
|         have_sgcaps = TRUE;
 | |
| 
 | |
|       /* I'm not totally sure how kanalok behaves, for now I assume that there
 | |
|        * aren't any special cases. */
 | |
|       if ((entry->Attributes & KANALOK) &&
 | |
|           (lock_bits & KANALOK))
 | |
|         mod_bits ^= 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 & ~mod_bits)
 | |
|             continue;
 | |
| 
 | |
|           /* Some keys have bogus mappings for the control key, e.g.  Ctrl +
 | |
|            * Backspace = Delete, Ctrl + [ = 0x1B or even  Ctrl + Shift + 6 =
 | |
|            * 0x1E on a US keyboard. So we have to ignore all cases of
 | |
|            * Ctrl that aren't part of AltGr.
 | |
|            */
 | |
|           if ((candidate_modbits & KBDCTRL) && !(candidate_modbits & KBDALT))
 | |
|             continue;
 | |
| 
 | |
|           c = entry->wch[level];
 | |
|           if (c == WCH_DEAD || have_sgcaps)
 | |
|             {
 | |
|               /* 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));
 | |
|               c = next_entry->wch[level];
 | |
|               candidate_is_dead = TRUE;
 | |
|             }
 | |
| 
 | |
|           if (c == WCH_DEAD || c == WCH_LGTR || c == WCH_NONE)
 | |
|             continue;
 | |
| 
 | |
|           score = POPCOUNT (candidate_modbits & mod_bits);
 | |
|           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). */
 | |
| 
 | |
|           /* 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. */
 | |
|           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
 | |
|   };
 |