forked from AuroraMiddleware/gtk
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. See https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4338
This commit is contained in:
parent
21d40fc038
commit
ea65abc7e2
@ -945,38 +945,71 @@ _gdk_win32_enable_hidpi (GdkWin32Display *display)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_gdk_win32_check_on_arm64 (GdkWin32Display *display)
|
||||
gboolean
|
||||
_gdk_win32_check_processor (GdkWin32ProcessorCheckType check_type)
|
||||
{
|
||||
static gsize checked = 0;
|
||||
static gboolean is_arm64 = FALSE;
|
||||
static gboolean is_wow64 = FALSE;
|
||||
|
||||
if (g_once_init_enter (&checked))
|
||||
{
|
||||
gboolean fallback_wow64_check = FALSE;
|
||||
HMODULE kernel32 = LoadLibraryW (L"kernel32.dll");
|
||||
|
||||
if (kernel32 != NULL)
|
||||
{
|
||||
display->cpu_funcs.isWow64Process2 =
|
||||
typedef BOOL (WINAPI *funcIsWow64Process2) (HANDLE, USHORT *, USHORT *);
|
||||
|
||||
funcIsWow64Process2 isWow64Process2 =
|
||||
(funcIsWow64Process2) GetProcAddress (kernel32, "IsWow64Process2");
|
||||
|
||||
if (display->cpu_funcs.isWow64Process2 != NULL)
|
||||
if (isWow64Process2 != NULL)
|
||||
{
|
||||
USHORT proc_cpu = 0;
|
||||
USHORT native_cpu = 0;
|
||||
|
||||
display->cpu_funcs.isWow64Process2 (GetCurrentProcess (),
|
||||
&proc_cpu,
|
||||
&native_cpu);
|
||||
isWow64Process2 (GetCurrentProcess (), &proc_cpu, &native_cpu);
|
||||
|
||||
if (native_cpu == IMAGE_FILE_MACHINE_ARM64)
|
||||
display->running_on_arm64 = TRUE;
|
||||
is_arm64 = TRUE;
|
||||
|
||||
if (proc_cpu != IMAGE_FILE_MACHINE_UNKNOWN)
|
||||
is_wow64 = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
fallback_wow64_check = TRUE;
|
||||
}
|
||||
|
||||
FreeLibrary (kernel32);
|
||||
}
|
||||
else
|
||||
{
|
||||
fallback_wow64_check = TRUE;
|
||||
}
|
||||
|
||||
if (fallback_wow64_check)
|
||||
IsWow64Process (GetCurrentProcess (), &is_wow64);
|
||||
|
||||
g_once_init_leave (&checked, 1);
|
||||
}
|
||||
|
||||
switch (check_type)
|
||||
{
|
||||
case GDK_WIN32_ARM64:
|
||||
return is_arm64;
|
||||
break;
|
||||
|
||||
case GDK_WIN32_WOW64:
|
||||
return is_wow64;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warning ("unknown CPU check type");
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -987,7 +1020,7 @@ gdk_win32_display_init (GdkWin32Display *display_win32)
|
||||
display_win32->monitors = G_LIST_MODEL (g_list_store_new (GDK_TYPE_MONITOR));
|
||||
|
||||
_gdk_win32_enable_hidpi (display_win32);
|
||||
_gdk_win32_check_on_arm64 (display_win32);
|
||||
display_win32->running_on_arm64 = _gdk_win32_check_processor (GDK_WIN32_ARM64);
|
||||
|
||||
/* if we have DPI awareness, set up fixed scale if set */
|
||||
if (display_win32->dpi_aware_type != PROCESS_DPI_UNAWARE &&
|
||||
|
@ -105,13 +105,6 @@ typedef enum {
|
||||
GDK_WIN32_TABLET_INPUT_API_WINPOINTER
|
||||
} GdkWin32TabletInputAPI;
|
||||
|
||||
/* Detect running architecture */
|
||||
typedef BOOL (WINAPI *funcIsWow64Process2) (HANDLE, USHORT *, USHORT *);
|
||||
typedef struct _GdkWin32KernelCPUFuncs
|
||||
{
|
||||
funcIsWow64Process2 isWow64Process2;
|
||||
} GdkWin32KernelCPUFuncs;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HDC hdc;
|
||||
@ -177,7 +170,6 @@ struct _GdkWin32Display
|
||||
|
||||
/* Running CPU items */
|
||||
guint running_on_arm64 : 1;
|
||||
GdkWin32KernelCPUFuncs cpu_funcs;
|
||||
};
|
||||
|
||||
struct _GdkWin32DisplayClass
|
||||
|
@ -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/.
|
||||
@ -644,11 +644,9 @@ build_key_event_state (BYTE *key_state)
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkWin32Keymap *keymap;
|
||||
keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
|
||||
|
||||
state = 0;
|
||||
|
||||
if (key_state[VK_SHIFT] & 0x80)
|
||||
state |= GDK_SHIFT_MASK;
|
||||
state = _gdk_win32_keymap_get_mod_mask (keymap);
|
||||
|
||||
if (key_state[VK_CAPITAL] & 0x01)
|
||||
state |= GDK_LOCK_MASK;
|
||||
@ -664,26 +662,6 @@ build_key_event_state (BYTE *key_state)
|
||||
if (key_state[VK_XBUTTON2] & 0x80)
|
||||
state |= GDK_BUTTON5_MASK;
|
||||
|
||||
keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
|
||||
|
||||
if (_gdk_win32_keymap_has_altgr (keymap) &&
|
||||
(key_state[VK_LCONTROL] & 0x80) &&
|
||||
(key_state[VK_RMENU] & 0x80))
|
||||
{
|
||||
state |= GDK_MOD2_MASK;
|
||||
if (key_state[VK_RCONTROL] & 0x80)
|
||||
state |= GDK_CONTROL_MASK;
|
||||
if (key_state[VK_LMENU] & 0x80)
|
||||
state |= GDK_ALT_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key_state[VK_CONTROL] & 0x80)
|
||||
state |= GDK_CONTROL_MASK;
|
||||
if (key_state[VK_MENU] & 0x80)
|
||||
state |= GDK_ALT_MASK;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
26
gdk/win32/gdkkeys-win32-impl-wow64.c
Normal file
26
gdk/win32/gdkkeys-win32-impl-wow64.c
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Philip Zander
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _WIN64
|
||||
#define GDK_WIN32_COMPILE_FOR_WOW64 1
|
||||
#include "gdkkeys-win32-impl.c"
|
||||
#endif
|
548
gdk/win32/gdkkeys-win32-impl.c
Normal file
548
gdk/win32/gdkkeys-win32-impl.c
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
/* Initialize with defaults */
|
||||
if (consumed_mod_bits)
|
||||
*consumed_mod_bits = 0;
|
||||
if (is_dead)
|
||||
*is_dead = FALSE;
|
||||
|
||||
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
|
||||
};
|
File diff suppressed because it is too large
Load Diff
168
gdk/win32/gdkkeys-win32.h
Normal file
168
gdk/win32/gdkkeys-win32.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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
|
||||
* See https://github.com/microsoft/windows-rs/blob/0.28.0/crates/deps/sys/src/Windows/Win32/UI/Input/KeyboardAndMouse/mod.rs
|
||||
*/
|
||||
|
||||
/* 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;
|
@ -356,7 +356,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);
|
||||
|
||||
@ -425,4 +425,12 @@ void _gdk_win32_surfaceing_init (void);
|
||||
void _gdk_drag_init (void);
|
||||
void _gdk_events_init (GdkDisplay *display);
|
||||
|
||||
typedef enum _GdkWin32ProcessorCheckType
|
||||
{
|
||||
GDK_WIN32_ARM64,
|
||||
GDK_WIN32_WOW64,
|
||||
} GdkWin32ProcessorCheckType;
|
||||
|
||||
gboolean _gdk_win32_check_processor (GdkWin32ProcessorCheckType check_type);
|
||||
|
||||
#endif /* __GDK_PRIVATE_WIN32_H__ */
|
||||
|
@ -19,6 +19,8 @@ gdk_win32_sources = files([
|
||||
'gdkhdataoutputstream-win32.c',
|
||||
'gdkinput-winpointer.c',
|
||||
'gdkkeys-win32.c',
|
||||
'gdkkeys-win32-impl.c',
|
||||
'gdkkeys-win32-impl-wow64.c',
|
||||
'gdkwin32langnotification.c',
|
||||
'gdkmain-win32.c',
|
||||
'gdkmonitor-win32.c',
|
||||
|
Loading…
Reference in New Issue
Block a user