client.c 34 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
    GdkDisplay *display;
69
70
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;
71
72
    gboolean use_xkl_layout;
    gint group;
73

74
75
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
76

77
78
    gulong key_pressed_handler;
    gulong key_released_handler;
79

Daiki Ueno's avatar
Daiki Ueno committed
80
81
    gboolean follows_focus;

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->display) {
        gdk_display_close (client->display);
        client->display = NULL;
    }

245
246
247
248
249
    if (client->settings) {
        g_object_unref (client->settings);
        client->settings = NULL;
    }

250
    G_OBJECT_CLASS (eekboard_client_parent_class)->dispose (object);
251
252
253
}

static void
254
eekboard_client_class_init (EekboardClientClass *klass)
255
256
257
258
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

259
260
261
    gobject_class->set_property = eekboard_client_set_property;
    gobject_class->get_property = eekboard_client_get_property;
    gobject_class->dispose = eekboard_client_dispose;
262
263
264
265
266
267
268
269
270

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

Daiki Ueno's avatar
Daiki Ueno committed
272
273
274
275
276
277
278
279
280
    pspec = g_param_spec_object ("eekboard",
                                 "Eekboard",
                                 "Eekboard",
                                 EEKBOARD_TYPE_EEKBOARD,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_EEKBOARD,
                                     pspec);

281
282
283
284
285
286
287
288
    pspec = g_param_spec_object ("context",
                                 "Context",
                                 "Context",
                                 EEKBOARD_TYPE_CONTEXT,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_CONTEXT,
                                     pspec);
289
290
291
}

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

317
gboolean
318
319
320
321
eekboard_client_load_keyboard_from_xkl (EekboardClient *client,
                                        const gchar    *model,
                                        const gchar    *layouts,
                                        const gchar    *options)
322
{
323
324
    client->use_xkl_layout = TRUE;

Daiki Ueno's avatar
Daiki Ueno committed
325
#if ENABLE_FOCUS_LISTENER
326
327
328
329
330
    return set_keyboard_from_xkl (client,
                                  !client->follows_focus,
                                  model,
                                  layouts,
                                  options);
Daiki Ueno's avatar
Daiki Ueno committed
331
#else  /* ENABLE_FOCUS_LISTENER */
332
333
334
335
336
    return set_keyboard_from_xkl (client,
                                  TRUE,
                                  model,
                                  layouts,
                                  options);
Daiki Ueno's avatar
Daiki Ueno committed
337
#endif  /* !ENABLE_FOCUS_LISTENER */
338
339
}

340
gboolean
341
eekboard_client_enable_xkl (EekboardClient *client)
342
{
343
344
345
346
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
347

348
349
350
    if (!client->xkl_engine) {
        client->xkl_engine =
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (client->display));
351
    }
352
    g_assert (client->xkl_engine);
353
354
355
356
357
358
359

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

360
    client->xkl_config_changed_handler =
361
362
        g_signal_connect (client->xkl_engine, "X-config-changed",
                          G_CALLBACK(on_xkl_config_changed), client);
363
    client->xkl_state_changed_handler =
364
365
366
367
368
369
370
371
372
373
        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);

374
375
    client->use_xkl_layout = FALSE;

376
377
    xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);

378
    return TRUE;
379
380
381
}

void
382
eekboard_client_disable_xkl (EekboardClient *client)
383
{
384
385
    client->use_xkl_layout = FALSE;

386
387
388
    if (client->xkl_engine)
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
    if (g_signal_handler_is_connected (client->xkl_engine,
389
                                       client->xkl_config_changed_handler))
390
        g_signal_handler_disconnect (client->xkl_engine,
391
                                     client->xkl_config_changed_handler);
392
    if (g_signal_handler_is_connected (client->xkl_engine,
393
                                       client->xkl_state_changed_handler))
394
        g_signal_handler_disconnect (client->xkl_engine,
395
                                     client->xkl_state_changed_handler);
396
397
}

Daiki Ueno's avatar
Daiki Ueno committed
398
#ifdef HAVE_ATSPI
399
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
400
eekboard_client_enable_atspi_focus (EekboardClient *client)
401
{
Daiki Ueno's avatar
Daiki Ueno committed
402
    GError *error;
403

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,
         "object:state-changed:focused",
         &error))
411
412
        return FALSE;

Daiki Ueno's avatar
Daiki Ueno committed
413
414
415
416
417
418
419
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "focus:",
         &error))
420
421
        return FALSE;

Daiki Ueno's avatar
Daiki Ueno committed
422
    client->follows_focus = TRUE;
423
424
425
426
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
427
eekboard_client_disable_atspi_focus (EekboardClient *client)
428
{
Daiki Ueno's avatar
Daiki Ueno committed
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
    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);
446
447
448
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
449
eekboard_client_enable_atspi_keystroke (EekboardClient *client)
450
{
Daiki Ueno's avatar
Daiki Ueno committed
451
452
    GError *error;

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

Daiki Ueno's avatar
Daiki Ueno committed
458
459
460
461
462
463
464
465
466
467
468
469
    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
470
        (client->keystroke_listener,
Daiki Ueno's avatar
Daiki Ueno committed
471
         NULL,
472
         0,
Daiki Ueno's avatar
Daiki Ueno committed
473
474
475
         ATSPI_KEY_RELEASED,
         ATSPI_KEYLISTENER_NOSYNC,
         &error))
476
477
478
479
480
        return FALSE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
481
eekboard_client_disable_atspi_keystroke (EekboardClient *client)
482
483
{
    if (client->keystroke_listener) {
Daiki Ueno's avatar
Daiki Ueno committed
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
        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);
501
502
503
504
        client->keystroke_listener = NULL;
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
505
506
507
static void
focus_listener_cb (const AtspiEvent *event,
                   void             *user_data)
508
{
509
    EekboardClient *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
510
511
512
513
514
515
516
517
518
    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;
519

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

Daiki Ueno's avatar
Daiki Ueno committed
555
556
557
static gboolean
keystroke_listener_cb (const AtspiDeviceEvent *stroke,
                       void                   *user_data)
558
{
559
    EekboardClient *client = user_data;
560
561
562
563
    EekKey *key;

    /* Ignore modifiers since the keystroke listener does not called
       when a modifier key is released. */
564
    key = eek_keyboard_find_key_by_keycode (client->keyboard,
Daiki Ueno's avatar
Daiki Ueno committed
565
                                            stroke->hw_code);
566
567
568
569
570
    if (key) {
        EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
        if (symbol && eek_symbol_is_modifier (symbol))
            return FALSE;
    }
571

Daiki Ueno's avatar
Daiki Ueno committed
572
573
    if (stroke->type == ATSPI_KEY_PRESSED) {
        eekboard_context_press_key (client->context, stroke->hw_code, NULL);
574
    } else {
Daiki Ueno's avatar
Daiki Ueno committed
575
        eekboard_context_release_key (client->context, stroke->hw_code, NULL);
576
577
    }

578
579
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
580
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
581

Daiki Ueno's avatar
Daiki Ueno committed
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
610
611
612
613
614
615
616
617
618
#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);
619
620
621
        } 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
622
623
624
625
626
627
628
629
630
631
632
633
        }
    }

    return message;
}

gboolean
eekboard_client_enable_ibus_focus (EekboardClient *client)
{
    GDBusConnection *connection;
    GError *error;

634
635
636
637
638
    if (!client->ibus_bus) {
        client->ibus_bus = ibus_bus_new ();
        g_object_ref_sink (client->ibus_bus);
    }

Daiki Ueno's avatar
Daiki Ueno committed
639
640
641
642
643
    connection = ibus_bus_get_connection (client->ibus_bus);
    add_match_rule (connection,
                    "type='method_call',"
                    "interface='" IBUS_INTERFACE_INPUT_CONTEXT "',"
                    "member='FocusIn'");
644
645
646
647
    add_match_rule (connection,
                    "type='method_call',"
                    "interface='" IBUS_INTERFACE_INPUT_CONTEXT "',"
                    "member='FocusOut'");
Daiki Ueno's avatar
Daiki Ueno committed
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
    client->ibus_focus_message_filter =
        g_dbus_connection_add_filter (connection,
                                      focus_message_filter,
                                      client,
                                      NULL);
    client->follows_focus = TRUE;
    return TRUE;
}

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

    client->follows_focus = FALSE;

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

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

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

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

711
#ifdef HAVE_XTEST
712
    update_modifier_keycodes (client);
713
#endif  /* HAVE_XTEST */
714
715
}

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

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

752
753
    if (client->keyboard)
        g_object_unref (client->keyboard);
754
    layout = eek_xkl_layout_new ();
755
756
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

    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
785
        gchar **_options;
786

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

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

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

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

820
#ifdef HAVE_XTEST
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
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
/* 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)
{
    Display *display = GDK_DISPLAY_XDISPLAY (client->display);
    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 &&
            XKeycodeToKeysym (display, i, 0) != 0)
            return i;

    return XKeysymToKeycode (display, 0x0023); /* XK_numbersign */
}

/* 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)
{
    Display *display = GDK_DISPLAY_XDISPLAY (client->display);
    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;
    replaced_keysym = XKeycodeToKeysym (display, replaced_keycode, 0);
    XFlush (display);

    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;

    XkbChangeMap (display, client->xkb, &changes);
    XFlush (display);

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

909
910
911
912
static void
send_fake_modifier_key_event (EekboardClient *client,
                              EekModifierType modifiers,
                              gboolean        is_pressed)
Daiki Ueno's avatar
Daiki Ueno committed
913
{
914
    gint i;
Daiki Ueno's avatar
Daiki Ueno committed
915

916
917
918
    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
919

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
            g_return_if_fail (keycode > 0);

            XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (client->display),
                               keycode,
                               is_pressed,
                               CurrentTime);
        }
    }
}

static void
send_fake_key_event (EekboardClient *client,
                     EekKey         *key,
                     gboolean        is_pressed)
{
    EekSymbol *symbol;
936
    EekModifierType keyboard_modifiers, modifiers;
937
    guint xkeysym;
938
    guint keycode, replaced_keysym = 0;
939
940
941
942
943
944
945
946
947
948

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

949
950
951
952
953
954
955
956
    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;
        }
957
    }
958
959
960
961
962
963
964
965
    
    /* 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;
966
967
968
969

    send_fake_modifier_key_event (client, modifiers, is_pressed);
    XSync (GDK_DISPLAY_XDISPLAY (client->display), False);

970
971
972
973
974
975
976
977
978
    keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (client->display),
                                xkeysym);
    g_return_if_fail (keycode > 0);

    XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (client->display),
                       keycode,
                       is_pressed,
                       CurrentTime);
    XSync (GDK_DISPLAY_XDISPLAY (client->display), False);
979
980
981

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

984
static void
985
986
987
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
988
{
989
    EekboardClient *client = user_data;
990
    send_fake_key_event (client, key, TRUE);
991
    send_fake_key_event (client, key, FALSE);
992
993
994
}

static void
995
996
997
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
998
{
999
}
1000

1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
static void
update_modifier_keycodes (EekboardClient *client)
{
    XModifierKeymap *mods;
    gint i, j;

    mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (client->display));
    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;
            }
        }
    }
1018
1019
1020
}

gboolean
1021
eekboard_client_enable_xtest (EekboardClient *client)
1022
{
1023
    int opcode, event_base, error_base, major_version, minor_version;
1024

1025
1026
1027
1028
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
1029

1030
1031
1032
1033
1034
1035
    if (!XTestQueryExtension (GDK_DISPLAY_XDISPLAY (client->display),
                              &event_base, &error_base,
                              &major_version, &minor_version)) {
        g_warning ("XTest extension is not available");
        return FALSE;
    }
1036
1037
1038
1039
1040
1041
1042
1043
1044

    if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (client->display),
                            &opcode, &event_base, &error_base,
                            &major_version, &minor_version)) {
        g_warning ("Xkb extension is not available");
        return FALSE;
    }

    if (!client->xkb)
1045
1046
1047
        client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (client->display),
                                 XkbKeySymsMask,
                                 XkbUseCoreKbd);
1048
1049
    g_assert (client->xkb);

1050
    update_modifier_keycodes (client);
1051

1052
    client->key_pressed_handler =
1053
        g_signal_connect (client->keyboard, "key-pressed",
1054
                          G_CALLBACK(on_key_pressed), client);
1055
    client->key_released_handler =
1056
        g_signal_connect (client->keyboard, "key-released",
1057
1058
1059
1060
1061
1062
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
1063
eekboard_client_disable_xtest (EekboardClient *client)
1064
{
1065
1066
1067
1068
1069
    if (client->xkb) {
        XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        client->xkb = NULL;
    }

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

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

1108
#endif  /* HAVE_XTEST */