diff --git a/src/client-main.c b/src/client-main.c index 7ee831a7..1bca60ad 100644 --- a/src/client-main.c +++ b/src/client-main.c @@ -179,6 +179,8 @@ main (int argc, char **argv) } client = eekboard_client_new (connection); + g_object_unref (connection); + if (client == NULL) { g_printerr ("Can't create a client\n"); exit (1); @@ -198,6 +200,7 @@ main (int argc, char **argv) else { g_printerr ("Unknown focus listener \"%s\". " "Try \"atspi\" or \"ibus\"\n", focus_listener); + g_object_unref (client); exit (1); } } @@ -212,22 +215,26 @@ main (int argc, char **argv) if (accessibility_enabled) { if (atspi_init () != 0) { g_printerr ("Can't init AT-SPI 2\n"); + g_object_unref (client); exit (1); } if (focus == FOCUS_ATSPI && !eekboard_client_enable_atspi_focus (client)) { g_printerr ("Can't register AT-SPI focus change event listeners\n"); + g_object_unref (client); exit (1); } if (opt_keystroke && !eekboard_client_enable_atspi_keystroke (client)) { g_printerr ("Can't register AT-SPI keystroke event listeners\n"); + g_object_unref (client); exit (1); } } else { g_printerr ("Desktop accessibility support is disabled\n"); + g_object_unref (client); exit (1); } } @@ -240,6 +247,7 @@ main (int argc, char **argv) if (focus == FOCUS_IBUS && !eekboard_client_enable_ibus_focus (client)) { g_printerr ("Can't register IBus focus change event listeners\n"); + g_object_unref (client); exit (1); } } @@ -247,24 +255,28 @@ main (int argc, char **argv) if (opt_use_system_layout && (opt_keyboard || opt_model || opt_layouts || opt_options)) { g_printerr ("Can't use --use-system-layout option with keyboard options\n"); + g_object_unref (client); exit (1); } - if (opt_use_system_layout) { - if (!eekboard_client_enable_xkl (client)) { - g_printerr ("Can't register xklavier event listeners\n"); - exit (1); - } - } else if (opt_model || opt_layouts || opt_options) { - if (!eekboard_client_set_xkl_config (client, - opt_model, - opt_layouts, - opt_options)) { - g_printerr ("Can't set xklavier config\n"); + if (!eekboard_client_enable_xkl (client)) { + g_printerr ("Can't register xklavier event listeners\n"); + g_object_unref (client); + exit (1); + } + + if (opt_use_system_layout || opt_model || opt_layouts || opt_options) { + if (!eekboard_client_load_keyboard_from_xkl (client, + opt_model, + opt_layouts, + opt_options)) { + g_printerr ("Can't load keyboard from xklavier config\n"); + g_object_unref (client); exit (1); } } else { gchar *file; + gboolean success; if (!opt_keyboard) opt_keyboard = DEFAULT_LAYOUT; @@ -273,17 +285,19 @@ main (int argc, char **argv) file = g_strdup (opt_keyboard); else file = g_strdup_printf ("%s/%s.xml", KEYBOARDDIR, opt_keyboard); - if (!eekboard_client_load_keyboard_from_file (client, file)) { + success = eekboard_client_load_keyboard_from_file (client, file); + g_free (file); + if (!success) { g_printerr ("Can't load keyboard file %s\n", file); - g_free (file); + g_object_unref (client); exit (1); } - g_free (file); } #ifdef HAVE_XTEST if (!eekboard_client_enable_xtest (client)) { g_printerr ("Can't init xtest\n"); + g_object_unref (client); exit (1); } #endif /* HAVE_XTEST */ @@ -310,6 +324,7 @@ main (int argc, char **argv) g_main_loop_run (loop); g_main_loop_unref (loop); + g_object_unref (client); return 0; } diff --git a/src/client.c b/src/client.c index af49ca0e..52dce88c 100644 --- a/src/client.c +++ b/src/client.c @@ -30,6 +30,7 @@ #ifdef HAVE_XTEST #include +#include #endif /* HAVE_XTEST */ #ifdef HAVE_IBUS @@ -67,6 +68,8 @@ struct _EekboardClient { GdkDisplay *display; XklEngine *xkl_engine; XklConfigRegistry *xkl_config_registry; + gboolean use_xkl_layout; + gint group; gulong xkl_config_changed_handler; gulong xkl_state_changed_handler; @@ -88,6 +91,9 @@ struct _EekboardClient { #ifdef HAVE_XTEST KeyCode modifier_keycodes[8]; + KeyCode reserved_keycode; + KeySym reserved_keysym; + XkbDescRec *xkb; #endif /* HAVE_XTEST */ GSettings *settings; @@ -121,14 +127,24 @@ static gboolean keystroke_listener_cb (const AtspiDeviceEvent *stroke, static gboolean set_keyboard (EekboardClient *client, gboolean show, EekLayout *layout); -static gboolean set_xkl_keyboard (EekboardClient *client, - gboolean show, - const gchar *model, - const gchar *layouts, - const gchar *options); +static gboolean set_keyboard_from_xkl (EekboardClient *client, + gboolean show, + const gchar *model, + const gchar *layouts, + const gchar *options); #ifdef HAVE_XTEST static void update_modifier_keycodes (EekboardClient *client); +static gboolean get_keycode_for_keysym + (EekboardClient *client, + guint keysym, + guint *keycode, + guint *modifiers); +static gboolean get_keycode_for_keysym_replace + (EekboardClient *client, + guint keysym, + guint *keycode, + guint *modifiers); #endif /* HAVE_XTEST */ static void @@ -311,23 +327,25 @@ eekboard_client_init (EekboardClient *client) } gboolean -eekboard_client_set_xkl_config (EekboardClient *client, - const gchar *model, - const gchar *layouts, - const gchar *options) +eekboard_client_load_keyboard_from_xkl (EekboardClient *client, + const gchar *model, + const gchar *layouts, + const gchar *options) { + client->use_xkl_layout = TRUE; + #if ENABLE_FOCUS_LISTENER - return set_xkl_keyboard (client, - !client->follows_focus, - model, - layouts, - options); + return set_keyboard_from_xkl (client, + !client->follows_focus, + model, + layouts, + options); #else /* ENABLE_FOCUS_LISTENER */ - return set_xkl_keyboard (client, - TRUE, - model, - layouts, - options); + return set_keyboard_from_xkl (client, + TRUE, + model, + layouts, + options); #endif /* !ENABLE_FOCUS_LISTENER */ } @@ -365,22 +383,18 @@ eekboard_client_enable_xkl (EekboardClient *client) (GdkFilterFunc) filter_xkl_event, client); + client->use_xkl_layout = FALSE; + xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE); -#if ENABLE_FOCUS_LISTENER - return set_xkl_keyboard (client, - !client->follows_focus, - NULL, - NULL, - NULL); -#else /* ENABLE_FOCUS_LISTENER */ - return set_xkl_keyboard (client, TRUE, NULL, NULL, NULL); -#endif /* !ENABLE_FOCUS_LISTENER */ + return TRUE; } void eekboard_client_disable_xkl (EekboardClient *client) { + client->use_xkl_layout = FALSE; + if (client->xkl_engine) xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE); if (g_signal_handler_is_connected (client->xkl_engine, @@ -665,8 +679,8 @@ EekboardClient * eekboard_client_new (GDBusConnection *connection) { EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT, - "connection", connection, - NULL); + "connection", connection, + NULL); if (client->context) return client; return NULL; @@ -691,12 +705,14 @@ on_xkl_config_changed (XklEngine *xklengine, EekboardClient *client = user_data; gboolean retval; - retval = set_xkl_keyboard (client, FALSE, NULL, NULL, NULL); - g_return_if_fail (retval); + if (client->use_xkl_layout) { + retval = set_keyboard_from_xkl (client, FALSE, NULL, NULL, NULL); + g_return_if_fail (retval); + } -#ifdef HAVE_FAKEKEY +#ifdef HAVE_XTEST update_modifier_keycodes (client); -#endif /* HAVE_FAKEKEY */ +#endif /* HAVE_XTEST */ } static gboolean @@ -726,11 +742,11 @@ set_keyboard (EekboardClient *client, } static gboolean -set_xkl_keyboard (EekboardClient *client, - gboolean show, - const gchar *model, - const gchar *layouts, - const gchar *options) +set_keyboard_from_xkl (EekboardClient *client, + gboolean show, + const gchar *model, + const gchar *layouts, + const gchar *options) { EekLayout *layout; gboolean retval; @@ -793,10 +809,13 @@ on_xkl_state_changed (XklEngine *xklengine, EekboardClient *client = user_data; if (type == GROUP_CHANGED && client->keyboard) { - gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard)); - if (group != value) { - eekboard_context_set_group (client->context, value, NULL); + if (client->use_xkl_layout) { + gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard)); + if (group != value) { + eekboard_context_set_group (client->context, value, NULL); + } } + client->group = value; } } @@ -841,13 +860,20 @@ send_fake_key_event (EekboardClient *client, xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol)); g_return_if_fail (xkeysym > 0); + if (!get_keycode_for_keysym (client, xkeysym, &keycode, &modifiers)) { + g_warning ("failed to lookup X keysym %X", xkeysym); + return; + } + + modifiers |= eek_keyboard_get_modifiers (client->keyboard); + + send_fake_modifier_key_event (client, modifiers, is_pressed); + XSync (GDK_DISPLAY_XDISPLAY (client->display), False); + keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (client->display), xkeysym); g_return_if_fail (keycode > 0); - modifiers = eek_keyboard_get_modifiers (client->keyboard); - send_fake_modifier_key_event (client, modifiers, is_pressed); - XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (client->display), keycode, is_pressed, @@ -862,6 +888,7 @@ on_key_pressed (EekKeyboard *keyboard, { EekboardClient *client = user_data; send_fake_key_event (client, key, TRUE); + send_fake_key_event (client, key, FALSE); } static void @@ -869,8 +896,6 @@ on_key_released (EekKeyboard *keyboard, EekKey *key, gpointer user_data) { - EekboardClient *client = user_data; - send_fake_key_event (client, key, FALSE); } static void @@ -892,10 +917,130 @@ update_modifier_keycodes (EekboardClient *client) } } +/* The following functions for keyboard mapping change are direct + translation of the code in Caribou (in libcaribou/xadapter.vala): + + - get_reserved_keycode + - reset_reserved + - get_keycode_for_keysym_replace (Caribou: replace_keycode) + - get_keycode_for_keysym_best (Caribou: best_keycode_keyval_match) + - get_keycode_for_keysym (Caribou: keycode_for_keyval) +*/ +static guint +get_reserved_keycode (EekboardClient *client) +{ + Display *display = GDK_DISPLAY_XDISPLAY (client->display); + gint i; + + for (i = client->xkb->max_key_code; i >= client->xkb->min_key_code; --i) { + if (client->xkb->map->key_sym_map[i].kt_index[0] == XkbOneLevelIndex) { + if (XKeycodeToKeysym (display, i, 0) != 0) { + gdk_error_trap_push (); + XGrabKey (display, i, 0, + gdk_x11_get_default_root_xwindow (), TRUE, + GrabModeSync, GrabModeSync); + XFlush (display); + XUngrabKey (display, i, 0, + gdk_x11_get_default_root_xwindow ()); + if (gdk_error_trap_pop () == 0) + return i; + } + } + } + + return XKeysymToKeycode (display, 0x0023); /* XK_numbersign */ +} + +static gboolean +reset_reserved (gpointer user_data) +{ + EekboardClient *client = user_data; + guint keycode, modifiers; + + get_keycode_for_keysym_replace (client, + client->reserved_keysym, + &keycode, + &modifiers); + return FALSE; +} + +static gboolean +get_keycode_for_keysym_replace (EekboardClient *client, + guint keysym, + guint *keycode, + guint *modifiers) +{ + Display *display = GDK_DISPLAY_XDISPLAY (client->display); + guint offset; + if (client->reserved_keycode == 0) { + client->reserved_keycode = get_reserved_keycode (client); + client->reserved_keysym = + XKeycodeToKeysym (display, + client->reserved_keycode, + 0); + } + XFlush (display); + + offset = client->xkb->map->key_sym_map[client->reserved_keycode].offset; + client->xkb->map->syms[offset] = keysym; + XkbSetMap (display, + XkbAllMapComponentsMask, + client->xkb); + XFlush (display); + + *keycode = client->reserved_keycode; + *modifiers = 0; + + if (keysym != client->reserved_keysym) + g_timeout_add (500, reset_reserved, client); + + return TRUE; +} + +static gboolean +get_keycode_for_keysym_best (EekboardClient *client, + guint keysym, + guint *keycode, + guint *modifiers) +{ + GdkKeymap *keymap = gdk_keymap_get_default (); + GdkKeymapKey *keys, *best_match; + gint n_keys, i; + + if (!gdk_keymap_get_entries_for_keyval (keymap, keysym, &keys, &n_keys)) + return FALSE; + + for (i = 0; i < n_keys; i++) + if (keys[i].group == client->group) + best_match = &keys[i]; + + *keycode = best_match->keycode; + *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0; + + g_free (keys); + + return TRUE; +} + +static gboolean +get_keycode_for_keysym (EekboardClient *client, + guint keysym, + guint *keycode, + guint *modifiers) +{ + if (get_keycode_for_keysym_best (client, keysym, keycode, modifiers)) + return TRUE; + + if (get_keycode_for_keysym_replace (client, keysym, keycode, modifiers)) + return TRUE; + + return FALSE; +} + gboolean eekboard_client_enable_xtest (EekboardClient *client) { - int event_base, error_base, major_version, minor_version; + int opcode, event_base, error_base, major_version, minor_version; if (!client->display) { client->display = gdk_display_get_default (); @@ -908,7 +1053,21 @@ eekboard_client_enable_xtest (EekboardClient *client) g_warning ("XTest extension is not available"); return FALSE; } - + + if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (client->display), + &opcode, &event_base, &error_base, + &major_version, &minor_version)) { + g_warning ("Xkb extension is not available"); + return FALSE; + } + + if (!client->xkb) + client->xkb = XkbGetKeyboard (GDK_DISPLAY_XDISPLAY (client->display), + XkbGBN_AllComponentsMask, + XkbUseCoreKbd); + g_assert (client->xkb); + + client->reserved_keycode = 0; update_modifier_keycodes (client); client->key_pressed_handler = @@ -924,6 +1083,11 @@ eekboard_client_enable_xtest (EekboardClient *client) void eekboard_client_disable_xtest (EekboardClient *client) { + if (client->xkb) { + XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */ + client->xkb = NULL; + } + if (g_signal_handler_is_connected (client->keyboard, client->key_pressed_handler)) g_signal_handler_disconnect (client->keyboard, diff --git a/src/client.h b/src/client.h index 4dd6c667..7d1a6a6d 100644 --- a/src/client.h +++ b/src/client.h @@ -31,36 +31,38 @@ G_BEGIN_DECLS typedef struct _EekboardClient EekboardClient; -EekboardClient * eekboard_client_new (GDBusConnection *connection); +EekboardClient * eekboard_client_new (GDBusConnection *connection); gboolean eekboard_client_load_keyboard_from_file - (EekboardClient *client, - const gchar *file); -gboolean eekboard_client_set_xkl_config (EekboardClient *client, - const gchar *model, - const gchar *layouts, - const gchar *options); + (EekboardClient *client, + const gchar *file); -gboolean eekboard_client_enable_xkl (EekboardClient *client); -void eekboard_client_disable_xkl (EekboardClient *client); +gboolean eekboard_client_load_keyboard_from_xkl + (EekboardClient *client, + const gchar *model, + const gchar *layouts, + const gchar *options); + +gboolean eekboard_client_enable_xkl (EekboardClient *client); +void eekboard_client_disable_xkl (EekboardClient *client); gboolean eekboard_client_enable_atspi_focus - (EekboardClient *client); + (EekboardClient *client); void eekboard_client_disable_atspi_focus - (EekboardClient *client); + (EekboardClient *client); gboolean eekboard_client_enable_atspi_keystroke - (EekboardClient *client); + (EekboardClient *client); void eekboard_client_disable_atspi_keystroke - (EekboardClient *client); + (EekboardClient *client); -gboolean eekboard_client_enable_xtest (EekboardClient *client); -void eekboard_client_disable_xtest (EekboardClient *client); +gboolean eekboard_client_enable_xtest (EekboardClient *client); +void eekboard_client_disable_xtest (EekboardClient *client); gboolean eekboard_client_enable_ibus_focus - (EekboardClient *client); + (EekboardClient *client); void eekboard_client_disable_ibus_focus - (EekboardClient *client); + (EekboardClient *client); G_END_DECLS #endif /* EEKBOARD_CLIENT_H */