forked from AuroraMiddleware/gtk
e5a4b91997
Instead of performing keyboard layout substitution whenever we find a matching entry in the registry, first try to load the original layout and only attempt substitution when that fails. See #4724
1099 lines
33 KiB
C
1099 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;
|
|
}
|
|
|
|
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;
|
|
LSTATUS status;
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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 *result = _get_keyboard_layout_file (layout_name);
|
|
|
|
/* If we could not retrieve a path, it may be that we need to perform layout
|
|
* substitution
|
|
*/
|
|
if (result == NULL)
|
|
{
|
|
char *substituted = get_keyboard_layout_substituted_name (layout_name);
|
|
result = _get_keyboard_layout_file (substituted);
|
|
g_free (substituted);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|