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
This commit is contained in:
Philip Zander 2020-07-28 20:22:30 +02:00
parent 068df4874a
commit 4039153ca7
9 changed files with 1345 additions and 1318 deletions

View File

@ -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 \

View File

@ -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

View File

@ -0,0 +1,4 @@
#ifndef _WIN64
#define GDK_WIN32_COMPILE_FOR_WOW64 1
#include "gdkkeys-win32-impl.c"
#endif

View File

@ -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
};

File diff suppressed because it is too large Load Diff

146
gdk/win32/gdkkeys-win32.h Normal file
View File

@ -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;

View File

@ -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);

View File

@ -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 \

View File

@ -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',