| /* 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 "gdkdisplay-win32.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) |
| |
| /* forward declarations */ |
| 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 |
| } |
| |
| static void |
| gdk_win32_keymap_constructed (GObject *object) |
| { |
| GdkWin32Keymap *keymap; |
| |
| keymap = GDK_WIN32_KEYMAP (object); |
| 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_FILE_NOT_FOUND) |
| { |
| return NULL; |
| } |
| else if (status != ERROR_SUCCESS) |
| { |
| g_warning("Could not open registry key '%s'. Error code: %d", |
| kbdKeyPath, (int)status); |
| return NULL; |
| } |
| |
| /* 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 fail_close_key; |
| } |
| |
| dir_len = GetSystemDirectoryA (0, 0); /* includes \0 */ |
| if (dir_len == 0) |
| { |
| g_warning("GetSystemDirectoryA failed. Error: %d", (int)GetLastError()); |
| goto fail_close_key; |
| } |
| |
| /* 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 fail_free_result; |
| |
| /* 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 fail_free_result; |
| |
| result[dir_len + file_name_len] = '\0'; |
| |
| RegCloseKey (hkey); |
| return result; |
| |
| fail_free_result: |
| g_free (result); |
| fail_close_key: |
| RegCloseKey (hkey); |
| 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; |
| GdkWin32Display *display = GDK_WIN32_DISPLAY (GDK_KEYMAP (keymap)->display); |
| |
| |
| if (keymap->current_serial == gdk_win32_display_get_keymap_serial (display) && |
| 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_win32_display_get_keymap_serial (display); |
| } |
| |
| 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; |
| } |
| |
| 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->constructed = gdk_win32_keymap_constructed; |
| 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; |
| } |