client.c 34.2 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
80
    gboolean follows_focus;

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

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

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

    GSettings *settings;
97
98
};

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

103
G_DEFINE_TYPE (EekboardClient, eekboard_client, G_TYPE_OBJECT);
104

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

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

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

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

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

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
182
183
184
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
185
186
187
188
189
190
191
192
193
194
195
    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;
    }
}

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

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

Daiki Ueno's avatar
Daiki Ueno committed
203
204
205
206
#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
207

Daiki Ueno's avatar
Daiki Ueno committed
208
209
210
211
212
213
214
215
#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 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

365
366
    client->use_xkl_layout = FALSE;

367
368
    xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);

369
    return TRUE;
370
371
372
}

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

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

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

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

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

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

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

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

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

Daiki Ueno's avatar
Daiki Ueno committed
449
450
451
452
453
454
455
456
457
458
459
460
    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
461
        (client->keystroke_listener,
Daiki Ueno's avatar
Daiki Ueno committed
462
         NULL,
463
         0,
Daiki Ueno's avatar
Daiki Ueno committed
464
465
466
         ATSPI_KEY_RELEASED,
         ATSPI_KEYLISTENER_NOSYNC,
         &error))
467
468
469
470
471
        return FALSE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
472
eekboard_client_disable_atspi_keystroke (EekboardClient *client)
473
474
{
    if (client->keystroke_listener) {
Daiki Ueno's avatar
Daiki Ueno committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
        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);
492
493
494
495
        client->keystroke_listener = NULL;
    }
}

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

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

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

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

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

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

Daiki Ueno's avatar
Daiki Ueno committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
#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);
}

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),
                   IBUS_INTERFACE_INPUT_CONTEXT) == 0) {
        const gchar *member = g_dbus_message_get_member (message);

        if (g_strcmp0 (member, "FocusIn") == 0) {
            eekboard_context_show_keyboard (client->context, NULL);
610
611
612
        } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                   g_strcmp0 (member, "FocusOut") == 0) {
            eekboard_context_hide_keyboard (client->context, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
613
614
615
616
617
618
        }
    }

    return message;
}

619
620
static void
_ibus_connect_focus_handlers (IBusBus *bus, gpointer user_data)
Daiki Ueno's avatar
Daiki Ueno committed
621
{
622
    EekboardClient *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
623
624
    GDBusConnection *connection;

625
    connection = ibus_bus_get_connection (bus);
Daiki Ueno's avatar
Daiki Ueno committed
626
627
628
629
    add_match_rule (connection,
                    "type='method_call',"
                    "interface='" IBUS_INTERFACE_INPUT_CONTEXT "',"
                    "member='FocusIn'");
630
631
632
633
    add_match_rule (connection,
                    "type='method_call',"
                    "interface='" IBUS_INTERFACE_INPUT_CONTEXT "',"
                    "member='FocusOut'");
Daiki Ueno's avatar
Daiki Ueno committed
634
635
636
637
638
    client->ibus_focus_message_filter =
        g_dbus_connection_add_filter (connection,
                                      focus_message_filter,
                                      client,
                                      NULL);
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
}

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
655
656
657
658
659
660
661
662
663
664
665
    client->follows_focus = TRUE;
    return TRUE;
}

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

    client->follows_focus = FALSE;

666
667
668
669
670
671
672
673
674
    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
675
676
677
}
#endif  /* HAVE_ATSPI */

678
679
EekboardClient *
eekboard_client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
680
{
681
    EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT,
682
683
                                           "connection", connection,
                                           NULL);
Daiki Ueno's avatar
Daiki Ueno committed
684
685
686
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
687
}
688
689
690
691
692
693

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
694
    EekboardClient *client = user_data;
695
696
697
698
699
700
701
702
703
704
    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)
{
705
    EekboardClient *client = user_data;
706
    gboolean retval;
707

708
709
710
711
    if (client->use_xkl_layout) {
        retval = set_keyboard_from_xkl (client, FALSE, NULL, NULL, NULL);
        g_return_if_fail (retval);
    }
712

713
#ifdef HAVE_XTEST
714
    update_modifier_keycodes (client);
715
#endif  /* HAVE_XTEST */
716
717
}

718
static gboolean
719
set_keyboard (EekboardClient *client,
Daiki Ueno's avatar
Daiki Ueno committed
720
721
              gboolean        show,
              EekLayout      *layout)
722
723
724
{
    gchar *keyboard_name;
    static gint keyboard_serial = 0;
725
    guint keyboard_id;
726

Daiki Ueno's avatar
Daiki Ueno committed
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    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
745
746
747
748
749
set_keyboard_from_xkl (EekboardClient *client,
                       gboolean        show,
                       const gchar    *model,
                       const gchar    *layouts,
                       const gchar    *options)
Daiki Ueno's avatar
Daiki Ueno committed
750
751
752
753
{
    EekLayout *layout;
    gboolean retval;

754
755
    if (client->keyboard)
        g_object_unref (client->keyboard);
756
    layout = eek_xkl_layout_new ();
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786

    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
787
        gchar **_options;
788

Daiki Ueno's avatar
Daiki Ueno committed
789
790
791
        _options = g_strsplit (options, ",", -1);
        if (!eek_xkl_layout_set_options (EEK_XKL_LAYOUT(layout), _options)) {
            g_strfreev (_options);
792
793
794
795
796
            g_object_unref (layout);
            return FALSE;
        }
    }

Daiki Ueno's avatar
Daiki Ueno committed
797
798
799
    retval = set_keyboard (client, show, layout);
    g_object_unref (layout);
    return retval;
800
801
802
803
804
805
806
807
808
}

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

811
    if (type == GROUP_CHANGED && client->keyboard) {
812
813
814
815
816
        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);
            }
817
        }
818
        client->group = value;
819
820
821
    }
}

822
#ifdef HAVE_XTEST
823
824
825
826
827
828
829
830
831
832
/* 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
833
834
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
835
836
837
838
    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
839
            XKeycodeToKeysym (xdisplay, i, 0) != 0)
840
841
            return i;

Daiki Ueno's avatar
Daiki Ueno committed
842
    return XKeysymToKeycode (xdisplay, 0x0023); /* XK_numbersign */
843
844
845
846
847
848
849
850
851
852
853
854
855
856
}

/* 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
857
858
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
859
860
861
862
863
864
865
866
867
868
    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
869
870
    replaced_keysym = XKeycodeToKeysym (xdisplay, replaced_keycode, 0);
    XFlush (xdisplay);
871
872
873
874
875
876
877
878

    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
879
880
    XkbChangeMap (xdisplay, client->xkb, &changes);
    XFlush (xdisplay);
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912

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

913
914
915
916
static void
send_fake_modifier_key_event (EekboardClient *client,
                              EekModifierType modifiers,
                              gboolean        is_pressed)
Daiki Ueno's avatar
Daiki Ueno committed
917
{
Daiki Ueno's avatar
Daiki Ueno committed
918
    GdkDisplay *display = gdk_display_get_default ();
919
    gint i;
Daiki Ueno's avatar
Daiki Ueno committed
920

921
922
923
    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
924

925
926
            g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
927
            XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
928
929
930
931
932
933
934
935
936
937
938
939
                               keycode,
                               is_pressed,
                               CurrentTime);
        }
    }
}

static void
send_fake_key_event (EekboardClient *client,
                     EekKey         *key,
                     gboolean        is_pressed)
{
Daiki Ueno's avatar
Daiki Ueno committed
940
    GdkDisplay *display = gdk_display_get_default ();
941
    EekSymbol *symbol;
942
    EekModifierType keyboard_modifiers, modifiers;
943
    guint xkeysym;
944
    guint keycode, replaced_keysym = 0;
945
946
947
948
949
950
951
952
953
954

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

955
956
957
958
959
960
961
962
    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;
        }
963
    }
964
965
966
967
968
969
970
971
    
    /* 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;
972
973

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

Daiki Ueno's avatar
Daiki Ueno committed
976
    keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
977
978
979
                                xkeysym);
    g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
980
    XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
981
982
983
                       keycode,
                       is_pressed,
                       CurrentTime);
Daiki Ueno's avatar
Daiki Ueno committed
984
    XSync (GDK_DISPLAY_XDISPLAY (display), False);
985
986
987

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

990
static void
991
992
993
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
994
{
995
    EekboardClient *client = user_data;
996
    send_fake_key_event (client, key, TRUE);
997
    send_fake_key_event (client, key, FALSE);
998
999
1000
}

static void
1001
1002
1003
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
1004
{
1005
}
1006

1007
1008
1009
static void
update_modifier_keycodes (EekboardClient *client)
{
Daiki Ueno's avatar
Daiki Ueno committed
1010
    GdkDisplay *display = gdk_display_get_default ();
1011
1012
1013
    XModifierKeymap *mods;
    gint i, j;

Daiki Ueno's avatar
Daiki Ueno committed
1014
    mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (display));
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
    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;
            }
        }
    }
1025
1026
1027
}

gboolean
1028
eekboard_client_enable_xtest (EekboardClient *client)
1029
{
Daiki Ueno's avatar
Daiki Ueno committed
1030
    GdkDisplay *display = gdk_display_get_default ();
1031
    int opcode, event_base, error_base, major_version, minor_version;
1032

Daiki Ueno's avatar
Daiki Ueno committed
1033
    g_assert (display);
1034

Daiki Ueno's avatar
Daiki Ueno committed
1035
    if (!XTestQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1036
1037
1038
1039
1040
                              &event_base, &error_base,
                              &major_version, &minor_version)) {
        g_warning ("XTest extension is not available");
        return FALSE;
    }
1041

Daiki Ueno's avatar
Daiki Ueno committed
1042
    if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1043
1044
1045
1046
1047
1048
1049
                            &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
1050
        client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (display),
1051
1052
                                 XkbKeySymsMask,
                                 XkbUseCoreKbd);
1053
1054
    g_assert (client->xkb);

1055
    update_modifier_keycodes (client);
1056

1057
    client->key_pressed_handler =
1058
        g_signal_connect (client->keyboard, "key-pressed",
1059
                          G_CALLBACK(on_key_pressed), client);
1060
    client->key_released_handler =
1061
        g_signal_connect (client->keyboard, "key-released",
1062
1063
1064
1065
1066
1067
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
1068
eekboard_client_disable_xtest (EekboardClient *client)
1069
{
1070
1071
1072
1073
1074
    if (client->xkb) {
        XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        client->xkb = NULL;
    }

1075
    if (g_signal_handler_is_connected (client->keyboard,
1076
                                       client->key_pressed_handler))
1077
        g_signal_handler_disconnect (client->keyboard,
1078
                                     client->key_pressed_handler);
1079
    if (g_signal_handler_is_connected (client->keyboard,
1080
                                       client->key_released_handler))
1081
        g_signal_handler_disconnect (client->keyboard,
1082
                                     client->key_released_handler);
1083
}
Daiki Ueno's avatar
Daiki Ueno committed
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103

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
1104
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
1105
    retval = set_keyboard (client, !client->follows_focus, layout);
Daiki Ueno's avatar
Daiki Ueno committed
1106
#else  /* ENABLE_FOCUS_LISTENER */
1107
    retval = set_keyboard (client, TRUE, layout);
Daiki Ueno's avatar
Daiki Ueno committed
1108
#endif  /* !ENABLE_FOCUS_LISTENER */
Daiki Ueno's avatar
Daiki Ueno committed
1109
1110
1111
1112
    g_object_unref (layout);
    return retval;
}

1113
#endif  /* HAVE_XTEST */