Learning_GTK4_tree/gdk/win32/gdkkeys-win32.c

1086 lines
33 KiB
C
Raw Normal View History

2023-12-12 11:36:42 +01:00
/* 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 "gdkdebugprivate.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;
}
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;
}