mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-14 20:51:07 +00:00
b0818f9535
Previously, we treated CapsLock and KanaLock as part of the global keyboard state, much like NumLock and ScrollLock, rather than using the supplied modifier mask. This was because GDK does not have a modifier mask for KanaLock, only for CapsLock, so it would not have been possible to properly support it. However, this approach ended up causing problems, with certain keyboard shortcuts not registering when capslock was active. This was first observed in Inkscape [0] and appears to affect shortcuts consisting of a single key (like 'a') with no additional modifiers (wheareas shortcuts like 'ctrl+a' work). So now we are using the supplied GDK_LOCK_MASK instead, and dropped support for KanaLock, which we probably don't need anyway (since regular text input should be handled by the IME input module -- the keymap is mainly for shortcuts and keybindings, where you don't really want KanaLock). [0] https://gitlab.com/inkscape/inkscape/-/issues/3082
1001 lines
30 KiB
C
1001 lines
30 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 2000 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. 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/.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include "gdkwin32keys.h"
|
|
|
|
#include "gdk.h"
|
|
|
|
#include "gdkprivate-win32.h"
|
|
#include "gdkdebug.h"
|
|
#include "gdkdisplayprivate.h"
|
|
#include "gdkkeysyms.h"
|
|
#include "gdkkeysprivate.h"
|
|
#include "gdkkeys-win32.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
|
|
#define GDK_MOD2_MASK (1 << 4)
|
|
|
|
/* GdkWin32Keymap */
|
|
|
|
struct _GdkWin32KeymapClass
|
|
{
|
|
GdkKeymapClass parent_class;
|
|
};
|
|
|
|
struct _GdkWin32Keymap
|
|
{
|
|
GdkKeymap parent_instance;
|
|
|
|
/* Array of HKL */
|
|
GArray *layout_handles;
|
|
|
|
/* 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;
|
|
|
|
static void update_keymap (GdkWin32Keymap *gdk_keymap);
|
|
static void clear_keyboard_layout_info (gpointer data);
|
|
|
|
static void
|
|
gdk_win32_keymap_init (GdkWin32Keymap *keymap)
|
|
{
|
|
/* 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;
|
|
|
|
keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl;
|
|
#ifndef _WIN64
|
|
if (_gdk_win32_check_processor (GDK_WIN32_WOW64))
|
|
keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl_wow64;
|
|
#endif
|
|
|
|
update_keymap (keymap);
|
|
}
|
|
|
|
static void
|
|
gdk_win32_keymap_finalize (GObject *object)
|
|
{
|
|
GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (object);
|
|
|
|
g_clear_pointer (&keymap->layout_handles, g_array_unref);
|
|
g_clear_pointer (&keymap->layout_infos, g_array_unref);
|
|
|
|
G_OBJECT_CLASS (gdk_win32_keymap_parent_class)->finalize (object);
|
|
}
|
|
|
|
/* Convenience wrapper functions */
|
|
|
|
static gboolean
|
|
load_layout_dll (GdkWin32Keymap *keymap,
|
|
const char *dll,
|
|
GdkWin32KeymapLayoutInfo *info)
|
|
{
|
|
return keymap->gdkwin32_keymap_impl->load_layout_dll (dll, info);
|
|
}
|
|
|
|
static void
|
|
init_vk_lookup_table (GdkWin32Keymap *keymap,
|
|
GdkWin32KeymapLayoutInfo *info)
|
|
{
|
|
keymap->gdkwin32_keymap_impl->init_vk_lookup_table (info);
|
|
}
|
|
|
|
static BYTE
|
|
keystate_to_modbits (GdkWin32Keymap *keymap,
|
|
GdkWin32KeymapLayoutInfo *info,
|
|
const BYTE keystate[256])
|
|
{
|
|
return keymap->gdkwin32_keymap_impl->keystate_to_modbits (info, keystate);
|
|
}
|
|
|
|
static BYTE
|
|
modbits_to_level (GdkWin32Keymap *keymap,
|
|
GdkWin32KeymapLayoutInfo *info,
|
|
BYTE modbits)
|
|
{
|
|
return keymap->gdkwin32_keymap_impl->modbits_to_level (info, modbits);
|
|
}
|
|
|
|
static WCHAR
|
|
vk_to_char_fuzzy (GdkWin32Keymap *keymap,
|
|
GdkWin32KeymapLayoutInfo *info,
|
|
BYTE mod_bits,
|
|
BYTE lock_bits,
|
|
BYTE *consumed_mod_bits,
|
|
gboolean *is_dead,
|
|
BYTE vk)
|
|
{
|
|
return keymap->gdkwin32_keymap_impl->vk_to_char_fuzzy (info, mod_bits, lock_bits,
|
|
consumed_mod_bits, is_dead, vk);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
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
|
|
clear_keyboard_layout_info (gpointer data)
|
|
{
|
|
GdkWin32KeymapLayoutInfo *layout_info = (GdkWin32KeymapLayoutInfo*) data;
|
|
|
|
g_free (layout_info->file);
|
|
|
|
if (layout_info->key_entries != NULL)
|
|
g_array_unref (layout_info->key_entries);
|
|
|
|
if (layout_info->reverse_lookup_table != NULL)
|
|
g_hash_table_destroy (layout_info->reverse_lookup_table);
|
|
|
|
if (layout_info->lib != NULL)
|
|
FreeLibrary (layout_info->lib);
|
|
|
|
memset (layout_info, 0, sizeof (GdkWin32KeymapLayoutInfo));
|
|
}
|
|
|
|
#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)
|
|
|
|
|
|
static guint
|
|
vk_and_mod_bits_to_gdk_keysym (GdkWin32Keymap *keymap,
|
|
GdkWin32KeymapLayoutInfo *info,
|
|
guint vk,
|
|
BYTE mod_bits,
|
|
BYTE lock_bits,
|
|
BYTE *consumed_mod_bits)
|
|
|
|
{
|
|
gboolean is_dead = FALSE;
|
|
gunichar c;
|
|
guint sym;
|
|
|
|
if (consumed_mod_bits)
|
|
*consumed_mod_bits = 0;
|
|
|
|
/* Handle special key: Tab */
|
|
if (vk == VK_TAB)
|
|
{
|
|
if (consumed_mod_bits)
|
|
*consumed_mod_bits = mod_bits & KBDSHIFT;
|
|
return (mod_bits & KBDSHIFT) ? GDK_KEY_ISO_Left_Tab : GDK_KEY_Tab;
|
|
}
|
|
|
|
/* Handle other special keys */
|
|
switch (vk)
|
|
{
|
|
#define MAP(a_vk, a_gdk) case a_vk: return a_gdk;
|
|
|
|
DEFINE_SPECIAL (MAP)
|
|
|
|
/* Non-bijective mappings: */
|
|
MAP (VK_SHIFT, GDK_KEY_Shift_L)
|
|
MAP (VK_CONTROL, GDK_KEY_Control_L)
|
|
MAP (VK_MENU, GDK_KEY_Alt_L)
|
|
MAP (VK_SNAPSHOT, GDK_KEY_Print)
|
|
|
|
#undef MAP
|
|
}
|
|
|
|
/* Handle regular keys (including dead keys) */
|
|
c = vk_to_char_fuzzy (keymap, info, mod_bits, lock_bits,
|
|
consumed_mod_bits, &is_dead, vk);
|
|
|
|
if (c == WCH_NONE)
|
|
return GDK_KEY_VoidSymbol;
|
|
|
|
sym = gdk_unicode_to_keyval (c);
|
|
|
|
if (is_dead)
|
|
{
|
|
switch (sym)
|
|
{
|
|
#define MAP(a_nondead, a_dead) case a_nondead: return a_dead;
|
|
DEFINE_DEAD (MAP)
|
|
#undef MAP
|
|
}
|
|
}
|
|
|
|
return sym;
|
|
}
|
|
|
|
static int
|
|
gdk_keysym_to_key_entry_index (GdkWin32KeymapLayoutInfo *info,
|
|
guint sym)
|
|
{
|
|
gunichar c;
|
|
gintptr index;
|
|
|
|
/* Special cases */
|
|
if (sym == GDK_KEY_Tab)
|
|
return VK_TAB;
|
|
if (sym == GDK_KEY_ISO_Left_Tab)
|
|
return 256;
|
|
|
|
/* Generic non-printable keys */
|
|
switch (sym)
|
|
{
|
|
#define MAP(a_vk, a_gdk) case a_gdk: return a_vk;
|
|
DEFINE_SPECIAL (MAP)
|
|
#undef MAP
|
|
}
|
|
|
|
/* Fix up dead keys */
|
|
#define MAP(a_nondead, a_dead) \
|
|
if (sym == a_dead) \
|
|
sym = a_nondead;
|
|
DEFINE_DEAD (MAP)
|
|
#undef MAP
|
|
|
|
/* Try converting to Unicode and back */
|
|
c = gdk_keyval_to_unicode (sym);
|
|
|
|
g_return_val_if_fail (info->reverse_lookup_table != NULL, -1);
|
|
|
|
index = -1;
|
|
if (g_hash_table_lookup_extended (info->reverse_lookup_table,
|
|
GINT_TO_POINTER (c),
|
|
NULL, (gpointer*) &index))
|
|
{
|
|
return index;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static GdkModifierType
|
|
mod_bits_to_gdk_mod_mask (BYTE mod_bits)
|
|
{
|
|
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_ALT_MASK;
|
|
return result;
|
|
}
|
|
|
|
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_ALT_MASK)
|
|
result |= KBDALT;
|
|
return result;
|
|
}
|
|
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static void
|
|
update_keymap (GdkWin32Keymap *keymap)
|
|
{
|
|
HKL current_layout;
|
|
BOOL changed = FALSE;
|
|
int n_layouts;
|
|
int i;
|
|
|
|
if (keymap->current_serial == _gdk_keymap_serial &&
|
|
keymap->layout_handles->len > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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));
|
|
|
|
g_array_set_size (keymap->layout_infos, n_layouts);
|
|
|
|
current_layout = GetKeyboardLayout (0);
|
|
|
|
for (i = 0; i < n_layouts; ++i)
|
|
{
|
|
GdkWin32KeymapLayoutInfo *info = &g_array_index(keymap->layout_infos,
|
|
GdkWin32KeymapLayoutInfo, i);
|
|
HKL hkl = g_array_index(keymap->layout_handles, HKL, i);
|
|
|
|
if (info->handle != hkl)
|
|
{
|
|
changed = TRUE;
|
|
|
|
/* Free old data */
|
|
clear_keyboard_layout_info (info);
|
|
|
|
/* Load new data */
|
|
info->handle = hkl;
|
|
ActivateKeyboardLayout (hkl, 0);
|
|
GetKeyboardLayoutNameA (info->name);
|
|
|
|
info->file = get_keyboard_layout_file (info->name);
|
|
|
|
if (load_layout_dll (keymap, info->file, info))
|
|
{
|
|
info->key_entries = g_array_new (FALSE, FALSE,
|
|
sizeof (GdkWin32KeymapKeyEntry));
|
|
|
|
info->reverse_lookup_table = g_hash_table_new (g_direct_hash,
|
|
g_direct_equal);
|
|
init_vk_lookup_table (keymap, info);
|
|
}
|
|
}
|
|
|
|
if (info->handle == current_layout)
|
|
keymap->active_layout = i;
|
|
}
|
|
|
|
if (changed)
|
|
ActivateKeyboardLayout (current_layout, 0);
|
|
|
|
keymap->current_serial = _gdk_keymap_serial;
|
|
}
|
|
|
|
guint8
|
|
_gdk_win32_keymap_get_rshift_scancode (GdkWin32Keymap *keymap)
|
|
{
|
|
return MapVirtualKey (VK_RSHIFT, MAPVK_VK_TO_VSC);
|
|
}
|
|
|
|
void
|
|
_gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap,
|
|
HKL hkl)
|
|
{
|
|
if (keymap != NULL &&
|
|
keymap->layout_handles->len > 0)
|
|
{
|
|
int group;
|
|
|
|
for (group = 0; group < keymap->layout_handles->len; group++)
|
|
if (g_array_index (keymap->layout_handles, HKL, group) == hkl)
|
|
keymap->active_layout = group;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
_gdk_win32_keymap_has_altgr (GdkWin32Keymap *keymap)
|
|
{
|
|
/* We just return FALSE, since it doesn't really matter because AltGr
|
|
* is the same as Ctrl + Alt. Hence, we will never get a GDK_MOD2_MASK,
|
|
* rather we will just get GDK_CONTROL_MASK | GDK_ALT_MASK. I don't
|
|
* think there is any clean way to distinguish <Ctrl + Alt> from
|
|
* <AltGr> on Windows. */
|
|
return FALSE;
|
|
}
|
|
|
|
guint8
|
|
_gdk_win32_keymap_get_active_group (GdkWin32Keymap *keymap)
|
|
{
|
|
if (keymap != NULL &&
|
|
keymap->layout_handles->len > 0)
|
|
return keymap->active_layout;
|
|
|
|
return 0;
|
|
}
|
|
|
|
GdkKeymap*
|
|
_gdk_win32_display_get_keymap (GdkDisplay *display)
|
|
{
|
|
g_return_val_if_fail (display == gdk_display_get_default (), NULL);
|
|
|
|
if (default_keymap == NULL)
|
|
default_keymap = g_object_new (gdk_win32_keymap_get_type (), NULL);
|
|
|
|
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)
|
|
{
|
|
switch (PRIMARYLANGID (LOWORD ((DWORD) (gintptr) hkl)))
|
|
{
|
|
case LANG_HEBREW:
|
|
case LANG_ARABIC:
|
|
#ifdef LANG_URDU
|
|
case LANG_URDU:
|
|
#endif
|
|
case LANG_FARSI:
|
|
/* Others? */
|
|
return PANGO_DIRECTION_RTL;
|
|
|
|
default:
|
|
return PANGO_DIRECTION_LTR;
|
|
}
|
|
}
|
|
|
|
static PangoDirection
|
|
gdk_win32_keymap_get_direction (GdkKeymap *gdk_keymap)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
HKL active_hkl;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), PANGO_DIRECTION_LTR);
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_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);
|
|
|
|
return get_hkl_direction (active_hkl);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_have_bidi_layouts (GdkKeymap *gdk_keymap)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
gboolean have_rtl = FALSE;
|
|
gboolean have_ltr = FALSE;
|
|
int group;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_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)
|
|
have_rtl = TRUE;
|
|
else
|
|
have_ltr = TRUE;
|
|
}
|
|
|
|
return have_ltr && have_rtl;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_get_caps_lock_state (GdkKeymap *keymap)
|
|
{
|
|
(void) keymap;
|
|
|
|
return ((GetKeyState (VK_CAPITAL) & 1) != 0);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_get_num_lock_state (GdkKeymap *keymap)
|
|
{
|
|
(void) keymap;
|
|
|
|
return ((GetKeyState (VK_NUMLOCK) & 1) != 0);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_get_scroll_lock_state (GdkKeymap *keymap)
|
|
{
|
|
(void) keymap;
|
|
|
|
return ((GetKeyState (VK_SCROLL) & 1) != 0);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_get_entries_for_keyval (GdkKeymap *gdk_keymap,
|
|
guint keyval,
|
|
GArray *retval)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
int group;
|
|
guint len = retval->len;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
|
|
g_return_val_if_fail (keyval != 0, FALSE);
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_keymap);
|
|
|
|
update_keymap (keymap);
|
|
|
|
for (group = 0; group < keymap->layout_handles->len; group++)
|
|
{
|
|
GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
|
|
GdkWin32KeymapLayoutInfo,
|
|
group);
|
|
int entry_index = gdk_keysym_to_key_entry_index (info, keyval);
|
|
|
|
while (entry_index >= 0)
|
|
{
|
|
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)
|
|
{
|
|
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,
|
|
modbits, 0, NULL);
|
|
if (sym == keyval || sym == GDK_KEY_VoidSymbol)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
entry_index = entry->next;
|
|
}
|
|
}
|
|
|
|
return retval->len > len;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_get_entries_for_keycode (GdkKeymap *gdk_keymap,
|
|
guint hardware_keycode,
|
|
GdkKeymapKey **keys,
|
|
guint **keyvals,
|
|
int *n_entries)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
GArray *key_array;
|
|
GArray *keyval_array;
|
|
int group;
|
|
BYTE vk;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
|
|
g_return_val_if_fail (n_entries != NULL, FALSE);
|
|
|
|
*n_entries = 0;
|
|
|
|
if (keys != NULL)
|
|
key_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
|
|
else
|
|
key_array = NULL;
|
|
|
|
if (keyvals != NULL)
|
|
keyval_array = g_array_new (FALSE, FALSE, sizeof (guint));
|
|
else
|
|
keyval_array = NULL;
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_keymap);
|
|
update_keymap (keymap);
|
|
|
|
vk = hardware_keycode;
|
|
|
|
for (group = 0; group < keymap->layout_handles->len; group++)
|
|
{
|
|
GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
|
|
GdkWin32KeymapLayoutInfo,
|
|
group);
|
|
int level;
|
|
|
|
for (level = 0; level <= info->max_level; ++level)
|
|
{
|
|
BYTE modbits = info->level_to_modbits[level];
|
|
BYTE consumed_modbits = 0;
|
|
GdkKeymapKey key = {0};
|
|
guint keyval;
|
|
|
|
keyval = vk_and_mod_bits_to_gdk_keysym (keymap, info, vk, modbits, 0, &consumed_modbits);
|
|
|
|
if (keyval == GDK_KEY_VoidSymbol || consumed_modbits != modbits)
|
|
continue;
|
|
|
|
key.keycode = vk;
|
|
key.group = group;
|
|
key.level = level;
|
|
|
|
if (key_array)
|
|
g_array_append_val (key_array, key);
|
|
|
|
if (keyval_array)
|
|
g_array_append_val (keyval_array, keyval);
|
|
|
|
++(*n_entries);
|
|
}
|
|
}
|
|
|
|
if (keys != NULL)
|
|
*keys = (GdkKeymapKey*) g_array_free (key_array, FALSE);
|
|
|
|
if (keyvals != NULL)
|
|
*keyvals = (guint*) g_array_free (keyval_array, FALSE);
|
|
|
|
return *n_entries > 0;
|
|
}
|
|
|
|
static guint
|
|
gdk_win32_keymap_lookup_key (GdkKeymap *gdk_keymap,
|
|
const GdkKeymapKey *key)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
GdkWin32KeymapLayoutInfo *info;
|
|
|
|
BYTE modbits;
|
|
guint sym;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), 0);
|
|
g_return_val_if_fail (key != NULL, 0);
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_keymap);
|
|
update_keymap (keymap);
|
|
|
|
info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo, key->group);
|
|
|
|
if (key->group < 0 || key->group >= keymap->layout_handles->len)
|
|
return 0;
|
|
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, modbits, 0, NULL);
|
|
|
|
if (sym == GDK_KEY_VoidSymbol)
|
|
return 0;
|
|
else
|
|
return sym;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_keymap_translate_keyboard_state (GdkKeymap *gdk_keymap,
|
|
guint hardware_keycode,
|
|
GdkModifierType state,
|
|
int group,
|
|
guint *keyval,
|
|
int *effective_group,
|
|
int *level,
|
|
GdkModifierType *consumed_modifiers)
|
|
{
|
|
GdkWin32Keymap *keymap;
|
|
guint tmp_keyval;
|
|
int tmp_effective_group;
|
|
int tmp_level;
|
|
BYTE consumed_mod_bits;
|
|
|
|
GdkWin32KeymapLayoutInfo *layout_info;
|
|
guint vk;
|
|
BYTE mod_bits;
|
|
BYTE lock_bits = 0;
|
|
|
|
g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
|
|
|
|
keymap = GDK_WIN32_KEYMAP (gdk_keymap);
|
|
update_keymap (keymap);
|
|
|
|
g_return_val_if_fail (group >= 0 && group < keymap->layout_infos->len, FALSE);
|
|
|
|
layout_info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo,
|
|
group);
|
|
|
|
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;
|
|
|
|
/* Translate lock state
|
|
*
|
|
* Right now the only locking modifier is CAPSLOCK. We don't handle KANALOK
|
|
* because GDK doesn't have an equivalent modifier mask to my knowledge (On
|
|
* X11, I believe the same effect is achieved by shifting to a different
|
|
* group. It's just a different concept, that doesn't translate to Windows).
|
|
* But since KANALOK is only used on far-eastern keyboards, which require IME
|
|
* anyway, this is probably fine. The IME input module has actually been the
|
|
* default for all languages (not just far-eastern) for a while now, which
|
|
* means that the keymap is now only used for things like accelerators and
|
|
* keybindings, where you probably don't even want KANALOK to affect the
|
|
* translation.
|
|
*/
|
|
|
|
if (state & GDK_LOCK_MASK)
|
|
lock_bits |= CAPLOK;
|
|
|
|
tmp_keyval = vk_and_mod_bits_to_gdk_keysym (keymap, layout_info, vk, mod_bits,
|
|
lock_bits, &consumed_mod_bits);
|
|
tmp_effective_group = group;
|
|
tmp_level = modbits_to_level (keymap, layout_info, consumed_mod_bits);
|
|
|
|
/* Determine consumed modifiers */
|
|
|
|
if (keyval)
|
|
*keyval = tmp_keyval;
|
|
if (effective_group)
|
|
*effective_group = tmp_effective_group;
|
|
if (level)
|
|
*level = tmp_level;
|
|
if (consumed_modifiers)
|
|
*consumed_modifiers = mod_bits_to_gdk_mod_mask (consumed_mod_bits);
|
|
|
|
return tmp_keyval != GDK_KEY_VoidSymbol;
|
|
}
|
|
|
|
static void
|
|
gdk_win32_keymap_class_init (GdkWin32KeymapClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GdkKeymapClass *keymap_class = GDK_KEYMAP_CLASS (klass);
|
|
|
|
object_class->finalize = gdk_win32_keymap_finalize;
|
|
|
|
keymap_class->get_direction = gdk_win32_keymap_get_direction;
|
|
keymap_class->have_bidi_layouts = gdk_win32_keymap_have_bidi_layouts;
|
|
keymap_class->get_caps_lock_state = gdk_win32_keymap_get_caps_lock_state;
|
|
keymap_class->get_num_lock_state = gdk_win32_keymap_get_num_lock_state;
|
|
keymap_class->get_scroll_lock_state = gdk_win32_keymap_get_scroll_lock_state;
|
|
keymap_class->get_entries_for_keyval = gdk_win32_keymap_get_entries_for_keyval;
|
|
keymap_class->get_entries_for_keycode = gdk_win32_keymap_get_entries_for_keycode;
|
|
keymap_class->lookup_key = gdk_win32_keymap_lookup_key;
|
|
keymap_class->translate_keyboard_state = gdk_win32_keymap_translate_keyboard_state;
|
|
}
|