client.c 23.7 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
24

#ifdef HAVE_CSPI
25
#include <cspi/spi.h>
Daiki Ueno's avatar
Daiki Ueno committed
26
27
#endif  /* HAVE_CSPI */

28
29
#include <gdk/gdkx.h>

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

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

#define CSW 640
#define CSH 480

enum {
    PROP_0,
    PROP_CONNECTION,
Daiki Ueno's avatar
Daiki Ueno committed
46
    PROP_EEKBOARD,
47
    PROP_CONTEXT,
48
49
50
    PROP_LAST
};

51
typedef struct _EekboardClientClass EekboardClientClass;
52

53
struct _EekboardClient {
54
55
    GObject parent;

56
    EekboardEekboard *eekboard;
57
    EekboardContext *context;
58

59
    EekKeyboard *keyboard;
60
    GdkDisplay *display;
61
62
63
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;

64
65
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
66

67
68
    gulong key_pressed_handler;
    gulong key_released_handler;
69

Daiki Ueno's avatar
Daiki Ueno committed
70
#ifdef HAVE_CSPI
Daiki Ueno's avatar
Daiki Ueno committed
71
    Accessible *acc;
72
73
    AccessibleEventListener *focus_listener;
    AccessibleEventListener *keystroke_listener;
Daiki Ueno's avatar
Daiki Ueno committed
74
75
76
77
78
#endif  /* HAVE_CSPI */

#ifdef HAVE_FAKEKEY
    FakeKey *fakekey;
#endif  /* HAVE_FAKEKEY */
79
80
};

81
struct _EekboardClientClass {
82
83
84
    GObjectClass parent_class;
};

85
G_DEFINE_TYPE (EekboardClient, eekboard_client, G_TYPE_OBJECT);
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

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

Daiki Ueno's avatar
Daiki Ueno committed
101
#ifdef HAVE_CSPI
102
103
104
105
106
107
static SPIBoolean      focus_listener_cb (const AccessibleEvent     *event,
                                          void                      *user_data);

static SPIBoolean      keystroke_listener_cb
                                         (const AccessibleKeystroke *stroke,
                                          void                      *user_data);
Daiki Ueno's avatar
Daiki Ueno committed
108
#endif  /* HAVE_CSPI */
Daiki Ueno's avatar
Daiki Ueno committed
109
110
111
112
static gboolean        set_keyboard      (EekboardClient            *client,
                                          gboolean                   show,
                                          EekLayout                 *layout);
static gboolean        set_xkl_keyboard  (EekboardClient            *client,
113
114
115
116
                                          gboolean                   show,
                                          const gchar               *model,
                                          const gchar               *layouts,
                                          const gchar               *options);
117
118

static void
119
eekboard_client_set_property (GObject      *object,
120
121
122
123
                                      guint         prop_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
{
124
    EekboardClient *client = EEKBOARD_CLIENT(object);
125
126
127
128
129
    GDBusConnection *connection;

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

131
        client->eekboard = eekboard_eekboard_new (connection, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
132
133
134
        if (client->eekboard != NULL) {
            client->context =
                eekboard_eekboard_create_context (client->eekboard,
135
                                                  "eekboard",
Daiki Ueno's avatar
Daiki Ueno committed
136
137
138
139
140
141
142
143
144
                                                  NULL);
            if (client->context == NULL) {
                g_object_unref (client->eekboard);
                client->eekboard = NULL;
            } else
                eekboard_eekboard_push_context (client->eekboard,
                                                client->context,
                                                NULL);
        }
145
146
147
148
149
150
151
152
153
        break;
    default:
        g_object_set_property (object,
                               g_param_spec_get_name (pspec),
                               value);
        break;
    }
}

154
static void
155
eekboard_client_get_property (GObject    *object,
156
157
158
159
                                     guint       prop_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
{
160
    EekboardClient *client = EEKBOARD_CLIENT(object);
161
162

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
163
164
165
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
166
167
168
169
170
171
172
173
174
175
176
    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;
    }
}

177
static void
178
eekboard_client_dispose (GObject *object)
179
{
180
    EekboardClient *client = EEKBOARD_CLIENT(object);
181

182
    eekboard_client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
183
184

#ifdef HAVE_CSPI
185
186
    eekboard_client_disable_cspi_focus (client);
    eekboard_client_disable_cspi_keystroke (client);
Daiki Ueno's avatar
Daiki Ueno committed
187
188
189
#endif  /* HAVE_CSPI */

#ifdef HAVE_FAKEKEY
190
    eekboard_client_disable_fakekey (client);
Daiki Ueno's avatar
Daiki Ueno committed
191
#endif  /* HAVE_FAKEKEY */
192

193
    if (client->context) {
194
195
        if (client->eekboard) {
            eekboard_eekboard_pop_context (client->eekboard, NULL);
196
197
198
199
200
201
        }

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

202
203
204
    if (client->eekboard) {
        g_object_unref (client->eekboard);
        client->eekboard = NULL;
205
206
    }

207
208
209
210
211
    if (client->keyboard) {
        g_object_unref (client->keyboard);
        client->keyboard = NULL;
    }

Daiki Ueno's avatar
Daiki Ueno committed
212
#ifdef HAVE_FAKEKEY
213
214
215
    if (client->fakekey) {
        client->fakekey = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
216
#endif  /* HAVE_FAKEKEY */
217

218
219
220
221
222
    if (client->display) {
        gdk_display_close (client->display);
        client->display = NULL;
    }

223
    G_OBJECT_CLASS (eekboard_client_parent_class)->dispose (object);
224
225
226
}

static void
227
eekboard_client_class_init (EekboardClientClass *klass)
228
229
230
231
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

232
233
234
    gobject_class->set_property = eekboard_client_set_property;
    gobject_class->get_property = eekboard_client_get_property;
    gobject_class->dispose = eekboard_client_dispose;
235
236
237
238
239
240
241
242
243

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

Daiki Ueno's avatar
Daiki Ueno committed
245
246
247
248
249
250
251
252
253
    pspec = g_param_spec_object ("eekboard",
                                 "Eekboard",
                                 "Eekboard",
                                 EEKBOARD_TYPE_EEKBOARD,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_EEKBOARD,
                                     pspec);

254
255
256
257
258
259
260
261
    pspec = g_param_spec_object ("context",
                                 "Context",
                                 "Context",
                                 EEKBOARD_TYPE_CONTEXT,
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_CONTEXT,
                                     pspec);
262
263
264
}

static void
265
eekboard_client_init (EekboardClient *client)
266
{
267
    client->eekboard = NULL;
268
    client->context = NULL;
269
    client->display = NULL;
270
271
    client->xkl_engine = NULL;
    client->xkl_config_registry = NULL;
272
    client->keyboard = NULL;
273
274
275
276
    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
277
278
279
280
281
282
283
#ifdef HAVE_CSPI
    client->focus_listener = NULL;
    client->keystroke_listener = NULL;
#endif  /* HAVE_CSPI */
#ifdef HAVE_FAKEKEY
    client->fakekey = NULL;
#endif  /* HAVE_FAKEKEY */
284
285
}

286
gboolean
287
eekboard_client_set_xkl_config (EekboardClient *client,
288
289
290
291
292
                                        const gchar *model,
                                        const gchar *layouts,
                                        const gchar *options)
{
#ifdef HAVE_CSPI
Daiki Ueno's avatar
Daiki Ueno committed
293
294
295
296
297
    return set_xkl_keyboard (client,
                             client->focus_listener ? FALSE : TRUE,
                             model,
                             layouts,
                             options);
298
#else
Daiki Ueno's avatar
Daiki Ueno committed
299
300
301
302
303
    return set_xkl_keyboard (client,
                             TRUE,
                             model,
                             layouts,
                             options);
304
305
306
#endif
}

307
gboolean
308
eekboard_client_enable_xkl (EekboardClient *client)
309
{
310
311
312
313
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
314

315
316
317
    if (!client->xkl_engine) {
        client->xkl_engine =
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (client->display));
318
    }
319
    g_assert (client->xkl_engine);
320
321
322
323
324
325
326

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

327
    client->xkl_config_changed_handler =
328
329
        g_signal_connect (client->xkl_engine, "X-config-changed",
                          G_CALLBACK(on_xkl_config_changed), client);
330
    client->xkl_state_changed_handler =
331
332
333
334
335
336
337
338
339
340
341
342
        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);

343
#ifdef HAVE_CSPI
Daiki Ueno's avatar
Daiki Ueno committed
344
345
346
347
348
    return set_xkl_keyboard (client,
                             client->focus_listener ? FALSE : TRUE,
                             NULL,
                             NULL,
                             NULL);
349
#else
Daiki Ueno's avatar
Daiki Ueno committed
350
    return set_xkl_keyboard (client, TRUE, NULL, NULL, NULL);
351
#endif
352
353
354
}

void
355
eekboard_client_disable_xkl (EekboardClient *client)
356
357
358
359
{
    if (client->xkl_engine)
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
    if (g_signal_handler_is_connected (client->xkl_engine,
360
                                       client->xkl_config_changed_handler))
361
        g_signal_handler_disconnect (client->xkl_engine,
362
                                     client->xkl_config_changed_handler);
363
    if (g_signal_handler_is_connected (client->xkl_engine,
364
                                       client->xkl_state_changed_handler))
365
        g_signal_handler_disconnect (client->xkl_engine,
366
                                     client->xkl_state_changed_handler);
367
368
}

Daiki Ueno's avatar
Daiki Ueno committed
369
#ifdef HAVE_CSPI
370
gboolean
371
eekboard_client_enable_cspi_focus (EekboardClient *client)
372
373
374
375
376
377
378
379
380
381
382
383
384
{
    client->focus_listener = SPI_createAccessibleEventListener
        ((AccessibleEventListenerCB)focus_listener_cb,
         client);

    if (!SPI_registerGlobalEventListener (client->focus_listener,
                                          "object:state-changed:focused"))
        return FALSE;

    if (!SPI_registerGlobalEventListener (client->focus_listener,
                                          "focus:"))
        return FALSE;

385
386
387
388
    return TRUE;
}

void
389
eekboard_client_disable_cspi_focus (EekboardClient *client)
390
391
392
393
394
395
396
397
398
{
    if (client->focus_listener) {
        SPI_deregisterGlobalEventListenerAll (client->focus_listener);
        AccessibleEventListener_unref (client->focus_listener);
        client->focus_listener = NULL;
    }
}

gboolean
399
eekboard_client_enable_cspi_keystroke (EekboardClient *client)
400
{
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
    client->keystroke_listener =
        SPI_createAccessibleKeystrokeListener (keystroke_listener_cb,
                                               client);

    if (!SPI_registerAccessibleKeystrokeListener
        (client->keystroke_listener,
         SPI_KEYSET_ALL_KEYS,
         0,
         SPI_KEY_PRESSED |
         SPI_KEY_RELEASED,
         SPI_KEYLISTENER_NOSYNC))
        return FALSE;
    return TRUE;
}

void
417
eekboard_client_disable_cspi_keystroke (EekboardClient *client)
418
419
420
421
422
423
424
425
426
427
428
429
430
{
    if (client->keystroke_listener) {
        SPI_deregisterAccessibleKeystrokeListener (client->keystroke_listener,
                                                   0);
        AccessibleKeystrokeListener_unref (client->keystroke_listener);
        client->keystroke_listener = NULL;
    }
}

static SPIBoolean
focus_listener_cb (const AccessibleEvent *event,
                   void                  *user_data)
{
431
    EekboardClient *client = user_data;
432
433
434
435
436
437
438
439
440
441
442
    Accessible *accessible = event->source;
    AccessibleStateSet *state_set = Accessible_getStateSet (accessible);
    AccessibleRole role = Accessible_getRole (accessible);

    if (AccessibleStateSet_contains (state_set, SPI_STATE_EDITABLE) ||
        role == SPI_ROLE_TERMINAL) {
        switch (role) {
        case SPI_ROLE_TEXT:
        case SPI_ROLE_PARAGRAPH:
        case SPI_ROLE_PASSWORD_TEXT:
        case SPI_ROLE_TERMINAL:
Daiki Ueno's avatar
Daiki Ueno committed
443
444
445
446
447
448
449
450
            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;
451
        case SPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
452
453
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
454
                eekboard_context_show_keyboard (client->context, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
455
456
457
            } else if (event->detail1 == 0) {
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
458
            }
Daiki Ueno's avatar
Daiki Ueno committed
459
460
            break;
            
461
462
463
        default:
            ;
        }
464
465
466
    } else {
        eekboard_context_hide_keyboard (client->context, NULL);
    }
467
468
469
470
471
472
473
474

    return FALSE;
}

static SPIBoolean
keystroke_listener_cb (const AccessibleKeystroke *stroke,
                       void                      *user_data)
{
475
    EekboardClient *client = user_data;
476
477
478
479
    EekKey *key;

    /* Ignore modifiers since the keystroke listener does not called
       when a modifier key is released. */
480
    key = eek_keyboard_find_key_by_keycode (client->keyboard,
481
                                            stroke->keycode);
482
483
484
485
486
    if (key) {
        EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
        if (symbol && eek_symbol_is_modifier (symbol))
            return FALSE;
    }
487

488
489
490
491
492
493
    if (stroke->type == SPI_KEY_PRESSED) {
        eekboard_context_press_key (client->context, stroke->keycode, NULL);
    } else {
        eekboard_context_release_key (client->context, stroke->keycode, NULL);
    }

494
495
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
496
497
#endif  /* HAVE_CSPI */

498
499
EekboardClient *
eekboard_client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
500
{
501
    EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT,
Daiki Ueno's avatar
Daiki Ueno committed
502
503
504
505
506
                                                  "connection", connection,
                                                  NULL);
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
507
}
508
509
510
511
512
513

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
514
    EekboardClient *client = user_data;
515
516
517
518
519
520
521
522
523
524
    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)
{
525
    EekboardClient *client = user_data;
526
    gboolean retval;
527

Daiki Ueno's avatar
Daiki Ueno committed
528
    retval = set_xkl_keyboard (client, FALSE, NULL, NULL, NULL);
529
    g_return_if_fail (retval);
530

Daiki Ueno's avatar
Daiki Ueno committed
531
#ifdef HAVE_FAKEKEY
532
533
    if (client->fakekey)
        fakekey_reload_keysyms (client->fakekey);
Daiki Ueno's avatar
Daiki Ueno committed
534
#endif  /* HAVE_FAKEKEY */
535
536
}

537
static gboolean
538
set_keyboard (EekboardClient *client,
Daiki Ueno's avatar
Daiki Ueno committed
539
540
              gboolean        show,
              EekLayout      *layout)
541
542
543
{
    gchar *keyboard_name;
    static gint keyboard_serial = 0;
544
    guint keyboard_id;
545

Daiki Ueno's avatar
Daiki Ueno committed
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
    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;

573
574
    if (client->keyboard)
        g_object_unref (client->keyboard);
575
    layout = eek_xkl_layout_new ();
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

    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
606
        gchar **_options;
607

Daiki Ueno's avatar
Daiki Ueno committed
608
609
610
        _options = g_strsplit (options, ",", -1);
        if (!eek_xkl_layout_set_options (EEK_XKL_LAYOUT(layout), _options)) {
            g_strfreev (_options);
611
612
613
614
615
            g_object_unref (layout);
            return FALSE;
        }
    }

Daiki Ueno's avatar
Daiki Ueno committed
616
617
618
    retval = set_keyboard (client, show, layout);
    g_object_unref (layout);
    return retval;
619
620
621
622
623
624
625
626
627
}

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

630
    if (type == GROUP_CHANGED && client->keyboard) {
631
        gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard));
632
        if (group != value) {
633
            eekboard_context_set_group (client->context, value, NULL);
634
635
636
637
        }
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
638
#ifdef HAVE_FAKEKEY
Daiki Ueno's avatar
Daiki Ueno committed
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
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;
}

656
static void
657
658
659
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
660
{
661
    EekboardClient *client = user_data;
662
663
664
    EekSymbol *symbol;

    g_assert (client->fakekey);
665

666
    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
667
    if (EEK_IS_KEYSYM(symbol) && !eek_symbol_is_modifier (symbol)) {
668
669
670
671
672
673
674
675
676
677
678
679
680
        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
681
682
683
684
685
686
687
688
689

        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               TRUE,
                               fakekey_modifiers);
        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               FALSE,
                               fakekey_modifiers);
690
    }
691
692
693
}

static void
694
695
696
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
697
{
698
    EekboardClient *client = user_data;
699
700
701
702
703
704

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

gboolean
705
eekboard_client_enable_fakekey (EekboardClient *client)
706
{
707
708
709
710
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
711

712
713
    if (!client->fakekey) {
        client->fakekey = fakekey_init (GDK_DISPLAY_XDISPLAY (client->display));
714
715
716
    }
    g_assert (client->fakekey);

717
    client->key_pressed_handler =
718
        g_signal_connect (client->keyboard, "key-pressed",
719
                          G_CALLBACK(on_key_pressed), client);
720
    client->key_released_handler =
721
        g_signal_connect (client->keyboard, "key-released",
722
723
724
725
726
727
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
728
eekboard_client_disable_fakekey (EekboardClient *client)
729
730
731
732
{
    if (client->fakekey)
        fakekey_release (client->fakekey);

733
    if (g_signal_handler_is_connected (client->keyboard,
734
                                       client->key_pressed_handler))
735
        g_signal_handler_disconnect (client->keyboard,
736
                                     client->key_pressed_handler);
737
    if (g_signal_handler_is_connected (client->keyboard,
738
                                       client->key_released_handler))
739
        g_signal_handler_disconnect (client->keyboard,
740
                                     client->key_released_handler);
741
}
Daiki Ueno's avatar
Daiki Ueno committed
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766

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);
    retval = set_keyboard (client, TRUE, layout);
    g_object_unref (layout);
    return retval;
}

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