Fix keysym replace logic.
This commit is contained in:
207
src/client.c
207
src/client.c
@ -90,8 +90,10 @@ struct _Client {
|
|||||||
#endif /* HAVE_IBUS */
|
#endif /* HAVE_IBUS */
|
||||||
|
|
||||||
#ifdef HAVE_XTEST
|
#ifdef HAVE_XTEST
|
||||||
KeyCode modifier_keycodes[8];
|
guint modifier_keycodes[8];
|
||||||
XkbDescRec *xkb;
|
XkbDescRec *xkb;
|
||||||
|
GSList *replaced_keycodes;
|
||||||
|
gulong reset_replaced_keycodes_timeout_handler;
|
||||||
#endif /* HAVE_XTEST */
|
#endif /* HAVE_XTEST */
|
||||||
|
|
||||||
GSettings *settings;
|
GSettings *settings;
|
||||||
@ -246,13 +248,7 @@ client_finalize (GObject *object)
|
|||||||
{
|
{
|
||||||
Client *client = CLIENT(object);
|
Client *client = CLIENT(object);
|
||||||
|
|
||||||
if (client->keyboards) {
|
g_slist_free (client->keyboards);
|
||||||
GSList *next = client->keyboards->next;
|
|
||||||
/* client->keyboards is a ring; break it before free */
|
|
||||||
client->keyboards->next = NULL;
|
|
||||||
g_slist_free (next);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (client_parent_class)->finalize (object);
|
G_OBJECT_CLASS (client_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,6 +837,19 @@ on_xkl_state_changed (XklEngine *xklengine,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_XTEST
|
#ifdef HAVE_XTEST
|
||||||
|
struct _KeycodeReplacement {
|
||||||
|
guint keycode;
|
||||||
|
guint new_keysym;
|
||||||
|
guint old_keysym;
|
||||||
|
};
|
||||||
|
typedef struct _KeycodeReplacement KeycodeReplacement;
|
||||||
|
|
||||||
|
static void
|
||||||
|
keycode_replacement_free (KeycodeReplacement *replacement)
|
||||||
|
{
|
||||||
|
g_slice_free (KeycodeReplacement, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
/* The following functions for keyboard mapping change are direct
|
/* The following functions for keyboard mapping change are direct
|
||||||
translation of the code in Caribou (in libcaribou/xadapter.vala):
|
translation of the code in Caribou (in libcaribou/xadapter.vala):
|
||||||
|
|
||||||
@ -851,16 +860,26 @@ on_xkl_state_changed (XklEngine *xklengine,
|
|||||||
static guint
|
static guint
|
||||||
get_replaced_keycode (Client *client)
|
get_replaced_keycode (Client *client)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
guint keycode;
|
||||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
for (i = client->xkb->max_key_code; i >= client->xkb->min_key_code; --i)
|
for (keycode = client->xkb->max_key_code;
|
||||||
if (client->xkb->map->key_sym_map[i].kt_index[0] == XkbOneLevelIndex &&
|
keycode >= client->xkb->min_key_code;
|
||||||
XKeycodeToKeysym (xdisplay, i, 0) != 0)
|
--keycode) {
|
||||||
return i;
|
guint offset = client->xkb->map->key_sym_map[keycode].offset;
|
||||||
|
if (client->xkb->map->key_sym_map[keycode].kt_index[0] == XkbOneLevelIndex &&
|
||||||
|
client->xkb->map->syms[offset] != NoSymbol) {
|
||||||
|
GSList *head;
|
||||||
|
for (head = client->replaced_keycodes; head; head = head->next) {
|
||||||
|
KeycodeReplacement *replacement = head->data;
|
||||||
|
if (replacement->keycode == keycode)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (head == NULL)
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return XKeysymToKeycode (xdisplay, 0x0023); /* XK_numbersign */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace keysym assigned to KEYCODE to KEYSYM. Both args are used
|
/* Replace keysym assigned to KEYCODE to KEYSYM. Both args are used
|
||||||
@ -872,36 +891,32 @@ get_replaced_keycode (Client *client)
|
|||||||
specified KEYCODE and KEYSYM. */
|
specified KEYCODE and KEYSYM. */
|
||||||
static gboolean
|
static gboolean
|
||||||
replace_keycode (Client *client,
|
replace_keycode (Client *client,
|
||||||
guint *keycode,
|
guint keycode,
|
||||||
guint *keysym)
|
guint *keysym)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
GdkDisplay *display = gdk_display_get_default ();
|
||||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||||
guint offset;
|
guint offset;
|
||||||
XkbMapChangesRec changes;
|
XkbMapChangesRec changes;
|
||||||
guint replaced_keycode, replaced_keysym;
|
guint old_keysym;
|
||||||
|
|
||||||
g_assert (keycode != NULL);
|
g_return_val_if_fail (client->xkb->min_key_code <= keycode &&
|
||||||
g_assert (keysym != NULL && *keysym != 0);
|
client->xkb->max_key_code,
|
||||||
|
FALSE);
|
||||||
|
g_return_val_if_fail (keysym != NULL, FALSE);
|
||||||
|
|
||||||
replaced_keycode = get_replaced_keycode (client);
|
offset = client->xkb->map->key_sym_map[keycode].offset;
|
||||||
if (replaced_keycode == 0)
|
old_keysym = client->xkb->map->syms[offset];
|
||||||
return FALSE;
|
|
||||||
replaced_keysym = XKeycodeToKeysym (xdisplay, replaced_keycode, 0);
|
|
||||||
XSync (xdisplay, False);
|
|
||||||
|
|
||||||
offset = client->xkb->map->key_sym_map[replaced_keycode].offset;
|
|
||||||
client->xkb->map->syms[offset] = *keysym;
|
client->xkb->map->syms[offset] = *keysym;
|
||||||
|
|
||||||
changes.changed = XkbKeySymsMask;
|
changes.changed = XkbKeySymsMask;
|
||||||
changes.first_key_sym = replaced_keycode;
|
changes.first_key_sym = keycode;
|
||||||
changes.num_key_syms = 1;
|
changes.num_key_syms = 1;
|
||||||
|
|
||||||
XkbChangeMap (xdisplay, client->xkb, &changes);
|
XkbChangeMap (xdisplay, client->xkb, &changes);
|
||||||
XSync (xdisplay, False);
|
XSync (xdisplay, False);
|
||||||
|
|
||||||
*keycode = replaced_keycode;
|
*keysym = old_keysym;
|
||||||
*keysym = replaced_keysym;
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -936,7 +951,7 @@ get_keycode_from_gdk_keymap (Client *client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_fake_modifier_key_event (Client *client,
|
send_fake_modifier_key_event (Client *client,
|
||||||
EekModifierType modifiers,
|
EekModifierType modifiers,
|
||||||
gboolean is_pressed)
|
gboolean is_pressed)
|
||||||
{
|
{
|
||||||
@ -957,27 +972,71 @@ send_fake_modifier_key_event (Client *client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
reset_replaced_keycodes (gpointer data)
|
||||||
|
{
|
||||||
|
Client *client = data;
|
||||||
|
GSList *head;
|
||||||
|
|
||||||
|
for (head = client->replaced_keycodes; head; head = head->next) {
|
||||||
|
KeycodeReplacement *replacement = head->data;
|
||||||
|
guint keysym = replacement->old_keysym;
|
||||||
|
replace_keycode (client, replacement->keycode, &keysym);
|
||||||
|
}
|
||||||
|
g_slist_free_full (client->replaced_keycodes,
|
||||||
|
(GDestroyNotify) keycode_replacement_free);
|
||||||
|
client->replaced_keycodes = NULL;
|
||||||
|
client->reset_replaced_keycodes_timeout_handler = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_fake_key_event (Client *client,
|
send_fake_key_event (Client *client,
|
||||||
EekSymbol *symbol,
|
guint xkeysym,
|
||||||
guint keyboard_modifiers,
|
guint keyboard_modifiers,
|
||||||
gboolean is_pressed)
|
gboolean is_pressed)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
GdkDisplay *display = gdk_display_get_default ();
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||||
EekModifierType modifiers;
|
EekModifierType modifiers;
|
||||||
guint xkeysym;
|
guint keycode;
|
||||||
guint keycode, replaced_keysym = 0;
|
guint old_keysym = 0;
|
||||||
|
GSList *head;
|
||||||
|
|
||||||
xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
|
|
||||||
g_return_if_fail (xkeysym > 0);
|
g_return_if_fail (xkeysym > 0);
|
||||||
|
|
||||||
|
keycode = 0;
|
||||||
|
for (head = client->replaced_keycodes; head; head = head->next) {
|
||||||
|
KeycodeReplacement *replacement = head->data;
|
||||||
|
if (replacement->new_keysym == xkeysym) {
|
||||||
|
keycode = replacement->keycode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
modifiers = 0;
|
modifiers = 0;
|
||||||
if (!get_keycode_from_gdk_keymap (client, xkeysym, &keycode, &modifiers)) {
|
if (keycode == 0) {
|
||||||
keycode = 0;
|
if (!get_keycode_from_gdk_keymap (client, xkeysym, &keycode, &modifiers)) {
|
||||||
replaced_keysym = xkeysym;
|
KeycodeReplacement *replacement;
|
||||||
if (!replace_keycode (client, &keycode, &replaced_keysym)) {
|
|
||||||
g_warning ("failed to lookup X keysym %X", xkeysym);
|
keycode = get_replaced_keycode (client);
|
||||||
return;
|
if (keycode == 0) {
|
||||||
|
g_warning ("no available keycode to replace");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_keysym = xkeysym;
|
||||||
|
if (!replace_keycode (client, keycode, &old_keysym)) {
|
||||||
|
g_warning ("failed to lookup X keysym %X", xkeysym);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
replacement = g_slice_new0 (KeycodeReplacement);
|
||||||
|
replacement->keycode = keycode;
|
||||||
|
replacement->new_keysym = xkeysym;
|
||||||
|
replacement->old_keysym = old_keysym;
|
||||||
|
client->replaced_keycodes =
|
||||||
|
g_slist_prepend (client->replaced_keycodes, replacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,26 +1044,26 @@ send_fake_key_event (Client *client,
|
|||||||
keyboard_modifiers &= ~EEK_SHIFT_MASK;
|
keyboard_modifiers &= ~EEK_SHIFT_MASK;
|
||||||
keyboard_modifiers &= ~EEK_LOCK_MASK;
|
keyboard_modifiers &= ~EEK_LOCK_MASK;
|
||||||
/* FIXME: may need to remap ISO_Level3_Shift and NumLock */
|
/* FIXME: may need to remap ISO_Level3_Shift and NumLock */
|
||||||
//keyboard_modifiers &= ~EEK_MOD5_MASK;
|
#if 0
|
||||||
//keyboard_modifiers &= ~client->alt_gr_mask;
|
keyboard_modifiers &= ~EEK_MOD5_MASK;
|
||||||
//keyboard_modifiers &= ~client->num_lock_mask;
|
keyboard_modifiers &= ~client->alt_gr_mask;
|
||||||
|
keyboard_modifiers &= ~client->num_lock_mask;
|
||||||
|
#endif
|
||||||
|
|
||||||
modifiers |= keyboard_modifiers;
|
modifiers |= keyboard_modifiers;
|
||||||
|
|
||||||
send_fake_modifier_key_event (client, modifiers, is_pressed);
|
send_fake_modifier_key_event (client, modifiers, is_pressed);
|
||||||
XSync (GDK_DISPLAY_XDISPLAY (display), False);
|
XTestFakeKeyEvent (xdisplay, keycode, is_pressed, CurrentTime);
|
||||||
|
|
||||||
keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display), xkeysym);
|
if (client->reset_replaced_keycodes_timeout_handler == 0 &&
|
||||||
g_return_if_fail (keycode > 0);
|
old_keysym != 0) {
|
||||||
|
/* Queue a timer to restore the old keycode. Ugly, but
|
||||||
XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
|
* required due to races / asynchronous X delivery. Long-term
|
||||||
keycode,
|
* fix is to extend the X keymap here instead of replace
|
||||||
is_pressed,
|
* entries. */
|
||||||
CurrentTime);
|
client->reset_replaced_keycodes_timeout_handler =
|
||||||
XSync (GDK_DISPLAY_XDISPLAY (display), False);
|
g_timeout_add (500, reset_replaced_keycodes, client);
|
||||||
|
}
|
||||||
if (replaced_keysym)
|
|
||||||
replace_keycode (client, &keycode, &replaced_keysym);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1041,12 +1100,11 @@ send_fake_key_events (Client *client,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore special keys */
|
if (EEK_IS_KEYSYM(symbol)) {
|
||||||
if (!EEK_IS_KEYSYM(symbol))
|
guint xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
|
||||||
return;
|
send_fake_key_event (client, xkeysym, keyboard_modifiers, TRUE);
|
||||||
|
send_fake_key_event (client, xkeysym, keyboard_modifiers, FALSE);
|
||||||
send_fake_key_event (client, symbol, keyboard_modifiers, TRUE);
|
}
|
||||||
send_fake_key_event (client, symbol, keyboard_modifiers, FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1091,10 +1149,11 @@ static void
|
|||||||
update_modifier_keycodes (Client *client)
|
update_modifier_keycodes (Client *client)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
GdkDisplay *display = gdk_display_get_default ();
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||||
XModifierKeymap *mods;
|
XModifierKeymap *mods;
|
||||||
gint i, j;
|
gint i, j;
|
||||||
|
|
||||||
mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (display));
|
mods = XGetModifierMapping (xdisplay);
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
client->modifier_keycodes[i] = 0;
|
client->modifier_keycodes[i] = 0;
|
||||||
for (j = 0; j < mods->max_keypermod; j++) {
|
for (j = 0; j < mods->max_keypermod; j++) {
|
||||||
@ -1112,18 +1171,19 @@ gboolean
|
|||||||
client_enable_xtest (Client *client)
|
client_enable_xtest (Client *client)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
GdkDisplay *display = gdk_display_get_default ();
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||||
int opcode, event_base, error_base, major_version, minor_version;
|
int opcode, event_base, error_base, major_version, minor_version;
|
||||||
|
|
||||||
g_assert (display);
|
g_assert (display);
|
||||||
|
|
||||||
if (!XTestQueryExtension (GDK_DISPLAY_XDISPLAY (display),
|
if (!XTestQueryExtension (xdisplay,
|
||||||
&event_base, &error_base,
|
&event_base, &error_base,
|
||||||
&major_version, &minor_version)) {
|
&major_version, &minor_version)) {
|
||||||
g_warning ("XTest extension is not available");
|
g_warning ("XTest extension is not available");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (display),
|
if (!XkbQueryExtension (xdisplay,
|
||||||
&opcode, &event_base, &error_base,
|
&opcode, &event_base, &error_base,
|
||||||
&major_version, &minor_version)) {
|
&major_version, &minor_version)) {
|
||||||
g_warning ("Xkb extension is not available");
|
g_warning ("Xkb extension is not available");
|
||||||
@ -1131,9 +1191,7 @@ client_enable_xtest (Client *client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!client->xkb)
|
if (!client->xkb)
|
||||||
client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (display),
|
client->xkb = XkbGetMap (xdisplay, XkbKeySymsMask, XkbUseCoreKbd);
|
||||||
XkbKeySymsMask,
|
|
||||||
XkbUseCoreKbd);
|
|
||||||
g_assert (client->xkb);
|
g_assert (client->xkb);
|
||||||
|
|
||||||
update_modifier_keycodes (client);
|
update_modifier_keycodes (client);
|
||||||
@ -1152,5 +1210,10 @@ client_disable_xtest (Client *client)
|
|||||||
XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */
|
XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */
|
||||||
client->xkb = NULL;
|
client->xkb = NULL;
|
||||||
}
|
}
|
||||||
|
if (client->replaced_keycodes) {
|
||||||
|
g_slist_free_full (client->replaced_keycodes,
|
||||||
|
(GDestroyNotify) keycode_replacement_free);
|
||||||
|
client->replaced_keycodes = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* HAVE_XTEST */
|
#endif /* HAVE_XTEST */
|
||||||
|
|||||||
Reference in New Issue
Block a user