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

Daiki Ueno's avatar
Daiki Ueno committed
31
32
33
#ifdef HAVE_FAKEKEY
#include <fakekey/fakekey.h>
#endif  /* HAVE_FAKEKEY */
34

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

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

#define CSW 640
#define CSH 480

enum {
    PROP_0,
    PROP_CONNECTION,
Daiki Ueno's avatar
Daiki Ueno committed
51
    PROP_EEKBOARD,
52
    PROP_CONTEXT,
53
54
55
    PROP_LAST
};

56
typedef struct _EekboardClientClass EekboardClientClass;
57

58
struct _EekboardClient {
59
60
    GObject parent;

61
    EekboardEekboard *eekboard;
62
    EekboardContext *context;
63

64
    EekKeyboard *keyboard;
65
    GdkDisplay *display;
66
67
68
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;

69
70
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
71

72
73
    gulong key_pressed_handler;
    gulong key_released_handler;
74

Daiki Ueno's avatar
Daiki Ueno committed
75
76
77
78
#if ENABLE_FOCUS_LISTENER
    gboolean follows_focus;
#endif  /* ENABLE_FOCUS_LISTENER */

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

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

Daiki Ueno's avatar
Daiki Ueno committed
89
90
91
#ifdef HAVE_FAKEKEY
    FakeKey *fakekey;
#endif  /* HAVE_FAKEKEY */
92
93
};

94
struct _EekboardClientClass {
95
96
97
    GObjectClass parent_class;
};

98
G_DEFINE_TYPE (EekboardClient, eekboard_client, G_TYPE_OBJECT);
99

Daiki Ueno's avatar
Daiki Ueno committed
100
101
102
103
104
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);
static gboolean        set_xkl_keyboard     (EekboardClient         *client,
                                             gboolean                show,
                                             const gchar            *model,
                                             const gchar            *layouts,
                                             const gchar            *options);
127
128

static void
129
eekboard_client_set_property (GObject      *object,
130
131
132
133
                                      guint         prop_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
{
134
    EekboardClient *client = EEKBOARD_CLIENT(object);
135
136
137
138
139
    GDBusConnection *connection;

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

141
        client->eekboard = eekboard_eekboard_new (connection, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
142
143
144
        if (client->eekboard != NULL) {
            client->context =
                eekboard_eekboard_create_context (client->eekboard,
145
                                                  "eekboard",
Daiki Ueno's avatar
Daiki Ueno committed
146
147
148
149
150
151
152
153
154
                                                  NULL);
            if (client->context == NULL) {
                g_object_unref (client->eekboard);
                client->eekboard = NULL;
            } else
                eekboard_eekboard_push_context (client->eekboard,
                                                client->context,
                                                NULL);
        }
155
156
157
158
159
160
161
162
163
        break;
    default:
        g_object_set_property (object,
                               g_param_spec_get_name (pspec),
                               value);
        break;
    }
}

164
static void
165
eekboard_client_get_property (GObject    *object,
166
167
168
169
                                     guint       prop_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
{
170
    EekboardClient *client = EEKBOARD_CLIENT(object);
171
172

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
173
174
175
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
176
177
178
179
180
181
182
183
184
185
186
    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;
    }
}

187
static void
188
eekboard_client_dispose (GObject *object)
189
{
190
    EekboardClient *client = EEKBOARD_CLIENT(object);
191

192
    eekboard_client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
193

Daiki Ueno's avatar
Daiki Ueno committed
194
195
196
197
#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
198

Daiki Ueno's avatar
Daiki Ueno committed
199
200
201
202
203
204
205
206
#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 */

Daiki Ueno's avatar
Daiki Ueno committed
207
#ifdef HAVE_FAKEKEY
208
    eekboard_client_disable_fakekey (client);
Daiki Ueno's avatar
Daiki Ueno committed
209
#endif  /* HAVE_FAKEKEY */
210

211
    if (client->context) {
212
213
        if (client->eekboard) {
            eekboard_eekboard_pop_context (client->eekboard, NULL);
214
215
216
217
218
219
        }

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

220
221
222
    if (client->eekboard) {
        g_object_unref (client->eekboard);
        client->eekboard = NULL;
223
224
    }

225
226
227
228
229
    if (client->keyboard) {
        g_object_unref (client->keyboard);
        client->keyboard = NULL;
    }

Daiki Ueno's avatar
Daiki Ueno committed
230
#ifdef HAVE_FAKEKEY
231
232
233
    if (client->fakekey) {
        client->fakekey = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
234
#endif  /* HAVE_FAKEKEY */
235

236
237
238
239
240
    if (client->display) {
        gdk_display_close (client->display);
        client->display = NULL;
    }

241
    G_OBJECT_CLASS (eekboard_client_parent_class)->dispose (object);
242
243
244
}

static void
245
eekboard_client_class_init (EekboardClientClass *klass)
246
247
248
249
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

250
251
252
    gobject_class->set_property = eekboard_client_set_property;
    gobject_class->get_property = eekboard_client_get_property;
    gobject_class->dispose = eekboard_client_dispose;
253
254
255
256
257
258
259
260
261

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

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

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

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

310
gboolean
311
eekboard_client_set_xkl_config (EekboardClient *client,
312
313
314
315
                                        const gchar *model,
                                        const gchar *layouts,
                                        const gchar *options)
{
Daiki Ueno's avatar
Daiki Ueno committed
316
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
317
    return set_xkl_keyboard (client,
Daiki Ueno's avatar
Daiki Ueno committed
318
                             !client->follows_focus,
Daiki Ueno's avatar
Daiki Ueno committed
319
320
321
                             model,
                             layouts,
                             options);
Daiki Ueno's avatar
Daiki Ueno committed
322
#else  /* ENABLE_FOCUS_LISTENER */
Daiki Ueno's avatar
Daiki Ueno committed
323
324
325
326
327
    return set_xkl_keyboard (client,
                             TRUE,
                             model,
                             layouts,
                             options);
Daiki Ueno's avatar
Daiki Ueno committed
328
#endif  /* !ENABLE_FOCUS_LISTENER */
329
330
}

331
gboolean
332
eekboard_client_enable_xkl (EekboardClient *client)
333
{
334
335
336
337
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
338

339
340
341
    if (!client->xkl_engine) {
        client->xkl_engine =
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (client->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
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);

    xkl_engine_start_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);

Daiki Ueno's avatar
Daiki Ueno committed
367
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
368
    return set_xkl_keyboard (client,
Daiki Ueno's avatar
Daiki Ueno committed
369
                             !client->follows_focus,
Daiki Ueno's avatar
Daiki Ueno committed
370
371
372
                             NULL,
                             NULL,
                             NULL);
Daiki Ueno's avatar
Daiki Ueno committed
373
#else  /* ENABLE_FOCUS_LISTENER */
Daiki Ueno's avatar
Daiki Ueno committed
374
    return set_xkl_keyboard (client, TRUE, NULL, NULL, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
375
#endif  /* !ENABLE_FOCUS_LISTENER */
376
377
378
}

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

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

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

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

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

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

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

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

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

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

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

Daiki Ueno's avatar
Daiki Ueno committed
515
516
    if (atspi_state_set_contains (state_set, ATSPI_STATE_EDITABLE) ||
        role == ATSPI_ROLE_TERMINAL) {
517
        switch (role) {
Daiki Ueno's avatar
Daiki Ueno committed
518
519
520
521
        case ATSPI_ROLE_TEXT:
        case ATSPI_ROLE_PARAGRAPH:
        case ATSPI_ROLE_PASSWORD_TEXT:
        case ATSPI_ROLE_TERMINAL:
Daiki Ueno's avatar
Daiki Ueno committed
522
523
524
525
526
527
528
529
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
                eekboard_context_show_keyboard (client->context, NULL);
            } else if (event->detail1 == 0 && accessible == client->acc) {
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
            }
            break;
Daiki Ueno's avatar
Daiki Ueno committed
530
        case ATSPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
531
532
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
533
                eekboard_context_show_keyboard (client->context, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
534
535
536
            } else if (event->detail1 == 0) {
                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
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
#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);
        }
    }

    return message;
}

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

    client->ibus_bus = ibus_bus_new ();
    connection = ibus_bus_get_connection (client->ibus_bus);
    add_match_rule (connection,
                    "type='method_call',"
                    "interface='" IBUS_INTERFACE_INPUT_CONTEXT "',"
                    "member='FocusIn'");
    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;

    connection = ibus_bus_get_connection (client->ibus_bus);
    g_dbus_connection_remove_filter (connection,
                                     client->ibus_focus_message_filter);
}
#endif  /* HAVE_ATSPI */

652
653
EekboardClient *
eekboard_client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
654
{
655
    EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT,
Daiki Ueno's avatar
Daiki Ueno committed
656
657
658
659
660
                                                  "connection", connection,
                                                  NULL);
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
661
}
662
663
664
665
666
667

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
668
    EekboardClient *client = user_data;
669
670
671
672
673
674
675
676
677
678
    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)
{
679
    EekboardClient *client = user_data;
680
    gboolean retval;
681

Daiki Ueno's avatar
Daiki Ueno committed
682
    retval = set_xkl_keyboard (client, FALSE, NULL, NULL, NULL);
683
    g_return_if_fail (retval);
684

Daiki Ueno's avatar
Daiki Ueno committed
685
#ifdef HAVE_FAKEKEY
686
687
    if (client->fakekey)
        fakekey_reload_keysyms (client->fakekey);
Daiki Ueno's avatar
Daiki Ueno committed
688
#endif  /* HAVE_FAKEKEY */
689
690
}

691
static gboolean
692
set_keyboard (EekboardClient *client,
Daiki Ueno's avatar
Daiki Ueno committed
693
694
              gboolean        show,
              EekLayout      *layout)
695
696
697
{
    gchar *keyboard_name;
    static gint keyboard_serial = 0;
698
    guint keyboard_id;
699

Daiki Ueno's avatar
Daiki Ueno committed
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
    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
set_xkl_keyboard (EekboardClient *client,
                  gboolean        show,
                  const gchar    *model,
                  const gchar    *layouts,
                  const gchar    *options)
{
    EekLayout *layout;
    gboolean retval;

727
728
    if (client->keyboard)
        g_object_unref (client->keyboard);
729
    layout = eek_xkl_layout_new ();
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759

    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
760
        gchar **_options;
761

Daiki Ueno's avatar
Daiki Ueno committed
762
763
764
        _options = g_strsplit (options, ",", -1);
        if (!eek_xkl_layout_set_options (EEK_XKL_LAYOUT(layout), _options)) {
            g_strfreev (_options);
765
766
767
768
769
            g_object_unref (layout);
            return FALSE;
        }
    }

Daiki Ueno's avatar
Daiki Ueno committed
770
771
772
    retval = set_keyboard (client, show, layout);
    g_object_unref (layout);
    return retval;
773
774
775
776
777
778
779
780
781
}

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

784
    if (type == GROUP_CHANGED && client->keyboard) {
785
        gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard));
786
        if (group != value) {
787
            eekboard_context_set_group (client->context, value, NULL);
788
789
790
791
        }
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
792
#ifdef HAVE_FAKEKEY
Daiki Ueno's avatar
Daiki Ueno committed
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
G_INLINE_FUNC FakeKeyModifier
get_fakekey_modifiers (EekModifierType modifiers)
{
    FakeKeyModifier retval = 0;

    if (modifiers & EEK_SHIFT_MASK)
        retval |= FAKEKEYMOD_SHIFT;
    if (modifiers & EEK_CONTROL_MASK)
        retval |= FAKEKEYMOD_CONTROL;
    if (modifiers & EEK_MOD1_MASK)
        retval |= FAKEKEYMOD_ALT;
    if (modifiers & EEK_META_MASK)
        retval |= FAKEKEYMOD_META;

    return retval;
}

810
static void
811
812
813
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
814
{
815
    EekboardClient *client = user_data;
816
817
818
    EekSymbol *symbol;

    g_assert (client->fakekey);
819

820
    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
821
    if (EEK_IS_KEYSYM(symbol) && !eek_symbol_is_modifier (symbol)) {
822
823
824
825
826
827
828
829
830
831
832
833
834
        guint xkeysym;
        guint keycode;
        EekModifierType modifiers;
        FakeKeyModifier fakekey_modifiers;

        xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
        g_return_if_fail (xkeysym > 0);
        keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (client->display),
                                    xkeysym);
        g_return_if_fail (keycode > 0);

        modifiers = eek_keyboard_get_modifiers (client->keyboard);
        fakekey_modifiers = get_fakekey_modifiers (modifiers);
Daiki Ueno's avatar
Daiki Ueno committed
835
836
837
838
839
840
841
842
843

        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               TRUE,
                               fakekey_modifiers);
        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               FALSE,
                               fakekey_modifiers);
844
    }
845
846
847
}

static void
848
849
850
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
851
{
852
    EekboardClient *client = user_data;
853
854
855
856
857
858

    g_assert (client->fakekey);
    fakekey_release (client->fakekey);
}

gboolean
859
eekboard_client_enable_fakekey (EekboardClient *client)
860
{
861
862
863
864
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
865

866
867
    if (!client->fakekey) {
        client->fakekey = fakekey_init (GDK_DISPLAY_XDISPLAY (client->display));
868
869
870
    }
    g_assert (client->fakekey);

871
    client->key_pressed_handler =
872
        g_signal_connect (client->keyboard, "key-pressed",
873
                          G_CALLBACK(on_key_pressed), client);
874
    client->key_released_handler =
875
        g_signal_connect (client->keyboard, "key-released",
876
877
878
879
880
881
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
882
eekboard_client_disable_fakekey (EekboardClient *client)
883
884
885
886
{
    if (client->fakekey)
        fakekey_release (client->fakekey);

887
    if (g_signal_handler_is_connected (client->keyboard,
888
                                       client->key_pressed_handler))
889
        g_signal_handler_disconnect (client->keyboard,
890
                                     client->key_pressed_handler);
891
    if (g_signal_handler_is_connected (client->keyboard,
892
                                       client->key_released_handler))
893
        g_signal_handler_disconnect (client->keyboard,
894
                                     client->key_released_handler);
895
}
Daiki Ueno's avatar
Daiki Ueno committed
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915

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
916
#if ENABLE_FOCUS_LISTENER
Daiki Ueno's avatar
Daiki Ueno committed
917
    retval = set_keyboard (client, !client->follows_focus, layout);
Daiki Ueno's avatar
Daiki Ueno committed
918
#else  /* ENABLE_FOCUS_LISTENER */
919
    retval = set_keyboard (client, TRUE, layout);
Daiki Ueno's avatar
Daiki Ueno committed
920
#endif  /* !ENABLE_FOCUS_LISTENER */
Daiki Ueno's avatar
Daiki Ueno committed
921
922
923
924
    g_object_unref (layout);
    return retval;
}

Daiki Ueno's avatar
Daiki Ueno committed
925
#endif  /* HAVE_FAKEKEY */