/* * Copyright (C) 2010-2011 Daiki Ueno * Copyright (C) 2010-2011 Red Hat, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #ifdef HAVE_ATSPI #include #include #endif /* HAVE_ATSPI */ #include #ifdef HAVE_XTEST #include #include #endif /* HAVE_XTEST */ #include "eek/eek.h" #include "eek/eek-xkl.h" #include "eekboard/eekboard-client.h" #include "eekboard/eekboard-xklutil.h" #include "client.h" #include #define CSW 640 #define CSH 480 #define DEFAULT_KEYBOARD "us" static gchar *default_keyboards[2] = { DEFAULT_KEYBOARD, NULL }; #define IBUS_INTERFACE_PANEL "org.freedesktop.IBus.Panel" enum { PROP_0, PROP_CONNECTION, PROP_EEKBOARD, PROP_CONTEXT, PROP_KEYBOARDS, PROP_LAST }; typedef struct _ClientClass ClientClass; struct _Client { GObject parent; EekboardClient *eekboard; EekboardContext *context; GSList *keyboards; GSList *keyboards_head; XklEngine *xkl_engine; XklConfigRegistry *xkl_config_registry; gulong xkl_config_changed_handler; gulong xkl_state_changed_handler; gulong key_activated_handler; gboolean follows_focus; guint hide_keyboard_timeout_id; GDBusConnection *ibus_connection; guint ibus_focus_message_filter; #ifdef HAVE_ATSPI AtspiAccessible *acc; AtspiDeviceListener *keystroke_listener; #endif /* HAVE_ATSPI */ guint modifier_keycodes[8]; XkbDescRec *xkb; GSettings *settings; }; struct _ClientClass { GObjectClass parent_class; }; G_DEFINE_TYPE (SeatEmitter, client, G_TYPE_OBJECT); #if ENABLE_FOCUS_LISTENER #define IS_KEYBOARD_VISIBLE(client) (!client->follows_focus) #else /* ENABLE_FOCUS_LISTENER */ #define IS_KEYBOARD_VISIBLE(client) TRUE #endif /* !ENABLE_FOCUS_LISTENER */ static GdkFilterReturn filter_xkl_event (GdkXEvent *xev, GdkEvent *event, gpointer user_data); static void on_xkl_config_changed (XklEngine *xklengine, gpointer user_data); static void on_xkl_state_changed (XklEngine *xklengine, XklEngineStateChange type, gint value, gboolean restore, gpointer user_data); #ifdef HAVE_ATSPI static void focus_listener_cb (const AtspiEvent *event, void *user_data); static gboolean keystroke_listener_cb (const AtspiDeviceEvent *stroke, void *user_data); #endif /* HAVE_ATSPI */ static gboolean set_keyboards (SeatEmitter *client, const gchar * const *keyboard); static gboolean set_keyboards_from_xkl (SeatEmitter *client); #ifdef HAVE_XTEST static void update_modifier_keycodes (Client *client); #endif /* HAVE_XTEST */ static void client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SeatEmitter *client = CLIENT(object); GDBusConnection *connection; gchar **keyboards; switch (prop_id) { case PROP_CONNECTION: connection = g_value_get_object (value); client->eekboard = eekboard_client_new (connection, NULL); if (client->eekboard != NULL) { client->context = eekboard_client_create_context (client->eekboard, "eekboard", NULL); if (client->context == NULL) { g_object_unref (client->eekboard); client->eekboard = NULL; } else { eekboard_client_push_context (client->eekboard, client->context, NULL); g_settings_bind (client->settings, "keyboards", client, "keyboards", G_SETTINGS_BIND_GET); } } break; case PROP_KEYBOARDS: keyboards = g_value_get_boxed (value); if (g_strv_length (keyboards) == 0) keyboards = default_keyboards; client_set_keyboards (client, (const gchar * const *)keyboards); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SeatEmitter *client = CLIENT(object); switch (prop_id) { case PROP_EEKBOARD: g_value_set_object (value, client->eekboard); break; case PROP_CONTEXT: g_value_set_object (value, client->context); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void client_dispose (GObject *object) { SeatEmitter *client = CLIENT(object); client_disable_xkl (client); #ifdef HAVE_ATSPI client_disable_atspi_focus (client); client_disable_atspi_keystroke (client); #endif /* HAVE_ATSPI */ client_disable_ibus_focus (client); if (client->ibus_connection) { g_object_unref (client->ibus_connection); client->ibus_connection = NULL; } #ifdef HAVE_XTEST client_disable_xtest (client); #endif /* HAVE_XTEST */ if (client->context) { g_object_unref (client->context); client->context = NULL; } if (client->eekboard) { g_object_unref (client->eekboard); client->eekboard = NULL; } if (client->settings) { g_object_unref (client->settings); client->settings = NULL; } G_OBJECT_CLASS (client_parent_class)->dispose (object); } static void client_finalize (GObject *object) { SeatEmitter *client = CLIENT(object); g_slist_free (client->keyboards); G_OBJECT_CLASS (client_parent_class)->finalize (object); } static void client_class_init (ClientClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = client_set_property; gobject_class->get_property = client_get_property; gobject_class->dispose = client_dispose; gobject_class->finalize = client_finalize; pspec = g_param_spec_object ("connection", "Connection", "Connection", G_TYPE_DBUS_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE); g_object_class_install_property (gobject_class, PROP_CONNECTION, pspec); pspec = g_param_spec_object ("eekboard", "Eekboard", "Eekboard", EEKBOARD_TYPE_CLIENT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_EEKBOARD, pspec); pspec = g_param_spec_object ("context", "Context", "Context", EEKBOARD_TYPE_CONTEXT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CONTEXT, pspec); pspec = g_param_spec_boxed ("keyboards", "Keyboards", "Keyboards", G_TYPE_STRV, G_PARAM_WRITABLE); g_object_class_install_property (gobject_class, PROP_KEYBOARDS, pspec); } static void client_init (SeatEmitter *client) { client->settings = g_settings_new ("org.fedorahosted.eekboard"); } gboolean client_set_keyboards (SeatEmitter *client, const gchar * const *keyboards) { gboolean retval; retval = set_keyboards (client, keyboards); if (retval && IS_KEYBOARD_VISIBLE (client)) eekboard_client_show_keyboard (client->eekboard, NULL); return retval; } gboolean client_enable_xkl (SeatEmitter *client) { GdkDisplay *display = gdk_display_get_default (); gboolean retval; g_assert (display); if (!client->xkl_engine) { client->xkl_engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (display)); } g_assert (client->xkl_engine); if (!client->xkl_config_registry) { client->xkl_config_registry = xkl_config_registry_get_instance (client->xkl_engine); xkl_config_registry_load (client->xkl_config_registry, FALSE); } client->xkl_config_changed_handler = g_signal_connect (client->xkl_engine, "X-config-changed", G_CALLBACK(on_xkl_config_changed), client); client->xkl_state_changed_handler = g_signal_connect (client->xkl_engine, "X-state-changed", G_CALLBACK(on_xkl_state_changed), client); gdk_window_add_filter (NULL, (GdkFilterFunc) filter_xkl_event, client); gdk_window_add_filter (gdk_get_default_root_window (), (GdkFilterFunc) filter_xkl_event, client); xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE); retval = set_keyboards_from_xkl (client); if (IS_KEYBOARD_VISIBLE (client)) eekboard_client_show_keyboard (client->eekboard, NULL); return retval; } void client_disable_xkl (SeatEmitter *client) { if (client->xkl_engine) { xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE); if (g_signal_handler_is_connected (client->xkl_engine, client->xkl_config_changed_handler)) g_signal_handler_disconnect (client->xkl_engine, client->xkl_config_changed_handler); if (g_signal_handler_is_connected (client->xkl_engine, client->xkl_state_changed_handler)) g_signal_handler_disconnect (client->xkl_engine, client->xkl_state_changed_handler); client->xkl_engine = NULL; } } #ifdef HAVE_ATSPI gboolean client_enable_atspi_focus (Client *client) { GError *error; error = NULL; if (!atspi_event_listener_register_from_callback ((AtspiEventListenerCB)focus_listener_cb, client, NULL, "object:state-changed:focused", &error)) { g_warning ("can't register object:state-changed:focused handler: %s", error->message); g_error_free (error); return FALSE; } error = NULL; if (!atspi_event_listener_register_from_callback ((AtspiEventListenerCB)focus_listener_cb, client, NULL, "focus:", &error)) { g_warning ("can't register focus: handler: %s", error->message); g_error_free (error); return FALSE; } client->follows_focus = TRUE; return TRUE; } void client_disable_atspi_focus (Client *client) { GError *error; client->follows_focus = FALSE; error = NULL; if (!atspi_event_listener_deregister_from_callback ((AtspiEventListenerCB)focus_listener_cb, client, "object:state-changed:focused", &error)) { g_warning ("can't deregister object:state-changed:focused handler: %s", error->message); g_error_free (error); } error = NULL; if (!atspi_event_listener_deregister_from_callback ((AtspiEventListenerCB)focus_listener_cb, client, "focus:", &error)) { g_warning ("can't deregister focus: handler: %s", error->message); g_error_free (error); } } gboolean client_enable_atspi_keystroke (Client *client) { GError *error; client->keystroke_listener = atspi_device_listener_new ((AtspiDeviceListenerCB)keystroke_listener_cb, client, NULL); error = NULL; if (!atspi_register_keystroke_listener (client->keystroke_listener, NULL, 0, ATSPI_KEY_PRESSED, ATSPI_KEYLISTENER_NOSYNC, &error)) { g_warning ("can't register keystroke listener for key press: %s", error->message); g_error_free (error); return FALSE; } error = NULL; if (!atspi_register_keystroke_listener (client->keystroke_listener, NULL, 0, ATSPI_KEY_RELEASED, ATSPI_KEYLISTENER_NOSYNC, &error)) { g_warning ("can't register keystroke listener for key release: %s", error->message); g_error_free (error); return FALSE; } return TRUE; } void client_disable_atspi_keystroke (Client *client) { if (client->keystroke_listener) { GError *error; error = NULL; if (!atspi_deregister_keystroke_listener (client->keystroke_listener, NULL, 0, ATSPI_KEY_PRESSED, &error)) { g_warning ("can't deregister keystroke listener for key press: %s", error->message); g_error_free (error); } error = NULL; if (!atspi_deregister_keystroke_listener (client->keystroke_listener, NULL, 0, ATSPI_KEY_RELEASED, &error)) { g_warning ("can't deregister keystroke listener for key release: %s", error->message); g_error_free (error); } g_object_unref (client->keystroke_listener); client->keystroke_listener = NULL; } } static void focus_listener_cb (const AtspiEvent *event, void *user_data) { Client *client = user_data; AtspiAccessible *accessible = event->source; AtspiStateSet *state_set = atspi_accessible_get_state_set (accessible); AtspiRole role; GError *error; error = NULL; role = atspi_accessible_get_role (accessible, &error); if (role == ATSPI_ROLE_INVALID) { g_warning ("can't get accessible role: %s", error->message); g_error_free (error); return; } if (atspi_state_set_contains (state_set, ATSPI_STATE_EDITABLE) || role == ATSPI_ROLE_TERMINAL) { switch (role) { case ATSPI_ROLE_TEXT: case ATSPI_ROLE_PARAGRAPH: case ATSPI_ROLE_PASSWORD_TEXT: case ATSPI_ROLE_TERMINAL: if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) { client->acc = accessible; eekboard_client_show_keyboard (client->eekboard, NULL); } else if (g_settings_get_boolean (client->settings, "auto-hide") && event->detail1 == 0 && accessible == client->acc) { client->acc = NULL; eekboard_client_hide_keyboard (client->eekboard, NULL); } break; case ATSPI_ROLE_ENTRY: if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) { client->acc = accessible; eekboard_client_show_keyboard (client->eekboard, NULL); } else if (g_settings_get_boolean (client->settings, "auto-hide") && event->detail1 == 0) { client->acc = NULL; eekboard_client_hide_keyboard (client->eekboard, NULL); } break; default: ; } } else { eekboard_client_hide_keyboard (client->eekboard, NULL); } } static gboolean keystroke_listener_cb (const AtspiDeviceEvent *stroke, void *user_data) { Client *client = user_data; switch (stroke->type) { case ATSPI_KEY_PRESSED: eekboard_context_press_keycode (client->context, stroke->hw_code, NULL); break; case ATSPI_KEY_RELEASED: eekboard_context_release_keycode (client->context, stroke->hw_code, NULL); break; default: g_return_val_if_reached (FALSE); } return TRUE; } #endif /* HAVE_ATSPI */ static void add_match_rule (GDBusConnection *connection, const gchar *match_rule) { GError *error; GDBusMessage *message; message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* path */ "org.freedesktop.DBus", /* interface */ "AddMatch"); g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); error = NULL; if (!g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error)) { g_warning ("can't register match rule %s: %s", match_rule, error->message); g_error_free (error); } g_object_unref (message); } static gboolean on_hide_keyboard_timeout (SeatEmitter *client) { eekboard_client_hide_keyboard (client->eekboard, NULL); client->hide_keyboard_timeout_id = 0; return FALSE; } static GDBusMessage * focus_message_filter (GDBusConnection *connection, GDBusMessage *message, gboolean incoming, gpointer user_data) { SeatEmitter *client = user_data; if (incoming && g_strcmp0 (g_dbus_message_get_interface (message), IBUS_INTERFACE_PANEL) == 0) { const gchar *member = g_dbus_message_get_member (message); if (g_strcmp0 (member, "FocusIn") == 0) { if (client->hide_keyboard_timeout_id > 0) { g_source_remove (client->hide_keyboard_timeout_id); client->hide_keyboard_timeout_id = 0; } eekboard_client_show_keyboard (client->eekboard, NULL); } else if (g_settings_get_boolean (client->settings, "auto-hide") && g_strcmp0 (member, "FocusOut") == 0) { guint delay; g_settings_get (client->settings, "auto-hide-delay", "u", &delay); client->hide_keyboard_timeout_id = g_timeout_add (delay, (GSourceFunc)on_hide_keyboard_timeout, client); } } return message; } static void _ibus_connect_focus_handlers (GDBusConnection *connection, gpointer user_data) { SeatEmitter *client = user_data; add_match_rule (connection, "type='method_call'," "interface='" IBUS_INTERFACE_PANEL "'," "member='FocusIn'"); add_match_rule (connection, "type='method_call'," "interface='" IBUS_INTERFACE_PANEL "'," "member='FocusOut'"); client->ibus_focus_message_filter = g_dbus_connection_add_filter (connection, focus_message_filter, client, NULL); } gboolean client_enable_ibus_focus (SeatEmitter *client) { if (client->ibus_connection == NULL) { const gchar *ibus_address; GError *error; ibus_address = g_getenv ("IBUS_ADDRESS"); if (ibus_address == NULL) { g_warning ("Can't get IBus address; set IBUS_ADDRESS"); return FALSE; } error = NULL; client->ibus_connection = g_dbus_connection_new_for_address_sync (ibus_address, G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, NULL, NULL, &error); if (client->ibus_connection == NULL) { g_warning ("Can't open connection to IBus: %s", error->message); g_error_free (error); return FALSE; } } _ibus_connect_focus_handlers (client->ibus_connection, client); client->follows_focus = TRUE; return TRUE; } void client_disable_ibus_focus (SeatEmitter *client) { client->follows_focus = FALSE; if (client->ibus_connection) { if (client->ibus_focus_message_filter != 0) { g_dbus_connection_remove_filter (client->ibus_connection, client->ibus_focus_message_filter); } g_object_unref (client->ibus_connection); client->ibus_connection = NULL; } } SeatEmitter * client_new (GDBusConnection *connection) { SeatEmitter *client = g_object_new (TYPE_CLIENT, "connection", connection, NULL); if (client->context) return client; return NULL; } static GdkFilterReturn filter_xkl_event (GdkXEvent *xev, GdkEvent *event, gpointer user_data) { SeatEmitter *client = user_data; XEvent *xevent = (XEvent *)xev; xkl_engine_filter_events (client->xkl_engine, xevent); return GDK_FILTER_CONTINUE; } static void on_xkl_config_changed (XklEngine *xklengine, gpointer user_data) { SeatEmitter *client = user_data; gboolean retval; retval = set_keyboards_from_xkl (client); g_return_if_fail (retval); #ifdef HAVE_XTEST update_modifier_keycodes (client); #endif /* HAVE_XTEST */ } static gboolean set_keyboards (SeatEmitter *client, const gchar * const *keyboards) { guint keyboard_id; const gchar * const *p; GSList *head; g_return_val_if_fail (keyboards != NULL, FALSE); g_return_val_if_fail (client->context, FALSE); if (client->keyboards) { for (head = client->keyboards; head; head = head->next) { eekboard_context_remove_keyboard (client->context, GPOINTER_TO_UINT(head->data), NULL); } g_slist_free (client->keyboards); client->keyboards = NULL; } for (p = keyboards; *p != NULL; p++) { keyboard_id = eekboard_context_add_keyboard (client->context, *p, NULL); if (keyboard_id == 0) { g_warning ("can't add keyboard %s", *p); continue; } client->keyboards = g_slist_prepend (client->keyboards, GUINT_TO_POINTER(keyboard_id)); } client->keyboards = g_slist_reverse (client->keyboards); client->keyboards_head = client->keyboards; /* select the first keyboard */ eekboard_context_set_keyboard (client->context, GPOINTER_TO_UINT(client->keyboards_head->data), NULL); return TRUE; } static gboolean set_keyboards_from_xkl (SeatEmitter *client) { XklConfigRec *rec; gchar *layout, *keyboard; guint keyboard_id; rec = xkl_config_rec_new (); xkl_config_rec_get_from_server (rec, client->xkl_engine); layout = eekboard_xkl_config_rec_to_string (rec); g_object_unref (rec); keyboard = g_strdup_printf ("xkb:%s", layout); g_free (layout); keyboard_id = eekboard_context_add_keyboard (client->context, keyboard, NULL); g_free (keyboard); if (keyboard_id == 0) return FALSE; eekboard_context_set_keyboard (client->context, keyboard_id, NULL); return TRUE; } static void on_xkl_state_changed (XklEngine *xklengine, XklEngineStateChange type, gint value, gboolean restore, gpointer user_data) { SeatEmitter *client = user_data; if (type == GROUP_CHANGED) eekboard_context_set_group (client->context, value, NULL); } /* The following functions for keyboard mapping change are direct translation of the code in Caribou (in libcaribou/xadapter.vala): - get_replaced_keycode (Caribou: get_reserved_keycode) - replace_keycode - get_keycode_from_gdk_keymap (Caribou: best_keycode_keyval_match) */ static guint get_replaced_keycode (SeatEmitter *client) { guint keycode; for (keycode = client->xkb->max_key_code; keycode >= client->xkb->min_key_code; --keycode) { 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) { return keycode; } } return 0; } /* Replace keysym assigned to KEYCODE to KEYSYM. Both args are used as in-out. If KEYCODE points to 0, this function picks a keycode from the current map and replace the associated keysym to KEYSYM. In that case, the replaced keycode is stored in KEYCODE and the old keysym is stored in KEYSYM. If otherwise (KEYCODE points to non-zero keycode), it simply changes the current map with the specified KEYCODE and KEYSYM. */ static gboolean replace_keycode (SeatEmitter *client, guint keycode, guint *keysym) { GdkDisplay *display = gdk_display_get_default (); Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); guint old_keysym; int keysyms_per_keycode; KeySym *syms; return TRUE; // FIXME: no xkb allocated at the moment, pretending all is fine g_return_val_if_fail (client->xkb->min_key_code <= keycode && keycode <= client->xkb->max_key_code, FALSE); g_return_val_if_fail (keysym != NULL, FALSE); /* * Switch keyboard mapping? syms = XGetKeyboardMapping (xdisplay, keycode, 1, &keysyms_per_keycode); old_keysym = syms[0]; syms[0] = *keysym; XChangeKeyboardMapping (xdisplay, keycode, 1, syms, 1); XSync (xdisplay, False); XFree (syms); *keysym = old_keysym; */ return TRUE; } static gboolean get_keycode_from_gdk_keymap (SeatEmitter *client, guint keysym, guint *keycode, guint *modifiers) { GdkKeymap *keymap = gdk_keymap_get_default (); GdkKeymapKey *keys, *best_match = NULL; 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 == eekboard_context_get_group (client->context, NULL)) best_match = &keys[i]; if (!best_match) { g_free (keys); return FALSE; } *keycode = best_match->keycode; *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0; g_free (keys); return TRUE; } int send_virtual_keyboard_key( Display* dpy, unsigned int keycode, Bool is_press, unsigned long delay ) { printf("Sending fake event %d press %d delay %d\n", keycode, is_press, delay); } /* never actually used? */ static void send_fake_modifier_key_event (SeatEmitter *client, EekModifierType modifiers, gboolean is_pressed) { GdkDisplay *display = gdk_display_get_default (); Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); gint i; for (i = 0; i < G_N_ELEMENTS(client->modifier_keycodes); i++) { if (modifiers & (1 << i)) { guint keycode = client->modifier_keycodes[i]; printf("Trying to send a modifier %d press %d\n", i, is_pressed); g_return_if_fail (keycode > 0); send_virtual_keyboard_key (xdisplay, keycode, is_pressed, CurrentTime); //XSync (xdisplay, False); } } } static void send_fake_key_event (SeatEmitter *client, guint xkeysym, guint keyboard_modifiers) { GdkDisplay *display = gdk_display_get_default (); Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); EekModifierType modifiers; guint keycode; guint old_keysym = xkeysym; g_return_if_fail (xkeysym > 0); modifiers = 0; if (!get_keycode_from_gdk_keymap (client, xkeysym, &keycode, &modifiers)) { keycode = get_replaced_keycode (client); if (keycode == 0) { g_warning ("no available keycode to replace"); return; } if (!replace_keycode (client, keycode, &old_keysym)) { g_warning ("failed to lookup X keysym %X", xkeysym); return; } } /* Clear level shift modifiers */ keyboard_modifiers &= ~EEK_SHIFT_MASK; keyboard_modifiers &= ~EEK_LOCK_MASK; /* FIXME: may need to remap ISO_Level3_Shift and NumLock */ #if 0 keyboard_modifiers &= ~EEK_MOD5_MASK; keyboard_modifiers &= ~client->alt_gr_mask; keyboard_modifiers &= ~client->num_lock_mask; #endif modifiers |= keyboard_modifiers; send_fake_modifier_key_event (client, modifiers, TRUE); send_virtual_keyboard_key (xdisplay, keycode, TRUE, 20); //XSync (xdisplay, False); send_virtual_keyboard_key (xdisplay, keycode, FALSE, 20); // XSync (xdisplay, False); send_fake_modifier_key_event (client, modifiers, FALSE); if (old_keysym != xkeysym) replace_keycode (client, keycode, &old_keysym); } static void send_fake_key_events (SeatEmitter *client, EekSymbol *symbol, guint keyboard_modifiers) { /* Ignore modifier keys */ if (eek_symbol_is_modifier (symbol)) return; /* If symbol is a text, convert chars in it to keysym */ if (EEK_IS_TEXT(symbol)) { const gchar *utf8 = eek_text_get_text (EEK_TEXT(symbol)); glong items_written; gunichar *ucs4 = g_utf8_to_ucs4_fast (utf8, -1, &items_written); gint i; for (i = 0; i < items_written; i++) { guint xkeysym; EekKeysym *keysym; gchar *name; name = g_strdup_printf ("U%04X", ucs4[i]); xkeysym = XStringToKeysym (name); g_free (name); keysym = eek_keysym_new (xkeysym); send_fake_key_events (client, EEK_SYMBOL(keysym), keyboard_modifiers); } g_free (ucs4); return; } if (EEK_IS_KEYSYM(symbol)) { guint xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol)); send_fake_key_event (client, xkeysym, keyboard_modifiers); } } static void on_key_activated (EekboardContext *context, guint keycode, EekSymbol *symbol, guint modifiers, gpointer user_data) { SeatEmitter *client = user_data; if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) { client->keyboards_head = g_slist_next (client->keyboards_head); if (client->keyboards_head == NULL) client->keyboards_head = client->keyboards; eekboard_context_set_keyboard (client->context, GPOINTER_TO_UINT(client->keyboards_head->data), NULL); return; } if (g_strcmp0 (eek_symbol_get_name (symbol), "preferences") == 0) { gchar *argv[2]; GError *error; argv[0] = g_build_filename (LIBEXECDIR, "eekboard-setup", NULL); argv[1] = NULL; error = NULL; if (!g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error)) { g_warning ("can't spawn %s: %s", argv[0], error->message); g_error_free (error); } g_free (argv[0]); return; } send_fake_key_events (client, symbol, modifiers); } #if 0 /* Finds the first key code for each modifier and saves it in modifier_keycodes */ static void update_modifier_keycodes (Client *client) { GdkDisplay *display = gdk_display_get_default (); Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); XModifierKeymap *mods; gint i, j; mods = XGetModifierMapping (xdisplay); for (i = 0; i < 8; i++) { client->modifier_keycodes[i] = 0; for (j = 0; j < mods->max_keypermod; j++) { KeyCode keycode = mods->modifiermap[mods->max_keypermod * i + j]; if (keycode != 0) { client->modifier_keycodes[i] = keycode; break; } } } XFreeModifiermap (mods); } #endif gboolean client_enable_xtest (SeatEmitter *client) { //GdkDisplay *display = gdk_display_get_default (); //Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); int opcode, event_base, error_base, major_version, minor_version; /* FIXME: need at least to fetch an xkb keymap (but what for?) g_assert (display); if (!XTestQueryExtension (xdisplay, &event_base, &error_base, &major_version, &minor_version)) { g_warning ("XTest extension is not available"); return FALSE; } if (!XkbQueryExtension (xdisplay, &opcode, &event_base, &error_base, &major_version, &minor_version)) { g_warning ("Xkb extension is not available"); return FALSE; } if (!client->xkb) client->xkb = XkbGetMap (xdisplay, XkbKeySymsMask, XkbUseCoreKbd); g_assert (client->xkb); update_modifier_keycodes (client); */ client->key_activated_handler = g_signal_connect (client->context, "key-activated", G_CALLBACK(on_key_activated), client); return TRUE; } void client_disable_xtest (SeatEmitter *client) { //if (client->xkb) { // XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */ //client->xkb = NULL; //} } //#endif /* HAVE_XTEST */