/* 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;
G_OBJECT_CLASS (gdk_win32_keymap_parent_class)->constructed (object);
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 (0 x1000384, /* 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)
{
GdkSeat *seat;
GdkDevice *device;
ActivateKeyboardLayout (current_layout, 0 );
seat = gdk_display_get_default_seat (GDK_DISPLAY (display));
device = gdk_seat_get_keyboard (seat);
g_object_notify (G_OBJECT (device), "active-layout-index" );
g_object_notify (G_OBJECT (device), "layout-names" );
}
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)
if (keymap->active_layout != group)
{
GdkSeat *seat;
GdkDevice *device;
keymap->active_layout = group;
seat = gdk_display_get_default_seat (GDK_KEYMAP (keymap)->display);
device = gdk_seat_get_keyboard (seat);
g_object_notify (G_OBJECT (device), "active-layout-index" );
}
}
}
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 char **
gdk_win32_keymap_get_layout_names (GdkKeymap *gdk_keymap)
{
GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (gdk_keymap);
update_keymap (keymap);
if (keymap->layout_infos)
{
GStrvBuilder *names_builder;
char **layout_names;
int i;
names_builder = g_strv_builder_new ();
for (i = 0 ; i < keymap->layout_infos->len; ++i)
{
LCID lcid;
char *endptr;
GdkWin32KeymapLayoutInfo *info;
info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo, i);
lcid = strtoul (info->name, &endptr, 16 );
if (info->name != endptr)
{
wchar_t *locale_name_w;
int locale_size = GetLocaleInfoW (lcid, LOCALE_SLOCALIZEDDISPLAYNAME, NULL, 0 );
locale_name_w = g_new0 (wchar_t , locale_size);
if (GetLocaleInfoW (lcid, LOCALE_SLOCALIZEDDISPLAYNAME, locale_name_w, locale_size))
{
gchar *locale_name = g_utf16_to_utf8 (locale_name_w, -1 , NULL, NULL, NULL);
g_strv_builder_add (names_builder, locale_name);
}
g_free (locale_name_w);
}
}
layout_names = g_strv_builder_end (names_builder);
g_strv_builder_unref (names_builder);
return layout_names;
}
return NULL;
}
static int
gdk_win32_keymap_get_active_layout_index (GdkKeymap *gdk_keymap)
{
GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (gdk_keymap);
update_keymap (keymap);
if (keymap->layout_infos)
return (int ) keymap->active_layout;
return -1 ;
}
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;
keymap_class->get_active_layout_index = gdk_win32_keymap_get_active_layout_index;
keymap_class->get_layout_names = gdk_win32_keymap_get_layout_names;
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland