client.c 35 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* 
 * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
 * 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 <http://www.gnu.org/licenses/>.
 */
Daiki Ueno's avatar
Daiki Ueno committed
18 19 20 21
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif  /* HAVE_CONFIG_H */

22
#include <libxklavier/xklavier.h>
Daiki Ueno's avatar
Daiki Ueno committed
23

Daiki Ueno's avatar
Daiki Ueno committed
24 25 26 27
#ifdef HAVE_ATSPI
#include <dbus/dbus.h>
#include <atspi/atspi.h>
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
28

29 30
#include <gdk/gdkx.h>

31 32
#ifdef HAVE_XTEST
#include <X11/extensions/XTest.h>
33
#include <X11/XKBlib.h>
34
#endif  /* HAVE_XTEST */
35 36 37

#include "eek/eek.h"
#include "eek/eek-xkl.h"
Daiki Ueno's avatar
Daiki Ueno committed
38 39
#include "eekboard/eekboard-client.h"
#include "eekboard/eekboard-xklutil.h"
40
#include "client.h"
41

42 43
#include <string.h>

44 45 46
#define CSW 640
#define CSH 480

47 48 49 50 51 52
#define DEFAULT_KEYBOARD "us"
static gchar *default_keyboards[2] = {
    DEFAULT_KEYBOARD,
    NULL
};

53 54
#define IBUS_INTERFACE_PANEL    "org.freedesktop.IBus.Panel"

55 56 57
enum {
    PROP_0,
    PROP_CONNECTION,
Daiki Ueno's avatar
Daiki Ueno committed
58
    PROP_EEKBOARD,
59
    PROP_CONTEXT,
60
    PROP_KEYBOARDS,
61 62 63
    PROP_LAST
};

Daiki Ueno's avatar
Daiki Ueno committed
64
typedef struct _ClientClass ClientClass;
65

Daiki Ueno's avatar
Daiki Ueno committed
66
struct _Client {
67 68
    GObject parent;

Daiki Ueno's avatar
Daiki Ueno committed
69
    EekboardClient *eekboard;
70
    EekboardContext *context;
71

Daiki Ueno's avatar
Daiki Ueno committed
72
    GSList *keyboards;
73 74
    GSList *keyboards_head;

75 76 77
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;

78 79
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
80

81
    gulong key_activated_handler;
82

Daiki Ueno's avatar
Daiki Ueno committed
83
    gboolean follows_focus;
Daiki Ueno's avatar
Daiki Ueno committed
84
    guint hide_keyboard_timeout_id;
Daiki Ueno's avatar
Daiki Ueno committed
85

86 87 88
    GDBusConnection *ibus_connection;
    guint ibus_focus_message_filter;

Daiki Ueno's avatar
Daiki Ueno committed
89 90 91 92
#ifdef HAVE_ATSPI
    AtspiAccessible *acc;
    AtspiDeviceListener *keystroke_listener;
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
93

94
#ifdef HAVE_XTEST
Daiki Ueno's avatar
Daiki Ueno committed
95
    guint modifier_keycodes[8]; 
96
    XkbDescRec *xkb;
97
#endif  /* HAVE_XTEST */
98 99

    GSettings *settings;
100 101
};

Daiki Ueno's avatar
Daiki Ueno committed
102
struct _ClientClass {
103 104 105
    GObjectClass parent_class;
};

Daiki Ueno's avatar
Daiki Ueno committed
106
G_DEFINE_TYPE (Client, client, G_TYPE_OBJECT);
107

108 109 110 111 112 113
#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 */

Daiki Ueno's avatar
Daiki Ueno committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
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 */
133
static gboolean        set_keyboards        (Client                 *client,
Daiki Ueno's avatar
Daiki Ueno committed
134
                                             const gchar * const    *keyboard);
135 136
static gboolean        set_keyboards_from_xkl
                                            (Client                 *client);
137 138
#ifdef HAVE_XTEST
static void            update_modifier_keycodes
139
                                            (Client                 *client);
140
#endif  /* HAVE_XTEST */
141 142

static void
Daiki Ueno's avatar
Daiki Ueno committed
143 144 145 146
client_set_property (GObject      *object,
                     guint         prop_id,
                     const GValue *value,
                     GParamSpec   *pspec)
147
{
Daiki Ueno's avatar
Daiki Ueno committed
148
    Client *client = CLIENT(object);
149
    GDBusConnection *connection;
150
    gchar **keyboards;
151 152 153 154

    switch (prop_id) {
    case PROP_CONNECTION:
        connection = g_value_get_object (value);
155

Daiki Ueno's avatar
Daiki Ueno committed
156
        client->eekboard = eekboard_client_new (connection, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
157 158
        if (client->eekboard != NULL) {
            client->context =
Daiki Ueno's avatar
Daiki Ueno committed
159 160 161
                eekboard_client_create_context (client->eekboard,
                                                "eekboard",
                                                NULL);
Daiki Ueno's avatar
Daiki Ueno committed
162 163 164
            if (client->context == NULL) {
                g_object_unref (client->eekboard);
                client->eekboard = NULL;
165
            } else {
Daiki Ueno's avatar
Daiki Ueno committed
166 167 168
                eekboard_client_push_context (client->eekboard,
                                              client->context,
                                              NULL);
169 170 171 172
                g_settings_bind (client->settings, "keyboards",
                                 client, "keyboards",
                                 G_SETTINGS_BIND_GET);
            }
Daiki Ueno's avatar
Daiki Ueno committed
173
        }
174
        break;
175
    case PROP_KEYBOARDS:
176 177 178 179
        keyboards = g_value_get_boxed (value);
        if (g_strv_length (keyboards) == 0)
            keyboards = default_keyboards;
        client_set_keyboards (client, (const gchar * const *)keyboards);
180
        break;
181
    default:
182
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183 184 185 186
        break;
    }
}

187
static void
Daiki Ueno's avatar
Daiki Ueno committed
188 189 190 191
client_get_property (GObject    *object,
                     guint       prop_id,
                     GValue     *value,
                     GParamSpec *pspec)
192
{
Daiki Ueno's avatar
Daiki Ueno committed
193
    Client *client = CLIENT(object);
194 195

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
196 197 198
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
199 200 201 202
    case PROP_CONTEXT:
        g_value_set_object (value, client->context);
        break;
    default:
203
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
204 205 206 207
        break;
    }
}

208
static void
Daiki Ueno's avatar
Daiki Ueno committed
209
client_dispose (GObject *object)
210
{
Daiki Ueno's avatar
Daiki Ueno committed
211
    Client *client = CLIENT(object);
212

Daiki Ueno's avatar
Daiki Ueno committed
213
    client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
214

Daiki Ueno's avatar
Daiki Ueno committed
215
#ifdef HAVE_ATSPI
Daiki Ueno's avatar
Daiki Ueno committed
216 217
    client_disable_atspi_focus (client);
    client_disable_atspi_keystroke (client);
Daiki Ueno's avatar
Daiki Ueno committed
218
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
219

Daiki Ueno's avatar
Daiki Ueno committed
220
    client_disable_ibus_focus (client);
221 222 223
    if (client->ibus_connection) {
        g_object_unref (client->ibus_connection);
        client->ibus_connection = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
224 225
    }

226
#ifdef HAVE_XTEST
Daiki Ueno's avatar
Daiki Ueno committed
227
    client_disable_xtest (client);
228
#endif  /* HAVE_XTEST */
229

230 231 232 233 234
    if (client->context) {
        g_object_unref (client->context);
        client->context = NULL;
    }

235 236 237
    if (client->eekboard) {
        g_object_unref (client->eekboard);
        client->eekboard = NULL;
238 239
    }

240 241 242 243 244
    if (client->settings) {
        g_object_unref (client->settings);
        client->settings = NULL;
    }

Daiki Ueno's avatar
Daiki Ueno committed
245
    G_OBJECT_CLASS (client_parent_class)->dispose (object);
246 247
}

Daiki Ueno's avatar
Daiki Ueno committed
248 249 250 251 252
static void
client_finalize (GObject *object)
{
    Client *client = CLIENT(object);

Daiki Ueno's avatar
Daiki Ueno committed
253
    g_slist_free (client->keyboards);
Daiki Ueno's avatar
Daiki Ueno committed
254 255 256
    G_OBJECT_CLASS (client_parent_class)->finalize (object);
}

257
static void
Daiki Ueno's avatar
Daiki Ueno committed
258
client_class_init (ClientClass *klass)
259 260 261 262
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

Daiki Ueno's avatar
Daiki Ueno committed
263 264 265
    gobject_class->set_property = client_set_property;
    gobject_class->get_property = client_get_property;
    gobject_class->dispose = client_dispose;
Daiki Ueno's avatar
Daiki Ueno committed
266
    gobject_class->finalize = client_finalize;
267 268 269 270 271 272 273 274 275

    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);
276

Daiki Ueno's avatar
Daiki Ueno committed
277 278 279
    pspec = g_param_spec_object ("eekboard",
                                 "Eekboard",
                                 "Eekboard",
Daiki Ueno's avatar
Daiki Ueno committed
280
                                 EEKBOARD_TYPE_CLIENT,
Daiki Ueno's avatar
Daiki Ueno committed
281 282 283 284 285
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_EEKBOARD,
                                     pspec);

286 287 288 289 290 291 292 293
    pspec = g_param_spec_object ("context",
                                 "Context",
                                 "Context",
                                 EEKBOARD_TYPE_CONTEXT,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_CONTEXT,
                                     pspec);
294 295 296 297 298 299 300 301 302

    pspec = g_param_spec_boxed ("keyboards",
                                "Keyboards",
                                "Keyboards",
                                G_TYPE_STRV,
                                G_PARAM_WRITABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_KEYBOARDS,
                                     pspec);
303 304 305
}

static void
Daiki Ueno's avatar
Daiki Ueno committed
306
client_init (Client *client)
307
{
308
    client->settings = g_settings_new ("org.fedorahosted.eekboard");
309 310
}

311
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
312 313
client_set_keyboards (Client              *client,
                      const gchar * const *keyboards)
314 315
{
    gboolean retval;
316
    retval = set_keyboards (client, keyboards);
317
    if (retval && IS_KEYBOARD_VISIBLE (client))
318
        eekboard_client_show_keyboard (client->eekboard, NULL);
319
    return retval;
320 321
}

322
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
323
client_enable_xkl (Client *client)
324
{
Daiki Ueno's avatar
Daiki Ueno committed
325
    GdkDisplay *display = gdk_display_get_default ();
326 327
    gboolean retval;

Daiki Ueno's avatar
Daiki Ueno committed
328
    g_assert (display);
329

330 331
    if (!client->xkl_engine) {
        client->xkl_engine =
Daiki Ueno's avatar
Daiki Ueno committed
332
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (display));
333
    }
334
    g_assert (client->xkl_engine);
335 336 337 338 339 340 341

    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);
    }

342
    client->xkl_config_changed_handler =
343 344
        g_signal_connect (client->xkl_engine, "X-config-changed",
                          G_CALLBACK(on_xkl_config_changed), client);
345
    client->xkl_state_changed_handler =
346 347 348 349 350 351 352 353 354 355 356 357
        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);

358
    retval = set_keyboards_from_xkl (client);
359
    if (IS_KEYBOARD_VISIBLE (client))
360
        eekboard_client_show_keyboard (client->eekboard, NULL);
361 362

    return retval;
363 364 365
}

void
Daiki Ueno's avatar
Daiki Ueno committed
366
client_disable_xkl (Client *client)
367
{
368
    if (client->xkl_engine) {
369
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
370 371 372 373 374 375 376 377 378 379 380

        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;
    }
381 382
}

Daiki Ueno's avatar
Daiki Ueno committed
383
#ifdef HAVE_ATSPI
384
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
385
client_enable_atspi_focus (Client *client)
386
{
Daiki Ueno's avatar
Daiki Ueno committed
387
    GError *error;
388

Daiki Ueno's avatar
Daiki Ueno committed
389 390 391 392 393 394
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "object:state-changed:focused",
Daiki Ueno's avatar
Daiki Ueno committed
395 396 397 398
         &error)) {
        g_warning ("can't register object:state-changed:focused handler: %s",
                   error->message);
        g_error_free (error);
399
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
400
    }
401

Daiki Ueno's avatar
Daiki Ueno committed
402 403 404 405 406 407
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "focus:",
Daiki Ueno's avatar
Daiki Ueno committed
408 409 410 411
         &error)) {
        g_warning ("can't register focus: handler: %s",
                   error->message);
        g_error_free (error);
412
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
413
    }
414

Daiki Ueno's avatar
Daiki Ueno committed
415
    client->follows_focus = TRUE;
416 417 418 419
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
420
client_disable_atspi_focus (Client *client)
421
{
Daiki Ueno's avatar
Daiki Ueno committed
422 423 424 425 426
    GError *error;

    client->follows_focus = FALSE;

    error = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
427
    if (!atspi_event_listener_deregister_from_callback
Daiki Ueno's avatar
Daiki Ueno committed
428 429 430
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         "object:state-changed:focused",
Daiki Ueno's avatar
Daiki Ueno committed
431 432 433 434 435
         &error)) {
        g_warning ("can't deregister object:state-changed:focused handler: %s",
                   error->message);
        g_error_free (error);
    }
Daiki Ueno's avatar
Daiki Ueno committed
436 437

    error = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
438
    if (!atspi_event_listener_deregister_from_callback
Daiki Ueno's avatar
Daiki Ueno committed
439 440 441
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         "focus:",
Daiki Ueno's avatar
Daiki Ueno committed
442 443 444 445 446
         &error)) {
        g_warning ("can't deregister focus: handler: %s",
                   error->message);
        g_error_free (error);
    }
447 448 449
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
450
client_enable_atspi_keystroke (Client *client)
451
{
Daiki Ueno's avatar
Daiki Ueno committed
452 453
    GError *error;

454
    client->keystroke_listener =
Daiki Ueno's avatar
Daiki Ueno committed
455
        atspi_device_listener_new ((AtspiDeviceListenerCB)keystroke_listener_cb,
Daiki Ueno's avatar
Daiki Ueno committed
456 457
                                   client,
                                   NULL);
458

Daiki Ueno's avatar
Daiki Ueno committed
459 460 461 462 463 464 465
    error = NULL;
    if (!atspi_register_keystroke_listener
        (client->keystroke_listener,
         NULL,
         0,
         ATSPI_KEY_PRESSED,
         ATSPI_KEYLISTENER_NOSYNC,
Daiki Ueno's avatar
Daiki Ueno committed
466 467 468 469
         &error)) {
        g_warning ("can't register keystroke listener for key press: %s",
                   error->message);
        g_error_free (error);
Daiki Ueno's avatar
Daiki Ueno committed
470
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
471
    }
Daiki Ueno's avatar
Daiki Ueno committed
472 473 474

    error = NULL;
    if (!atspi_register_keystroke_listener
475
        (client->keystroke_listener,
Daiki Ueno's avatar
Daiki Ueno committed
476
         NULL,
477
         0,
Daiki Ueno's avatar
Daiki Ueno committed
478 479
         ATSPI_KEY_RELEASED,
         ATSPI_KEYLISTENER_NOSYNC,
Daiki Ueno's avatar
Daiki Ueno committed
480 481 482 483
         &error)) {
        g_warning ("can't register keystroke listener for key release: %s",
                   error->message);
        g_error_free (error);
484
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
485
    }
486 487 488 489
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
490
client_disable_atspi_keystroke (Client *client)
491 492
{
    if (client->keystroke_listener) {
Daiki Ueno's avatar
Daiki Ueno committed
493 494 495
        GError *error;

        error = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
496 497 498 499 500 501 502 503 504 505
        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);
        }
Daiki Ueno's avatar
Daiki Ueno committed
506 507

        error = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
508 509 510 511 512 513 514 515 516
        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);
        }
Daiki Ueno's avatar
Daiki Ueno committed
517 518

        g_object_unref (client->keystroke_listener);
519 520 521 522
        client->keystroke_listener = NULL;
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
523 524 525
static void
focus_listener_cb (const AtspiEvent *event,
                   void             *user_data)
526
{
Daiki Ueno's avatar
Daiki Ueno committed
527
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
528 529 530 531 532 533 534
    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);
Daiki Ueno's avatar
Daiki Ueno committed
535
    if (role == ATSPI_ROLE_INVALID) {
Daiki Ueno's avatar
Daiki Ueno committed
536 537 538
        g_warning ("can't get accessible role: %s",
                   error->message);
        g_error_free (error);
Daiki Ueno's avatar
Daiki Ueno committed
539
        return;
Daiki Ueno's avatar
Daiki Ueno committed
540
    }
541

Daiki Ueno's avatar
Daiki Ueno committed
542 543
    if (atspi_state_set_contains (state_set, ATSPI_STATE_EDITABLE) ||
        role == ATSPI_ROLE_TERMINAL) {
544
        switch (role) {
Daiki Ueno's avatar
Daiki Ueno committed
545 546 547 548
        case ATSPI_ROLE_TEXT:
        case ATSPI_ROLE_PARAGRAPH:
        case ATSPI_ROLE_PASSWORD_TEXT:
        case ATSPI_ROLE_TERMINAL:
Daiki Ueno's avatar
Daiki Ueno committed
549 550
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
551
                eekboard_client_show_keyboard (client->eekboard, NULL);
552 553
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0 && accessible == client->acc) {
Daiki Ueno's avatar
Daiki Ueno committed
554
                client->acc = NULL;
555
                eekboard_client_hide_keyboard (client->eekboard, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
556 557
            }
            break;
Daiki Ueno's avatar
Daiki Ueno committed
558
        case ATSPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
559 560
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
561
                eekboard_client_show_keyboard (client->eekboard, NULL);
562 563
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
564
                client->acc = NULL;
565
                eekboard_client_hide_keyboard (client->eekboard, NULL);
566
            }
Daiki Ueno's avatar
Daiki Ueno committed
567 568
            break;
            
569 570 571
        default:
            ;
        }
572
    } else {
573
        eekboard_client_hide_keyboard (client->eekboard, NULL);
574
    }
575 576
}

Daiki Ueno's avatar
Daiki Ueno committed
577 578 579
static gboolean
keystroke_listener_cb (const AtspiDeviceEvent *stroke,
                       void                   *user_data)
580
{
Daiki Ueno's avatar
Daiki Ueno committed
581
    Client *client = user_data;
582

583 584
    switch (stroke->type) {
    case ATSPI_KEY_PRESSED:
Daiki Ueno's avatar
Daiki Ueno committed
585
        eekboard_context_press_keycode (client->context, stroke->hw_code, NULL);
586 587
        break;
    case ATSPI_KEY_RELEASED:
Daiki Ueno's avatar
Daiki Ueno committed
588
        eekboard_context_release_keycode (client->context, stroke->hw_code, NULL);
589 590 591
        break;
    default:
        g_return_val_if_reached (FALSE);
592
    }
593 594
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
595
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
596

Daiki Ueno's avatar
Daiki Ueno committed
597 598 599 600
static void
add_match_rule (GDBusConnection *connection,
                const gchar     *match_rule)
{
Daiki Ueno's avatar
Daiki Ueno committed
601 602
    GError *error;
    GDBusMessage *message;
Daiki Ueno's avatar
Daiki Ueno committed
603

Daiki Ueno's avatar
Daiki Ueno committed
604 605 606 607 608 609
    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;
Daiki Ueno's avatar
Daiki Ueno committed
610 611 612 613 614 615 616 617 618
    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);
    }
Daiki Ueno's avatar
Daiki Ueno committed
619
    g_object_unref (message);
Daiki Ueno's avatar
Daiki Ueno committed
620 621
}

Daiki Ueno's avatar
Daiki Ueno committed
622
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
623
on_hide_keyboard_timeout (Client *client)
Daiki Ueno's avatar
Daiki Ueno committed
624
{
625
    eekboard_client_hide_keyboard (client->eekboard, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
626 627 628 629
    client->hide_keyboard_timeout_id = 0;
    return FALSE;
}

Daiki Ueno's avatar
Daiki Ueno committed
630 631 632 633 634 635
static GDBusMessage *
focus_message_filter (GDBusConnection *connection,
                      GDBusMessage    *message,
                      gboolean         incoming,
                      gpointer         user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
636
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
637 638 639

    if (incoming &&
        g_strcmp0 (g_dbus_message_get_interface (message),
640
                   IBUS_INTERFACE_PANEL) == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
641 642 643
        const gchar *member = g_dbus_message_get_member (message);

        if (g_strcmp0 (member, "FocusIn") == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
644 645 646 647
            if (client->hide_keyboard_timeout_id > 0) {
                g_source_remove (client->hide_keyboard_timeout_id);
                client->hide_keyboard_timeout_id = 0;
            }
648
            eekboard_client_show_keyboard (client->eekboard, NULL);
649 650
        } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                   g_strcmp0 (member, "FocusOut") == 0) {
651 652
            guint delay;
            g_settings_get (client->settings, "auto-hide-delay", "u", &delay);
Daiki Ueno's avatar
Daiki Ueno committed
653
            client->hide_keyboard_timeout_id =
654
                g_timeout_add (delay,
Daiki Ueno's avatar
Daiki Ueno committed
655 656
                               (GSourceFunc)on_hide_keyboard_timeout,
                               client);
Daiki Ueno's avatar
Daiki Ueno committed
657 658 659 660 661 662
        }
    }

    return message;
}

663
static void
664
_ibus_connect_focus_handlers (GDBusConnection *connection, gpointer user_data)
Daiki Ueno's avatar
Daiki Ueno committed
665
{
Daiki Ueno's avatar
Daiki Ueno committed
666
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
667 668 669

    add_match_rule (connection,
                    "type='method_call',"
670
                    "interface='" IBUS_INTERFACE_PANEL "',"
Daiki Ueno's avatar
Daiki Ueno committed
671
                    "member='FocusIn'");
672 673
    add_match_rule (connection,
                    "type='method_call',"
674
                    "interface='" IBUS_INTERFACE_PANEL "',"
675
                    "member='FocusOut'");
Daiki Ueno's avatar
Daiki Ueno committed
676 677 678 679 680
    client->ibus_focus_message_filter =
        g_dbus_connection_add_filter (connection,
                                      focus_message_filter,
                                      client,
                                      NULL);
681 682 683
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
684
client_enable_ibus_focus (Client *client)
685
{
686 687 688
    if (client->ibus_connection == NULL) {
        const gchar *ibus_address;
        GError *error;
689

690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
        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);
710

Daiki Ueno's avatar
Daiki Ueno committed
711 712 713 714 715
    client->follows_focus = TRUE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
716
client_disable_ibus_focus (Client *client)
Daiki Ueno's avatar
Daiki Ueno committed
717 718 719
{
    client->follows_focus = FALSE;

720
    if (client->ibus_connection) {
721
        if (client->ibus_focus_message_filter != 0) {
722
            g_dbus_connection_remove_filter (client->ibus_connection,
723 724
                                             client->ibus_focus_message_filter);
        }
725 726
        g_object_unref (client->ibus_connection);
        client->ibus_connection = NULL;
727
    }
Daiki Ueno's avatar
Daiki Ueno committed
728 729
}

Daiki Ueno's avatar
Daiki Ueno committed
730 731
Client *
client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
732
{
Daiki Ueno's avatar
Daiki Ueno committed
733 734 735
    Client *client = g_object_new (TYPE_CLIENT,
                                   "connection", connection,
                                   NULL);
Daiki Ueno's avatar
Daiki Ueno committed
736 737 738
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
739
}
740 741 742 743 744 745

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
746
    Client *client = user_data;
747 748 749 750 751 752 753 754 755 756
    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)
{
Daiki Ueno's avatar
Daiki Ueno committed
757
    Client *client = user_data;
758
    gboolean retval;
759

760
    retval = set_keyboards_from_xkl (client);
761
    g_return_if_fail (retval);
762

763
#ifdef HAVE_XTEST
764
    update_modifier_keycodes (client);
765
#endif  /* HAVE_XTEST */
766 767
}

768
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
769 770
set_keyboards (Client              *client,
               const gchar * const *keyboards)
771
{
772
    guint keyboard_id;
Daiki Ueno's avatar
Daiki Ueno committed
773
    const gchar * const *p;
774 775 776 777
    GSList *head;

    g_return_val_if_fail (keyboards != NULL, FALSE);
    g_return_val_if_fail (client->context, FALSE);
Daiki Ueno's avatar
Daiki Ueno committed
778

779 780 781 782 783 784
    if (client->keyboards) {
        for (head = client->keyboards; head; head = head->next) {
            eekboard_context_remove_keyboard (client->context,
                                              GPOINTER_TO_UINT(head->data),
                                              NULL);
        }
Daiki Ueno's avatar
Daiki Ueno committed
785
        g_slist_free (client->keyboards);
786 787
        client->keyboards = NULL;
    }
788

789 790 791
    for (p = keyboards; *p != NULL; p++) {
        keyboard_id = eekboard_context_add_keyboard (client->context, *p, NULL);
        if (keyboard_id == 0) {
792 793
            g_warning ("can't add keyboard %s", *p);
            continue;
794
        }
795 796
        client->keyboards = g_slist_prepend (client->keyboards,
                                             GUINT_TO_POINTER(keyboard_id));
Daiki Ueno's avatar
Daiki Ueno committed
797 798
    }

799 800
    client->keyboards = g_slist_reverse (client->keyboards);
    client->keyboards_head = client->keyboards;
Daiki Ueno's avatar
Daiki Ueno committed
801 802 803

    /* select the first keyboard */
    eekboard_context_set_keyboard (client->context,
804
                                   GPOINTER_TO_UINT(client->keyboards_head->data),
Daiki Ueno's avatar
Daiki Ueno committed
805
                                   NULL);
Daiki Ueno's avatar
Daiki Ueno committed
806 807 808
    return TRUE;
}

809
static gboolean
810
set_keyboards_from_xkl (Client *client)
811 812 813
{
    XklConfigRec *rec;
    gchar *layout, *keyboard;
Daiki Ueno's avatar
Daiki Ueno committed
814
    guint keyboard_id;
815 816 817 818 819 820 821 822

    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);
Daiki Ueno's avatar
Daiki Ueno committed
823 824 825 826

    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 keyboard,
                                                 NULL);
827
    g_free (keyboard);
Daiki Ueno's avatar
Daiki Ueno committed
828 829 830
    if (keyboard_id == 0)
        return FALSE;
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
831

Daiki Ueno's avatar
Daiki Ueno committed
832
    return TRUE;
833 834
}

835 836 837 838 839 840 841
static void
on_xkl_state_changed (XklEngine           *xklengine,
                      XklEngineStateChange type,
                      gint                 value,
                      gboolean             restore,
                      gpointer             user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
842
    Client *client = user_data;
843

844 845
    if (type == GROUP_CHANGED)
        eekboard_context_set_group (client->context, value, NULL);
846 847
}

848
#ifdef HAVE_XTEST
849 850 851 852 853 854 855 856
/* 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
Daiki Ueno's avatar
Daiki Ueno committed
857
get_replaced_keycode (Client *client)
858
{
Daiki Ueno's avatar
Daiki Ueno committed
859 860 861 862 863 864 865 866
    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) {
Daiki Ueno's avatar
Daiki Ueno committed
867
            return keycode;
Daiki Ueno's avatar
Daiki Ueno committed
868 869
        }
    }
870

Daiki Ueno's avatar
Daiki Ueno committed
871
    return 0;
872 873 874 875 876 877 878 879 880 881
}

/* 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
Daiki Ueno's avatar
Daiki Ueno committed
882
replace_keycode (Client *client,
Daiki Ueno's avatar
Daiki Ueno committed
883 884
                 guint   keycode,
                 guint  *keysym)
885
{
Daiki Ueno's avatar
Daiki Ueno committed
886 887
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);