mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-14 12:41:07 +00:00
7d6257cb1e
For some users, GetKeyboardLayoutNameA() returns an alias instead of the fully resolved keyboard layout identifier. In that case, we have to query the registry to resolve the alias before we can look up the DLL path. See comments under https://gitlab.gnome.org/GNOME/gtk/-/issues/4610
1098 lines
33 KiB
C
1098 lines
33 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);
|
|
}
|
|
|
|
/*
|
|
* Return the keyboard layout according to the user's keyboard layout
|
|
* substitution preferences.
|
|
*
|
|
* The result is heap-allocated and should be freed with g_free().
|
|
*/
|
|
static char*
|
|
get_keyboard_layout_substituted_name (const char *layout_name)
|
|
{
|
|
HKEY hkey = 0;
|
|
DWORD var_type = REG_SZ;
|
|
char *result = NULL;
|
|
DWORD buf_len = 0;
|
|
LSTATUS status;
|
|
|
|
static const char *substitute_path = "Keyboard Layout\\Substitutes";
|
|
|
|
status = RegOpenKeyExA (HKEY_CURRENT_USER, substitute_path, 0,
|
|
KEY_QUERY_VALUE, &hkey);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
/* No substitute set for this value, not sure if this is a normal case */
|
|
g_warning("Could not open registry key '%s'. Error code: %d",
|
|
substitute_path, (int)status);
|
|
|
|
goto fail1;
|
|
}
|
|
|
|
status = RegQueryValueExA (hkey, layout_name, 0, &var_type, 0, &buf_len);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
g_debug("Could not query registry key '%s\\%s'. Error code: %d",
|
|
substitute_path, layout_name, (int)status);
|
|
goto fail2;
|
|
}
|
|
|
|
/* Allocate buffer */
|
|
result = (char*) g_malloc (buf_len);
|
|
|
|
/* Retrieve substitute name */
|
|
status = RegQueryValueExA (hkey, layout_name, 0, &var_type,
|
|
(LPBYTE) result, &buf_len);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
g_warning("Could not obtain registry value at key '%s\\%s'. "
|
|
"Error code: %d",
|
|
substitute_path, layout_name, (int)status);
|
|
goto fail3;
|
|
}
|
|
|
|
RegCloseKey (hkey);
|
|
return result;
|
|
|
|
fail3:
|
|
g_free (result);
|
|
fail2:
|
|
RegCloseKey (hkey);
|
|
fail1:
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
char *final_layout_name = NULL;
|
|
HKEY hkey = 0;
|
|
DWORD var_type = REG_SZ;
|
|
char *result = NULL;
|
|
DWORD file_name_len = 0;
|
|
int dir_len = 0;
|
|
int buf_len = 0;
|
|
LSTATUS status;
|
|
|
|
static const char prefix[] = "SYSTEM\\CurrentControlSet\\Control\\"
|
|
"Keyboard Layouts\\";
|
|
char kbdKeyPath[sizeof (prefix) + KL_NAMELENGTH];
|
|
|
|
/* The user may have a keyboard substitute configured */
|
|
final_layout_name = get_keyboard_layout_substituted_name (layout_name);
|
|
if (final_layout_name != NULL)
|
|
{
|
|
g_debug ("Substituting keyboard layout name from '%s' to '%s'",
|
|
layout_name, final_layout_name);
|
|
g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s",
|
|
prefix, final_layout_name);
|
|
g_free (final_layout_name);
|
|
final_layout_name = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_debug ("Could not get substitute keyboard layout name for '%s', "
|
|
"will use '%s' directly", layout_name, layout_name);
|
|
g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s",
|
|
prefix, layout_name);
|
|
}
|
|
|
|
status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0,
|
|
KEY_QUERY_VALUE, &hkey);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
g_warning("Could not open registry key '%s'. Error code: %d",
|
|
kbdKeyPath, (int)status);
|
|
goto fail1;
|
|
}
|
|
|
|
/* Get sizes */
|
|
status = RegQueryValueExA (hkey, "Layout File", 0, &var_type, 0,
|
|
&file_name_len);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
g_warning("Could not query registry key '%s\\Layout File'. Error code: %d",
|
|
kbdKeyPath, (int)status);
|
|
goto fail2;
|
|
}
|
|
|
|
dir_len = GetSystemDirectoryA (0, 0); /* includes \0 */
|
|
if (dir_len == 0)
|
|
{
|
|
g_warning("GetSystemDirectoryA failed. Error: %d", (int)GetLastError());
|
|
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 */
|
|
status = RegQueryValueExA (hkey, "Layout File", 0, &var_type,
|
|
(LPBYTE) &result[dir_len], &file_name_len);
|
|
if (status != 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) \
|
|
map (VK_CAPITAL, GDK_KEY_Caps_Lock)
|
|
|
|
|
|
#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;
|
|
|
|
if (info->reverse_lookup_table == NULL)
|
|
return -1;
|
|
|
|
/* 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);
|
|
|
|
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 (info->file != NULL && 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);
|
|
}
|
|
else
|
|
{
|
|
g_warning("Failed to load keyboard layout DLL for layout %s: %s",
|
|
info->name, info->file);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
/* Just a diagnostic message to inform the user why their keypresses aren't working.
|
|
* Shouldn't happen under normal circumstances. */
|
|
if (tmp_keyval == GDK_KEY_VoidSymbol && layout_info->tables == NULL)
|
|
g_warning("Failed to translate keypress (keycode: %u) for group %d (%s) because "
|
|
"we could not load the layout.",
|
|
hardware_keycode, group, layout_info->name);
|
|
|
|
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;
|
|
}
|