client.c 35.5 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/>.
 */
18 19 20 21
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif  /* HAVE_CONFIG_H */

22
#include <libxklavier/xklavier.h>
23

24 25 26 27
#ifdef HAVE_ATSPI
#include <dbus/dbus.h>
#include <atspi/atspi.h>
#endif  /* HAVE_ATSPI */
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,
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

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

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

86 87 88
    GDBusConnection *ibus_connection;
    guint ibus_focus_message_filter;

89 90 91 92
#ifdef HAVE_ATSPI
    AtspiAccessible *acc;
    AtspiDeviceListener *keystroke_listener;
#endif  /* HAVE_ATSPI */
93

Daiki Ueno's avatar
Daiki Ueno committed
94
    guint modifier_keycodes[8]; 
95
    XkbDescRec *xkb;
96 97

    GSettings *settings;
98 99
};

Daiki Ueno's avatar
Daiki Ueno committed
100
struct _ClientClass {
101 102 103
    GObjectClass parent_class;
};

Daiki Ueno's avatar
Daiki Ueno committed
104
G_DEFINE_TYPE (Client, client, G_TYPE_OBJECT);
105

106 107 108 109 110 111
#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 */

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

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

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

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

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

    switch (prop_id) {
194 195 196
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
197 198 199 200
    case PROP_CONTEXT:
        g_value_set_object (value, client->context);
        break;
    default:
201
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202 203 204 205
        break;
    }
}

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

Daiki Ueno's avatar
Daiki Ueno committed
211
    client_disable_xkl (client);
212

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

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

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

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

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

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

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

246 247 248 249 250
static void
client_finalize (GObject *object)
{
    Client *client = CLIENT(object);

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

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

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

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

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

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

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

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

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

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

326
    g_assert (display);
327

328 329
    if (!client->xkl_engine) {
        client->xkl_engine =
330
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (display));
331
    }
332
    g_assert (client->xkl_engine);
333 334 335 336 337 338 339

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

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

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

    return retval;
361 362 363
}

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

        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;
    }
379 380
}

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

387 388 389 390 391 392
    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
393 394 395 396
         &error)) {
        g_warning ("can't register object:state-changed:focused handler: %s",
                   error->message);
        g_error_free (error);
397
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
398
    }
399

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

413
    client->follows_focus = TRUE;
414 415 416 417
    return TRUE;
}

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

    client->follows_focus = FALSE;

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

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

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

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

457 458 459 460 461 462 463
    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
464 465 466 467
         &error)) {
        g_warning ("can't register keystroke listener for key press: %s",
                   error->message);
        g_error_free (error);
468
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
469
    }
470 471 472

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

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

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

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

        g_object_unref (client->keystroke_listener);
517 518 519 520
        client->keystroke_listener = NULL;
    }
}

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

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

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

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

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

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

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

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

    if (incoming &&
        g_strcmp0 (g_dbus_message_get_interface (message),
638
                   IBUS_INTERFACE_PANEL) == 0) {
639 640 641
        const gchar *member = g_dbus_message_get_member (message);

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

    return message;
}

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

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

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

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

709 710 711 712 713
    client->follows_focus = TRUE;
    return TRUE;
}

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

718
    if (client->ibus_connection) {
719
        if (client->ibus_focus_message_filter != 0) {
720
            g_dbus_connection_remove_filter (client->ibus_connection,
721 722
                                             client->ibus_focus_message_filter);
        }
723 724
        g_object_unref (client->ibus_connection);
        client->ibus_connection = NULL;
725
    }
726 727
}

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

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

758
    retval = set_keyboards_from_xkl (client);
759
    g_return_if_fail (retval);
760

761
#ifdef HAVE_XTEST
762
    update_modifier_keycodes (client);
763
#endif  /* HAVE_XTEST */
764 765
}

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

    g_return_val_if_fail (keyboards != NULL, FALSE);
    g_return_val_if_fail (client->context, FALSE);
776

777 778 779 780 781 782
    if (client->keyboards) {
        for (head = client->keyboards; head; head = head->next) {
            eekboard_context_remove_keyboard (client->context,
                                              GPOINTER_TO_UINT(head->data),
                                              NULL);
        }
783
        g_slist_free (client->keyboards);
784 785
        client->keyboards = NULL;
    }
786

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

797 798
    client->keyboards = g_slist_reverse (client->keyboards);
    client->keyboards_head = client->keyboards;
799 800 801

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

807
static gboolean
808
set_keyboards_from_xkl (Client *client)
809 810 811
{
    XklConfigRec *rec;
    gchar *layout, *keyboard;
812
    guint keyboard_id;
813 814 815 816 817 818 819 820

    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);
821 822 823 824

    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 keyboard,
                                                 NULL);
825
    g_free (keyboard);
826 827 828
    if (keyboard_id == 0)
        return FALSE;
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
829

830
    return TRUE;
831 832
}

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

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

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

Daiki Ueno's avatar
Daiki Ueno committed
868
    return 0;
869 870 871 872 873 874 875 876 877 878
}

/* 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
879
replace_keycode (Client *client,
Daiki Ueno's avatar
Daiki Ueno committed
880 881
                 guint   keycode,
                 guint  *keysym)
882
{
883 884
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
Daiki Ueno's avatar
Daiki Ueno committed
885
    guint old_keysym;
Daiki Ueno's avatar
Daiki Ueno committed
886 887
    int keysyms_per_keycode;
    KeySym *syms;
888
return TRUE; // FIXME: no xkb allocated at the moment, pretending all is fine
Daiki Ueno's avatar
Daiki Ueno committed
889
    g_return_val_if_fail (client->xkb->min_key_code <= keycode &&
Daiki Ueno's avatar
Daiki Ueno committed
890
                          keycode <= client->xkb->max_key_code,
Daiki Ueno's avatar
Daiki Ueno committed
891 892
                          FALSE);
    g_return_val_if_fail (keysym != NULL, FALSE);
893 894
/*
 * Switch keyboard mapping?
Daiki Ueno's avatar
Daiki Ueno committed
895 896 897 898
    syms = XGetKeyboardMapping (xdisplay, keycode, 1, &keysyms_per_keycode);
    old_keysym = syms[0];
    syms[0] = *keysym;
    XChangeKeyboardMapping (xdisplay, keycode, 1, syms, 1);
899
    XSync (xdisplay, False);
Daiki Ueno's avatar
Daiki Ueno committed
900
    XFree (syms);
Daiki Ueno's avatar
Daiki Ueno committed
901
    *keysym = old_keysym;
902
*/
903 904 905 906
    return TRUE;
}

static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
907
get_keycode_from_gdk_keymap (Client *client,
908 909 910 911 912
                             guint           keysym,
                             guint          *keycode,
                             guint          *modifiers)
{
    GdkKeymap *keymap = gdk_keymap_get_default ();
913
    GdkKeymapKey *keys, *best_match = NULL;
914 915 916 917 918 919
    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++)
920
        if (keys[i].group == eekboard_context_get_group (client->context, NULL))
921 922
            best_match = &keys[i];

923 924 925 926 927
    if (!best_match) {
        g_free (keys);
        return FALSE;
    }

928 929 930 931 932 933 934
    *keycode = best_match->keycode;
    *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0;

    g_free (keys);
    return TRUE;
}

935 936 937 938 939 940 941 942 943 944
int WaylandFakeKeyEvent(
    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? */
945
static void
Daiki Ueno's avatar
Daiki Ueno committed
946
send_fake_modifier_key_event (Client         *client,
947 948
                              EekModifierType modifiers,
                              gboolean        is_pressed)
949
{
950
    GdkDisplay *display = gdk_display_get_default ();
Daiki Ueno's avatar
Daiki Ueno committed
951
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
952
    gint i;
953

954 955 956
    for (i = 0; i < G_N_ELEMENTS(client->modifier_keycodes); i++) {
        if (modifiers & (1 << i)) {
            guint keycode = client->modifier_keycodes[i];
957
            printf("Trying to send a modifier %d press %d\n", i, is_pressed);
958 959
            g_return_if_fail (keycode > 0);

960
            WaylandFakeKeyEvent (xdisplay,
961 962 963
                               keycode,
                               is_pressed,
                               CurrentTime);
964
            //XSync (xdisplay, False);
965 966 967 968 969
        }
    }
}

static void
Daiki Ueno's avatar
Daiki Ueno committed
970 971
send_fake_key_event (Client  *client,
                     guint    xkeysym,
Daiki Ueno's avatar
Daiki Ueno committed
972
                     guint    keyboard_modifiers)
973
{
974
    GdkDisplay *display = gdk_display_get_default ();
Daiki Ueno's avatar
Daiki Ueno committed
975
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
976
    EekModifierType modifiers;
Daiki Ueno's avatar
Daiki Ueno committed
977
    guint keycode;
Daiki Ueno's avatar
Daiki Ueno committed
978
    guint old_keysym = xkeysym;
979 980 981

    g_return_if_fail (xkeysym > 0);

982
    modifiers = 0;
Daiki Ueno's avatar
Daiki Ueno committed
983 984 985 986 987 988
    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;
        }
Daiki Ueno's avatar
Daiki Ueno committed
989

Daiki Ueno's avatar
Daiki Ueno committed
990 991 992
        if (!replace_keycode (client, keycode, &old_keysym)) {
            g_warning ("failed to lookup X keysym %X", xkeysym);
            return;
993
        }
994
    }
Daiki Ueno's avatar
Daiki Ueno committed
995

996 997 998
    /* Clear level shift modifiers */
    keyboard_modifiers &= ~EEK_SHIFT_MASK;
    keyboard_modifiers &= ~EEK_LOCK_MASK;
999
    /* FIXME: may need to remap ISO_Level3_Shift and NumLock */
Daiki Ueno's avatar
Daiki Ueno committed
1000 1001 1002 1003 1004
#if 0
    keyboard_modifiers &= ~EEK_MOD5_MASK;
    keyboard_modifiers &= ~client->alt_gr_mask;
    keyboard_modifiers &= ~client->num_lock_mask;
#endif
1005 1006

    modifiers |= keyboard_modifiers;
1007

Daiki Ueno's avatar
Daiki Ueno committed
1008
    send_fake_modifier_key_event (client, modifiers, TRUE);
1009 1010 1011 1012
    WaylandFakeKeyEvent (xdisplay, keycode, TRUE, 20);
    //XSync (xdisplay, False);
    WaylandFakeKeyEvent (xdisplay, keycode, FALSE, 20);
    // XSync (xdisplay, False);
Daiki Ueno's avatar
Daiki Ueno committed
1013 1014 1015 1016
    send_fake_modifier_key_event (client, modifiers, FALSE);

    if (old_keysym != xkeysym)
        replace_keycode (client, keycode, &old_keysym);
1017 1018
}

Daiki Ueno's avatar
Daiki Ueno committed
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
static void
send_fake_key_events (Client    *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)) {
Daiki Ueno's avatar
Daiki Ueno committed
1030
        const gchar *utf8 = eek_text_get_text (EEK_TEXT(symbol));
Daiki Ueno's avatar
Daiki Ueno committed
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
        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;
    }

Daiki Ueno's avatar
Daiki Ueno committed
1053 1054
    if (EEK_IS_KEYSYM(symbol)) {
        guint xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
Daiki Ueno's avatar
Daiki Ueno committed
1055
        send_fake_key_event (client, xkeysym, keyboard_modifiers);
Daiki Ueno's avatar
Daiki Ueno committed
1056
    }
Daiki Ueno's avatar
Daiki Ueno committed
1057 1058
}

1059
static void
1060 1061 1062 1063 1064
on_key_activated (EekboardContext *context,
                  guint            keycode,
                  EekSymbol       *symbol,
                  guint            modifiers,
                  gpointer         user_data)
1065
{
Daiki Ueno's avatar
Daiki Ueno committed
1066
    Client *client = user_data;
1067 1068

    if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) {
1069 1070 1071
        client->keyboards_head = g_slist_next (client->keyboards_head);
        if (client->keyboards_head == NULL)
            client->keyboards_head = client->keyboards;
1072
        eekboard_context_set_keyboard (client->context,
1073
                                       GPOINTER_TO_UINT(client->keyboards_head->data),
1074
                                       NULL);
1075
        return;
1076 1077
    }

1078
    if (g_strcmp0 (eek_symbol_get_name (symbol), "preferences") == 0) {
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
        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]);
1091
        return;
1092 1093
    }

Daiki Ueno's avatar
Daiki Ueno committed
1094
    send_fake_key_events (client, symbol, modifiers);
1095
}
1096

1097 1098
#if 0
/* Finds the first key code for each modifier and saves it in modifier_keycodes */
1099
static void
Daiki Ueno's avatar
Daiki Ueno committed
1100
update_modifier_keycodes (Client *client)
1101
{
1102
    GdkDisplay *display = gdk_display_get_default ();
Daiki Ueno's avatar
Daiki Ueno committed
1103
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
1104 1105 1106
    XModifierKeymap *mods;
    gint i, j;

Daiki Ueno's avatar
Daiki Ueno committed
1107
    mods = XGetModifierMapping (xdisplay);
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
    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;
            }
        }
    }
1118
    XFreeModifiermap (mods);
1119
}
1120
#endif
1121
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
1122
client_enable_xtest (Client *client)
1123
{
1124 1125
    //GdkDisplay *display = gdk_display_get_default ();
    //Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
1126
    int opcode, event_base, error_base, major_version, minor_version;
1127

1128
    /* FIXME: need at least to fetch an xkb keymap (but what for?)
1129
    g_assert (display);
1130

Daiki Ueno's avatar
Daiki Ueno committed
1131
    if (!XTestQueryExtension (xdisplay,
1132 1133 1134 1135 1136
                              &event_base, &error_base,
                              &major_version, &minor_version)) {
        g_warning ("XTest extension is not available");
        return FALSE;
    }
1137

Daiki Ueno's avatar
Daiki Ueno committed
1138
    if (!XkbQueryExtension (xdisplay,
1139 1140 1141 1142 1143 1144 1145
                            &opcode, &event_base, &error_base,
                            &major_version, &minor_version)) {
        g_warning ("Xkb extension is not available");
        return FALSE;
    }

    if (!client->xkb)
Daiki Ueno's avatar
Daiki Ueno committed
1146
        client->xkb = XkbGetMap (xdisplay, XkbKeySymsMask, XkbUseCoreKbd);
1147 1148
    g_assert (client->xkb);

1149
    update_modifier_keycodes (client);
1150
*/
1151 1152 1153
    client->key_activated_handler =
        g_signal_connect (client->context, "key-activated",
                          G_CALLBACK(on_key_activated), client);
1154 1155 1156 1157 1158

    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
1159
client_disable_xtest (Client *client)
1160
{
1161 1162 1163 1164
    //if (client->xkb) {
      //  XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        //client->xkb = NULL;
    //}
Daiki Ueno's avatar
Daiki Ueno committed
1165
}
1166
//#endif  /* HAVE_XTEST */