client.c 34.9 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

Daiki Ueno's avatar
Daiki Ueno committed
36
37
38
39
#ifdef HAVE_IBUS
#include <ibus.h>
#endif  /* HAVE_IBUS */

40
41
#include "eek/eek.h"
#include "eek/eek-xkl.h"
42
#include "eekboard/eekboard.h"
43
#include "client.h"
Daiki Ueno's avatar
Daiki Ueno committed
44
#include "xklutil.h"
45

46
47
#include <string.h>

48
49
50
51
52
53
#define CSW 640
#define CSH 480

enum {
    PROP_0,
    PROP_CONNECTION,
Daiki Ueno's avatar
Daiki Ueno committed
54
    PROP_EEKBOARD,
55
    PROP_CONTEXT,
56
57
58
    PROP_LAST
};

59
typedef struct _EekboardClientClass EekboardClientClass;
60

61
struct _EekboardClient {
62
63
    GObject parent;

64
    EekboardEekboard *eekboard;
65
    EekboardContext *context;
66

67
    EekKeyboard *keyboard;
68
69
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;
70
71
    gboolean use_xkl_layout;
    gint group;
72

73
74
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
75

76
77
    gulong key_pressed_handler;
    gulong key_released_handler;
78

Daiki Ueno's avatar
Daiki Ueno committed
79
    gboolean follows_focus;
Daiki Ueno's avatar
Daiki Ueno committed
80
    guint hide_keyboard_timeout_id;
Daiki Ueno's avatar
Daiki Ueno committed
81

Daiki Ueno's avatar
Daiki Ueno committed
82
83
84
85
#ifdef HAVE_ATSPI
    AtspiAccessible *acc;
    AtspiDeviceListener *keystroke_listener;
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
86

Daiki Ueno's avatar
Daiki Ueno committed
87
88
89
90
91
#ifdef HAVE_IBUS
    IBusBus *ibus_bus;
    guint ibus_focus_message_filter;
#endif  /* HAVE_IBUS */

92
93
#ifdef HAVE_XTEST
    KeyCode modifier_keycodes[8]; 
94
    XkbDescRec *xkb;
95
#endif  /* HAVE_XTEST */
96
97

    GSettings *settings;
98
99
};

100
struct _EekboardClientClass {
101
102
103
    GObjectClass parent_class;
};

104
G_DEFINE_TYPE (EekboardClient, eekboard_client, G_TYPE_OBJECT);
105

Daiki Ueno's avatar
Daiki Ueno committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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_keyboard         (EekboardClient         *client,
                                             gboolean                show,
                                             EekLayout              *layout);
128
129
130
131
132
static gboolean        set_keyboard_from_xkl (EekboardClient         *client,
                                              gboolean                show,
                                              const gchar            *model,
                                              const gchar            *layouts,
                                              const gchar            *options);
133
134
135
136
#ifdef HAVE_XTEST
static void            update_modifier_keycodes
                                            (EekboardClient         *client);
#endif  /* HAVE_XTEST */
137
138

static void
139
eekboard_client_set_property (GObject      *object,
140
141
142
143
                                      guint         prop_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
{
144
    EekboardClient *client = EEKBOARD_CLIENT(object);
145
146
147
148
149
    GDBusConnection *connection;

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

151
        client->eekboard = eekboard_eekboard_new (connection, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
152
153
154
        if (client->eekboard != NULL) {
            client->context =
                eekboard_eekboard_create_context (client->eekboard,
155
                                                  "eekboard",
Daiki Ueno's avatar
Daiki Ueno committed
156
157
158
159
160
161
162
163
164
                                                  NULL);
            if (client->context == NULL) {
                g_object_unref (client->eekboard);
                client->eekboard = NULL;
            } else
                eekboard_eekboard_push_context (client->eekboard,
                                                client->context,
                                                NULL);
        }
165
166
167
168
169
170
171
172
173
        break;
    default:
        g_object_set_property (object,
                               g_param_spec_get_name (pspec),
                               value);
        break;
    }
}

174
static void
175
eekboard_client_get_property (GObject    *object,
176
177
178
                              guint       prop_id,
                              GValue     *value,
                              GParamSpec *pspec)
179
{
180
    EekboardClient *client = EEKBOARD_CLIENT(object);
181
182

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
183
184
185
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
186
187
188
189
190
191
192
193
194
195
196
    case PROP_CONTEXT:
        g_value_set_object (value, client->context);
        break;
    default:
        g_object_get_property (object,
                               g_param_spec_get_name (pspec),
                               value);
        break;
    }
}

197
static void
198
eekboard_client_dispose (GObject *object)
199
{
200
    EekboardClient *client = EEKBOARD_CLIENT(object);
201

202
    eekboard_client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
203

Daiki Ueno's avatar
Daiki Ueno committed
204
205
206
207
#ifdef HAVE_ATSPI
    eekboard_client_disable_atspi_focus (client);
    eekboard_client_disable_atspi_keystroke (client);
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
208

Daiki Ueno's avatar
Daiki Ueno committed
209
210
211
212
213
214
215
216
#ifdef HAVE_IBUS
    eekboard_client_disable_ibus_focus (client);
    if (client->ibus_bus) {
        g_object_unref (client->ibus_bus);
        client->ibus_bus = NULL;
    }
#endif  /* HAVE_IBUS */

217
218
219
#ifdef HAVE_XTEST
    eekboard_client_disable_xtest (client);
#endif  /* HAVE_XTEST */
220

221
    if (client->context) {
222
223
        if (client->eekboard) {
            eekboard_eekboard_pop_context (client->eekboard, NULL);
224
225
226
227
228
229
        }

        g_object_unref (client->context);
        client->context = NULL;
    }

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

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

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

245
    G_OBJECT_CLASS (eekboard_client_parent_class)->dispose (object);
246
247
248
}

static void
249
eekboard_client_class_init (EekboardClientClass *klass)
250
251
252
253
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

254
255
256
    gobject_class->set_property = eekboard_client_set_property;
    gobject_class->get_property = eekboard_client_get_property;
    gobject_class->dispose = eekboard_client_dispose;
257
258
259
260
261
262
263
264
265

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

Daiki Ueno's avatar
Daiki Ueno committed
267
268
269
270
271
272
273
274
275
    pspec = g_param_spec_object ("eekboard",
                                 "Eekboard",
                                 "Eekboard",
                                 EEKBOARD_TYPE_EEKBOARD,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_EEKBOARD,
                                     pspec);

276
277
278
279
280
281
282
283
    pspec = g_param_spec_object ("context",
                                 "Context",
                                 "Context",
                                 EEKBOARD_TYPE_CONTEXT,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_CONTEXT,
                                     pspec);
284
285
286
}

static void
287
eekboard_client_init (EekboardClient *client)
288
{
289
    client->eekboard = NULL;
290
    client->context = NULL;
291
292
    client->xkl_engine = NULL;
    client->xkl_config_registry = NULL;
293
    client->keyboard = NULL;
294
295
296
297
    client->key_pressed_handler = 0;
    client->key_released_handler = 0;
    client->xkl_config_changed_handler = 0;
    client->xkl_state_changed_handler = 0;
Daiki Ueno's avatar
Daiki Ueno committed
298
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
299
    client->follows_focus = FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
300
    client->hide_keyboard_timeout_id = 0;
Daiki Ueno's avatar
Daiki Ueno committed
301
302
#endif  /* ENABLE_FOCUS_LISTENER */
#ifdef HAVE_ATSPI
Daiki Ueno's avatar
Daiki Ueno committed
303
    client->keystroke_listener = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
304
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
305
306
307
308
#ifdef HAVE_IBUS
    client->ibus_bus = NULL;
    client->ibus_focus_message_filter = 0;
#endif  /* HAVE_IBUS */
309
    client->settings = g_settings_new ("org.fedorahosted.eekboard");
310
311
}

312
gboolean
313
314
315
316
eekboard_client_load_keyboard_from_xkl (EekboardClient *client,
                                        const gchar    *model,
                                        const gchar    *layouts,
                                        const gchar    *options)
317
{
318
319
    client->use_xkl_layout = TRUE;

Daiki Ueno's avatar
Daiki Ueno committed
320
#if ENABLE_FOCUS_LISTENER
321
322
323
324
325
    return set_keyboard_from_xkl (client,
                                  !client->follows_focus,
                                  model,
                                  layouts,
                                  options);
Daiki Ueno's avatar
Daiki Ueno committed
326
#else  /* ENABLE_FOCUS_LISTENER */
327
328
329
330
331
    return set_keyboard_from_xkl (client,
                                  TRUE,
                                  model,
                                  layouts,
                                  options);
Daiki Ueno's avatar
Daiki Ueno committed
332
#endif  /* !ENABLE_FOCUS_LISTENER */
333
334
}

335
gboolean
336
eekboard_client_enable_xkl (EekboardClient *client)
337
{
Daiki Ueno's avatar
Daiki Ueno committed
338
339
    GdkDisplay *display = gdk_display_get_default ();
    g_assert (display);
340

341
342
    if (!client->xkl_engine) {
        client->xkl_engine =
Daiki Ueno's avatar
Daiki Ueno committed
343
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (display));
344
    }
345
    g_assert (client->xkl_engine);
346
347
348
349
350
351
352

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

353
    client->xkl_config_changed_handler =
354
355
        g_signal_connect (client->xkl_engine, "X-config-changed",
                          G_CALLBACK(on_xkl_config_changed), client);
356
    client->xkl_state_changed_handler =
357
358
359
360
361
362
363
364
365
366
        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);

367
368
    client->use_xkl_layout = FALSE;

369
370
    xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);

371
    return TRUE;
372
373
374
}

void
375
eekboard_client_disable_xkl (EekboardClient *client)
376
{
377
378
    client->use_xkl_layout = FALSE;

379
380
381
    if (client->xkl_engine)
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
    if (g_signal_handler_is_connected (client->xkl_engine,
382
                                       client->xkl_config_changed_handler))
383
        g_signal_handler_disconnect (client->xkl_engine,
384
                                     client->xkl_config_changed_handler);
385
    if (g_signal_handler_is_connected (client->xkl_engine,
386
                                       client->xkl_state_changed_handler))
387
        g_signal_handler_disconnect (client->xkl_engine,
388
                                     client->xkl_state_changed_handler);
389
390
}

Daiki Ueno's avatar
Daiki Ueno committed
391
#ifdef HAVE_ATSPI
392
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
393
eekboard_client_enable_atspi_focus (EekboardClient *client)
394
{
Daiki Ueno's avatar
Daiki Ueno committed
395
    GError *error;
396

Daiki Ueno's avatar
Daiki Ueno committed
397
398
399
400
401
402
403
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "object:state-changed:focused",
         &error))
404
405
        return FALSE;

Daiki Ueno's avatar
Daiki Ueno committed
406
407
408
409
410
411
412
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "focus:",
         &error))
413
414
        return FALSE;

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
eekboard_client_disable_atspi_focus (EekboardClient *client)
421
{
Daiki Ueno's avatar
Daiki Ueno committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
    GError *error;

    client->follows_focus = FALSE;

    error = NULL;
    atspi_event_listener_deregister_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         "object:state-changed:focused",
         &error);

    error = NULL;
    atspi_event_listener_deregister_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         "focus:",
         &error);
439
440
441
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
442
eekboard_client_enable_atspi_keystroke (EekboardClient *client)
443
{
Daiki Ueno's avatar
Daiki Ueno committed
444
445
    GError *error;

446
    client->keystroke_listener =
Daiki Ueno's avatar
Daiki Ueno committed
447
448
449
        atspi_device_listener_new ((AtspiDeviceListenerCB)keystroke_listener_cb,
                                   NULL,
                                   client);
450

Daiki Ueno's avatar
Daiki Ueno committed
451
452
453
454
455
456
457
458
459
460
461
462
    error = NULL;
    if (!atspi_register_keystroke_listener
        (client->keystroke_listener,
         NULL,
         0,
         ATSPI_KEY_PRESSED,
         ATSPI_KEYLISTENER_NOSYNC,
         &error))
        return FALSE;

    error = NULL;
    if (!atspi_register_keystroke_listener
463
        (client->keystroke_listener,
Daiki Ueno's avatar
Daiki Ueno committed
464
         NULL,
465
         0,
Daiki Ueno's avatar
Daiki Ueno committed
466
467
468
         ATSPI_KEY_RELEASED,
         ATSPI_KEYLISTENER_NOSYNC,
         &error))
469
470
471
472
473
        return FALSE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
474
eekboard_client_disable_atspi_keystroke (EekboardClient *client)
475
476
{
    if (client->keystroke_listener) {
Daiki Ueno's avatar
Daiki Ueno committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
        GError *error;

        error = NULL;
        atspi_deregister_keystroke_listener (client->keystroke_listener,
                                             NULL,
                                             0,
                                             ATSPI_KEY_PRESSED,
                                             &error);

        error = NULL;
        atspi_deregister_keystroke_listener (client->keystroke_listener,
                                             NULL,
                                             0,
                                             ATSPI_KEY_RELEASED,
                                             &error);

        g_object_unref (client->keystroke_listener);
494
495
496
497
        client->keystroke_listener = NULL;
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
498
499
500
static void
focus_listener_cb (const AtspiEvent *event,
                   void             *user_data)
501
{
502
    EekboardClient *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
503
504
505
506
507
508
509
510
511
    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 (error)
        return;
512

Daiki Ueno's avatar
Daiki Ueno committed
513
514
    if (atspi_state_set_contains (state_set, ATSPI_STATE_EDITABLE) ||
        role == ATSPI_ROLE_TERMINAL) {
515
        switch (role) {
Daiki Ueno's avatar
Daiki Ueno committed
516
517
518
519
        case ATSPI_ROLE_TEXT:
        case ATSPI_ROLE_PARAGRAPH:
        case ATSPI_ROLE_PASSWORD_TEXT:
        case ATSPI_ROLE_TERMINAL:
Daiki Ueno's avatar
Daiki Ueno committed
520
521
522
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
                eekboard_context_show_keyboard (client->context, NULL);
523
524
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0 && accessible == client->acc) {
Daiki Ueno's avatar
Daiki Ueno committed
525
526
527
528
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
            }
            break;
Daiki Ueno's avatar
Daiki Ueno committed
529
        case ATSPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
530
531
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
532
                eekboard_context_show_keyboard (client->context, NULL);
533
534
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
535
536
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
537
            }
Daiki Ueno's avatar
Daiki Ueno committed
538
539
            break;
            
540
541
542
        default:
            ;
        }
543
544
545
    } else {
        eekboard_context_hide_keyboard (client->context, NULL);
    }
546
547
}

Daiki Ueno's avatar
Daiki Ueno committed
548
549
550
static gboolean
keystroke_listener_cb (const AtspiDeviceEvent *stroke,
                       void                   *user_data)
551
{
552
    EekboardClient *client = user_data;
553
554
555
556
    EekKey *key;

    /* Ignore modifiers since the keystroke listener does not called
       when a modifier key is released. */
557
    key = eek_keyboard_find_key_by_keycode (client->keyboard,
Daiki Ueno's avatar
Daiki Ueno committed
558
                                            stroke->hw_code);
559
560
561
562
563
    if (key) {
        EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
        if (symbol && eek_symbol_is_modifier (symbol))
            return FALSE;
    }
564

Daiki Ueno's avatar
Daiki Ueno committed
565
566
    if (stroke->type == ATSPI_KEY_PRESSED) {
        eekboard_context_press_key (client->context, stroke->hw_code, NULL);
567
    } else {
Daiki Ueno's avatar
Daiki Ueno committed
568
        eekboard_context_release_key (client->context, stroke->hw_code, NULL);
569
570
    }

571
572
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
573
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
574

Daiki Ueno's avatar
Daiki Ueno committed
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
#ifdef HAVE_IBUS
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;
  g_dbus_connection_send_message (connection,
                                  message,
                                  G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                  NULL,
                                  &error);
  g_object_unref (message);
}

Daiki Ueno's avatar
Daiki Ueno committed
597
598
599
600
601
602
603
604
static gboolean
on_hide_keyboard_timeout (EekboardClient *client)
{
    eekboard_context_hide_keyboard (client->context, NULL);
    client->hide_keyboard_timeout_id = 0;
    return FALSE;
}

Daiki Ueno's avatar
Daiki Ueno committed
605
606
607
608
609
610
611
612
613
614
static GDBusMessage *
focus_message_filter (GDBusConnection *connection,
                      GDBusMessage    *message,
                      gboolean         incoming,
                      gpointer         user_data)
{
    EekboardClient *client = user_data;

    if (incoming &&
        g_strcmp0 (g_dbus_message_get_interface (message),
615
                   IBUS_INTERFACE_PANEL) == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
616
617
618
        const gchar *member = g_dbus_message_get_member (message);

        if (g_strcmp0 (member, "FocusIn") == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
619
620
621
622
            if (client->hide_keyboard_timeout_id > 0) {
                g_source_remove (client->hide_keyboard_timeout_id);
                client->hide_keyboard_timeout_id = 0;
            }
Daiki Ueno's avatar
Daiki Ueno committed
623
            eekboard_context_show_keyboard (client->context, NULL);
624
625
        } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                   g_strcmp0 (member, "FocusOut") == 0) {
626
627
            gint delay = g_settings_get_int (client->settings,
                                             "auto-hide-delay");
Daiki Ueno's avatar
Daiki Ueno committed
628
            client->hide_keyboard_timeout_id =
629
                g_timeout_add (delay,
Daiki Ueno's avatar
Daiki Ueno committed
630
631
                               (GSourceFunc)on_hide_keyboard_timeout,
                               client);
Daiki Ueno's avatar
Daiki Ueno committed
632
633
634
635
636
637
        }
    }

    return message;
}

638
639
static void
_ibus_connect_focus_handlers (IBusBus *bus, gpointer user_data)
Daiki Ueno's avatar
Daiki Ueno committed
640
{
641
    EekboardClient *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
642
643
    GDBusConnection *connection;

644
    connection = ibus_bus_get_connection (bus);
Daiki Ueno's avatar
Daiki Ueno committed
645
646
    add_match_rule (connection,
                    "type='method_call',"
647
                    "interface='" IBUS_INTERFACE_PANEL "',"
Daiki Ueno's avatar
Daiki Ueno committed
648
                    "member='FocusIn'");
649
650
    add_match_rule (connection,
                    "type='method_call',"
651
                    "interface='" IBUS_INTERFACE_PANEL "',"
652
                    "member='FocusOut'");
Daiki Ueno's avatar
Daiki Ueno committed
653
654
655
656
657
    client->ibus_focus_message_filter =
        g_dbus_connection_add_filter (connection,
                                      focus_message_filter,
                                      client,
                                      NULL);
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
}

gboolean
eekboard_client_enable_ibus_focus (EekboardClient *client)
{
    if (!client->ibus_bus) {
        client->ibus_bus = ibus_bus_new ();
        g_object_ref (client->ibus_bus);
        g_signal_connect (client->ibus_bus, "connected",
                          G_CALLBACK(_ibus_connect_focus_handlers),
                          client);
    }

    if (ibus_bus_is_connected (client->ibus_bus))
        _ibus_connect_focus_handlers (client->ibus_bus, client);

Daiki Ueno's avatar
Daiki Ueno committed
674
675
676
677
678
679
680
681
682
683
684
    client->follows_focus = TRUE;
    return TRUE;
}

void
eekboard_client_disable_ibus_focus (EekboardClient *client)
{
    GDBusConnection *connection;

    client->follows_focus = FALSE;

685
686
687
688
689
690
691
692
693
    if (client->ibus_bus) {
        if (client->ibus_focus_message_filter != 0) {
            connection = ibus_bus_get_connection (client->ibus_bus);
            g_dbus_connection_remove_filter (connection,
                                             client->ibus_focus_message_filter);
        }
        g_object_unref (client->ibus_bus);
        client->ibus_bus = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
694
695
696
}
#endif  /* HAVE_ATSPI */

697
698
EekboardClient *
eekboard_client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
699
{
700
    EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT,
701
702
                                           "connection", connection,
                                           NULL);
Daiki Ueno's avatar
Daiki Ueno committed
703
704
705
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
706
}
707
708
709
710
711
712

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
713
    EekboardClient *client = user_data;
714
715
716
717
718
719
720
721
722
723
    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)
{
724
    EekboardClient *client = user_data;
725
    gboolean retval;
726

727
728
729
730
    if (client->use_xkl_layout) {
        retval = set_keyboard_from_xkl (client, FALSE, NULL, NULL, NULL);
        g_return_if_fail (retval);
    }
731

732
#ifdef HAVE_XTEST
733
    update_modifier_keycodes (client);
734
#endif  /* HAVE_XTEST */
735
736
}

737
static gboolean
738
set_keyboard (EekboardClient *client,
Daiki Ueno's avatar
Daiki Ueno committed
739
740
              gboolean        show,
              EekLayout      *layout)
741
742
743
{
    gchar *keyboard_name;
    static gint keyboard_serial = 0;
744
    guint keyboard_id;
745

Daiki Ueno's avatar
Daiki Ueno committed
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
    client->keyboard = eek_keyboard_new (layout, CSW, CSH);
    eek_keyboard_set_modifier_behavior (client->keyboard,
                                        EEK_MODIFIER_BEHAVIOR_LATCH);

    keyboard_name = g_strdup_printf ("keyboard%d", keyboard_serial++);
    eek_element_set_name (EEK_ELEMENT(client->keyboard), keyboard_name);
    g_free (keyboard_name);

    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 client->keyboard,
                                                 NULL);
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
    if (show)
        eekboard_context_show_keyboard (client->context, NULL);
    return TRUE;
}

static gboolean
764
765
766
767
768
set_keyboard_from_xkl (EekboardClient *client,
                       gboolean        show,
                       const gchar    *model,
                       const gchar    *layouts,
                       const gchar    *options)
Daiki Ueno's avatar
Daiki Ueno committed
769
770
771
772
{
    EekLayout *layout;
    gboolean retval;

773
774
    if (client->keyboard)
        g_object_unref (client->keyboard);
775
    layout = eek_xkl_layout_new ();
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805

    if (model) {
        if (!eek_xkl_layout_set_model (EEK_XKL_LAYOUT(layout), model)) {
            g_object_unref (layout);
            return FALSE;
        }
    }

    if (layouts) {
        XklConfigRec *rec;

        rec = eekboard_xkl_config_rec_new_from_string (layouts);
        if (!eek_xkl_layout_set_layouts (EEK_XKL_LAYOUT(layout),
                                         rec->layouts)) {
            g_object_unref (rec);
            g_object_unref (layout);
            return FALSE;
        }

        if (!eek_xkl_layout_set_variants (EEK_XKL_LAYOUT(layout),
                                          rec->variants)) {
            g_object_unref (rec);
            g_object_unref (layout);
            return FALSE;
        }            

        g_object_unref (rec);
    }

    if (options) {
Daiki Ueno's avatar
Daiki Ueno committed
806
        gchar **_options;
807

Daiki Ueno's avatar
Daiki Ueno committed
808
809
810
        _options = g_strsplit (options, ",", -1);
        if (!eek_xkl_layout_set_options (EEK_XKL_LAYOUT(layout), _options)) {
            g_strfreev (_options);
811
812
813
814
815
            g_object_unref (layout);
            return FALSE;
        }
    }

Daiki Ueno's avatar
Daiki Ueno committed
816
817
818
    retval = set_keyboard (client, show, layout);
    g_object_unref (layout);
    return retval;
819
820
821
822
823
824
825
826
827
}

static void
on_xkl_state_changed (XklEngine           *xklengine,
                      XklEngineStateChange type,
                      gint                 value,
                      gboolean             restore,
                      gpointer             user_data)
{
828
    EekboardClient *client = user_data;
829

830
    if (type == GROUP_CHANGED && client->keyboard) {
831
832
833
834
835
        if (client->use_xkl_layout) {
            gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard));
            if (group != value) {
                eekboard_context_set_group (client->context, value, NULL);
            }
836
        }
837
        client->group = value;
838
839
840
    }
}

841
#ifdef HAVE_XTEST
842
843
844
845
846
847
848
849
850
851
/* 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 (EekboardClient *client)
{
Daiki Ueno's avatar
Daiki Ueno committed
852
853
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
854
855
856
857
    gint i;

    for (i = client->xkb->max_key_code; i >= client->xkb->min_key_code; --i)
        if (client->xkb->map->key_sym_map[i].kt_index[0] == XkbOneLevelIndex &&
Daiki Ueno's avatar
Daiki Ueno committed
858
            XKeycodeToKeysym (xdisplay, i, 0) != 0)
859
860
            return i;

Daiki Ueno's avatar
Daiki Ueno committed
861
    return XKeysymToKeycode (xdisplay, 0x0023); /* XK_numbersign */
862
863
864
865
866
867
868
869
870
871
872
873
874
875
}

/* 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 (EekboardClient *client,
                 guint          *keycode,
                 guint          *keysym)
{
Daiki Ueno's avatar
Daiki Ueno committed
876
877
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
878
879
880
881
882
883
884
885
886
887
    guint offset;
    XkbMapChangesRec changes;
    guint replaced_keycode, replaced_keysym;

    g_assert (keycode != NULL);
    g_assert (keysym != NULL && *keysym != 0);

    replaced_keycode = get_replaced_keycode (client);
    if (replaced_keycode == 0)
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
888
889
    replaced_keysym = XKeycodeToKeysym (xdisplay, replaced_keycode, 0);
    XFlush (xdisplay);
890
891
892
893
894
895
896
897

    offset = client->xkb->map->key_sym_map[replaced_keycode].offset;
    client->xkb->map->syms[offset] = *keysym;

    changes.changed = XkbKeySymsMask;
    changes.first_key_sym = replaced_keycode;
    changes.num_key_syms = 1;

Daiki Ueno's avatar
Daiki Ueno committed
898
899
    XkbChangeMap (xdisplay, client->xkb, &changes);
    XFlush (xdisplay);
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931

    *keycode = replaced_keycode;
    *keysym = replaced_keysym;

    return TRUE;
}

static gboolean
get_keycode_from_gdk_keymap (EekboardClient *client,
                             guint           keysym,
                             guint          *keycode,
                             guint          *modifiers)
{
    GdkKeymap *keymap = gdk_keymap_get_default ();
    GdkKeymapKey *keys, *best_match;
    gint n_keys, i;

    if (!gdk_keymap_get_entries_for_keyval (keymap, keysym, &keys, &n_keys))
        return FALSE;

    for (i = 0; i < n_keys; i++)
        if (keys[i].group == client->group)
            best_match = &keys[i];

    *keycode = best_match->keycode;
    *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0;

    g_free (keys);

    return TRUE;
}

932
933
934
935
static void
send_fake_modifier_key_event (EekboardClient *client,
                              EekModifierType modifiers,
                              gboolean        is_pressed)
Daiki Ueno's avatar
Daiki Ueno committed
936
{
Daiki Ueno's avatar
Daiki Ueno committed
937
    GdkDisplay *display = gdk_display_get_default ();
938
    gint i;
Daiki Ueno's avatar
Daiki Ueno committed
939

940
941
942
    for (i = 0; i < G_N_ELEMENTS(client->modifier_keycodes); i++) {
        if (modifiers & (1 << i)) {
            guint keycode = client->modifier_keycodes[i];
Daiki Ueno's avatar
Daiki Ueno committed
943

944
945
            g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
946
            XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
947
948
949
950
951
952
953
954
955
956
957
958
                               keycode,
                               is_pressed,
                               CurrentTime);
        }
    }
}

static void
send_fake_key_event (EekboardClient *client,
                     EekKey         *key,
                     gboolean        is_pressed)
{
Daiki Ueno's avatar
Daiki Ueno committed
959
    GdkDisplay *display = gdk_display_get_default ();
960
    EekSymbol *symbol;
961
    EekModifierType keyboard_modifiers, modifiers;
962
    guint xkeysym;
963
    guint keycode, replaced_keysym = 0;
964
965
966
967
968
969
970
971
972
973

    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);

    /* Ignore special keys and modifiers */
    if (!EEK_IS_KEYSYM(symbol) || eek_symbol_is_modifier (symbol))
        return;

    xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
    g_return_if_fail (xkeysym > 0);

974
975
976
977
978
979
980
981
    modifiers = 0;
    if (!get_keycode_from_gdk_keymap (client, xkeysym, &keycode, &modifiers)) {
        keycode = 0;
        replaced_keysym = xkeysym;
        if (!replace_keycode (client, &keycode, &replaced_keysym)) {
            g_warning ("failed to lookup X keysym %X", xkeysym);
            return;
        }
982
    }
983
984
985
986
987
988
989
990
    
    /* Clear level shift modifiers */
    keyboard_modifiers = eek_keyboard_get_modifiers (client->keyboard);
    keyboard_modifiers &= ~EEK_SHIFT_MASK;
    keyboard_modifiers &= ~EEK_LOCK_MASK;
    keyboard_modifiers &= ~eek_keyboard_get_alt_gr_mask (client->keyboard);

    modifiers |= keyboard_modifiers;
991
992

    send_fake_modifier_key_event (client, modifiers, is_pressed);
Daiki Ueno's avatar
Daiki Ueno committed
993
    XSync (GDK_DISPLAY_XDISPLAY (display), False);
994

Daiki Ueno's avatar
Daiki Ueno committed
995
    keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
996
997
998
                                xkeysym);
    g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
999
    XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
1000
1001
1002
                       keycode,
                       is_pressed,
                       CurrentTime);
Daiki Ueno's avatar
Daiki Ueno committed
1003
    XSync (GDK_DISPLAY_XDISPLAY (display), False);
1004
1005
1006

    if (replaced_keysym)
        replace_keycode (client, &keycode, &replaced_keysym);
Daiki Ueno's avatar
Daiki Ueno committed
1007
1008
}

1009
static void
1010
1011
1012
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
1013
{
1014
    EekboardClient *client = user_data;
1015
    send_fake_key_event (client, key, TRUE);
1016
    send_fake_key_event (client, key, FALSE);
1017
1018
1019
}

static void
1020
1021
1022
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
1023
{
1024
}
1025

1026
1027
1028
static void
update_modifier_keycodes (EekboardClient *client)
{
Daiki Ueno's avatar
Daiki Ueno committed
1029
    GdkDisplay *display = gdk_display_get_default ();
1030
1031
1032
    XModifierKeymap *mods;
    gint i, j;

Daiki Ueno's avatar
Daiki Ueno committed
1033
    mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (display));
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
    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;
            }
        }
    }
1044
1045
1046
}

gboolean
1047
eekboard_client_enable_xtest (EekboardClient *client)
1048
{
Daiki Ueno's avatar
Daiki Ueno committed
1049
    GdkDisplay *display = gdk_display_get_default ();
1050
    int opcode, event_base, error_base, major_version, minor_version;
1051

Daiki Ueno's avatar
Daiki Ueno committed
1052
    g_assert (display);
1053

Daiki Ueno's avatar
Daiki Ueno committed
1054
    if (!XTestQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1055
1056
1057
1058
1059
                              &event_base, &error_base,
                              &major_version, &minor_version)) {
        g_warning ("XTest extension is not available");
        return FALSE;
    }
1060

Daiki Ueno's avatar
Daiki Ueno committed
1061
    if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1062
1063
1064
1065
1066
1067
1068
                            &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
1069
        client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (display),
1070
1071
                                 XkbKeySymsMask,
                                 XkbUseCoreKbd);
1072
1073
    g_assert (client->xkb);

1074
    update_modifier_keycodes (client);
1075

1076
    client->key_pressed_handler =
1077
        g_signal_connect (client->keyboard, "key-pressed",
1078
                          G_CALLBACK(on_key_pressed), client);
1079
    client->key_released_handler =
1080
        g_signal_connect (client->keyboard, "key-released",
1081
1082
1083
1084
1085
1086
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
1087
eekboard_client_disable_xtest (EekboardClient *client)
1088
{
1089
1090
1091
1092
1093
    if (client->xkb) {
        XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        client->xkb = NULL;
    }

1094
    if (g_signal_handler_is_connected (client->keyboard,
1095
                                       client->key_pressed_handler))
1096
        g_signal_handler_disconnect (client->keyboard,
1097
                                     client->key_pressed_handler);
1098
    if (g_signal_handler_is_connected (client->keyboard,
1099
                                       client->key_released_handler))
1100
        g_signal_handler_disconnect (client->keyboard,
1101
                                     client->key_released_handler);
1102
}
Daiki Ueno's avatar
Daiki Ueno committed
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122

gboolean
eekboard_client_load_keyboard_from_file (EekboardClient *client,
                                         const gchar    *keyboard_file)
{
    GFile *file;
    GFileInputStream *input;
    GError *error;
    EekLayout *layout;
    gboolean retval;

    file = g_file_new_for_path (keyboard_file);

    error = NULL;
    input = g_file_read (file, NULL, &error);
    if (input == NULL)
        return FALSE;

    layout = eek_xml_layout_new (G_INPUT_STREAM(input));
    g_object_unref (input);
Daiki Ueno's avatar
Daiki Ueno committed
1123
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
1124
    retval = set_keyboard (client, !client->follows_focus, layout);
Daiki Ueno's avatar
Daiki Ueno committed
1125
#else  /* ENABLE_FOCUS_LISTENER */
1126
    retval = set_keyboard (client, TRUE, layout);
Daiki Ueno's avatar
Daiki Ueno committed
1127
#endif  /* !ENABLE_FOCUS_LISTENER */
Daiki Ueno's avatar
Daiki Ueno committed
1128
1129
1130
1131
    g_object_unref (layout);
    return retval;
}

1132
#endif  /* HAVE_XTEST */