client.c 22.4 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 */
109
static gboolean        set_keyboard      (EekboardClient     *client,
110
111
112
113
                                          gboolean                   show,
                                          const gchar               *model,
                                          const gchar               *layouts,
                                          const gchar               *options);
114
115

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

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

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

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

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

174
static void
175
eekboard_client_dispose (GObject *object)
176
{
177
    EekboardClient *client = EEKBOARD_CLIENT(object);
178

179
    eekboard_client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
180
181

#ifdef HAVE_CSPI
182
183
    eekboard_client_disable_cspi_focus (client);
    eekboard_client_disable_cspi_keystroke (client);
Daiki Ueno's avatar
Daiki Ueno committed
184
185
186
#endif  /* HAVE_CSPI */

#ifdef HAVE_FAKEKEY
187
    eekboard_client_disable_fakekey (client);
Daiki Ueno's avatar
Daiki Ueno committed
188
#endif  /* HAVE_FAKEKEY */
189

190
    if (client->context) {
191
192
        if (client->eekboard) {
            eekboard_eekboard_pop_context (client->eekboard, NULL);
193
194
195
196
197
198
        }

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

199
200
201
    if (client->eekboard) {
        g_object_unref (client->eekboard);
        client->eekboard = NULL;
202
203
    }

204
205
206
207
208
    if (client->keyboard) {
        g_object_unref (client->keyboard);
        client->keyboard = NULL;
    }

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

215
216
217
218
219
    if (client->display) {
        gdk_display_close (client->display);
        client->display = NULL;
    }

220
    G_OBJECT_CLASS (eekboard_client_parent_class)->dispose (object);
221
222
223
}

static void
224
eekboard_client_class_init (EekboardClientClass *klass)
225
226
227
228
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

229
230
231
    gobject_class->set_property = eekboard_client_set_property;
    gobject_class->get_property = eekboard_client_get_property;
    gobject_class->dispose = eekboard_client_dispose;
232
233
234
235
236
237
238
239
240

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

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

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

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

283
gboolean
284
eekboard_client_set_xkl_config (EekboardClient *client,
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
                                        const gchar *model,
                                        const gchar *layouts,
                                        const gchar *options)
{
#ifdef HAVE_CSPI
    return set_keyboard (client,
                         client->focus_listener ? FALSE : TRUE,
                         model,
                         layouts,
                         options);
#else
    return set_keyboard (client,
                         TRUE,
                         model,
                         layouts,
                         options);
#endif
}

304
gboolean
305
eekboard_client_enable_xkl (EekboardClient *client)
306
{
307
308
309
310
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
311

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

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

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

340
#ifdef HAVE_CSPI
341
342
    return set_keyboard (client, client->focus_listener ? FALSE : TRUE,
                         NULL, NULL, NULL);
343
#else
344
    return set_keyboard (client, TRUE, NULL, NULL, NULL);
345
#endif
346
347
348
}

void
349
eekboard_client_disable_xkl (EekboardClient *client)
350
351
352
353
{
    if (client->xkl_engine)
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
    if (g_signal_handler_is_connected (client->xkl_engine,
354
                                       client->xkl_config_changed_handler))
355
        g_signal_handler_disconnect (client->xkl_engine,
356
                                     client->xkl_config_changed_handler);
357
    if (g_signal_handler_is_connected (client->xkl_engine,
358
                                       client->xkl_state_changed_handler))
359
        g_signal_handler_disconnect (client->xkl_engine,
360
                                     client->xkl_state_changed_handler);
361
362
}

Daiki Ueno's avatar
Daiki Ueno committed
363
#ifdef HAVE_CSPI
364
gboolean
365
eekboard_client_enable_cspi_focus (EekboardClient *client)
366
367
368
369
370
371
372
373
374
375
376
377
378
{
    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;

379
380
381
382
    return TRUE;
}

void
383
eekboard_client_disable_cspi_focus (EekboardClient *client)
384
385
386
387
388
389
390
391
392
{
    if (client->focus_listener) {
        SPI_deregisterGlobalEventListenerAll (client->focus_listener);
        AccessibleEventListener_unref (client->focus_listener);
        client->focus_listener = NULL;
    }
}

gboolean
393
eekboard_client_enable_cspi_keystroke (EekboardClient *client)
394
{
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    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
411
eekboard_client_disable_cspi_keystroke (EekboardClient *client)
412
413
414
415
416
417
418
419
420
421
422
423
424
{
    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)
{
425
    EekboardClient *client = user_data;
426
427
428
429
430
431
432
433
434
435
436
    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
437
438
439
440
441
442
443
444
            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;
445
        case SPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
446
447
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
448
                eekboard_context_show_keyboard (client->context, NULL);
Daiki Ueno's avatar
Daiki Ueno committed
449
450
451
            } else if (event->detail1 == 0) {
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
452
            }
Daiki Ueno's avatar
Daiki Ueno committed
453
454
            break;
            
455
456
457
        default:
            ;
        }
458
459
460
    } else {
        eekboard_context_hide_keyboard (client->context, NULL);
    }
461
462
463
464
465
466
467
468

    return FALSE;
}

static SPIBoolean
keystroke_listener_cb (const AccessibleKeystroke *stroke,
                       void                      *user_data)
{
469
    EekboardClient *client = user_data;
470
471
472
473
    EekKey *key;

    /* Ignore modifiers since the keystroke listener does not called
       when a modifier key is released. */
474
    key = eek_keyboard_find_key_by_keycode (client->keyboard,
475
                                            stroke->keycode);
476
477
478
479
480
    if (key) {
        EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
        if (symbol && eek_symbol_is_modifier (symbol))
            return FALSE;
    }
481

482
483
484
485
486
487
    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);
    }

488
489
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
490
491
#endif  /* HAVE_CSPI */

492
493
EekboardClient *
eekboard_client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
494
{
495
    EekboardClient *client = g_object_new (EEKBOARD_TYPE_CLIENT,
Daiki Ueno's avatar
Daiki Ueno committed
496
497
498
499
500
                                                  "connection", connection,
                                                  NULL);
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
501
}
502
503
504
505
506
507

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
508
    EekboardClient *client = user_data;
509
510
511
512
513
514
515
516
517
518
    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)
{
519
    EekboardClient *client = user_data;
520
    gboolean retval;
521

522
523
    retval = set_keyboard (client, FALSE, NULL, NULL, NULL);
    g_return_if_fail (retval);
524

Daiki Ueno's avatar
Daiki Ueno committed
525
#ifdef HAVE_FAKEKEY
526
527
    if (client->fakekey)
        fakekey_reload_keysyms (client->fakekey);
Daiki Ueno's avatar
Daiki Ueno committed
528
#endif  /* HAVE_FAKEKEY */
529
530
}

531
static gboolean
532
set_keyboard (EekboardClient *client,
533
534
535
536
              gboolean               show,
              const gchar           *model,
              const gchar           *layouts,
              const gchar           *options)
537
538
539
540
{
    EekLayout *layout;
    gchar *keyboard_name;
    static gint keyboard_serial = 0;
541
    guint keyboard_id;
542

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

    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
576
        gchar **_options;
577

Daiki Ueno's avatar
Daiki Ueno committed
578
579
580
        _options = g_strsplit (options, ",", -1);
        if (!eek_xkl_layout_set_options (EEK_XKL_LAYOUT(layout), _options)) {
            g_strfreev (_options);
581
582
583
584
585
            g_object_unref (layout);
            return FALSE;
        }
    }

586
587
    client->keyboard = eek_keyboard_new (layout, CSW, CSH);
    eek_keyboard_set_modifier_behavior (client->keyboard,
588
                                        EEK_MODIFIER_BEHAVIOR_LATCH);
589
590

    keyboard_name = g_strdup_printf ("keyboard%d", keyboard_serial++);
591
    eek_element_set_name (EEK_ELEMENT(client->keyboard), keyboard_name);
Daiki Ueno's avatar
Daiki Ueno committed
592
    g_free (keyboard_name);
593

594
595
596
597
    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 client->keyboard,
                                                 NULL);
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
598
599
    if (show)
        eekboard_context_show_keyboard (client->context, NULL);
600
601

    return TRUE;
602
603
604
605
606
607
608
609
610
}

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

613
    if (type == GROUP_CHANGED && client->keyboard) {
614
        gint group = eek_element_get_group (EEK_ELEMENT(client->keyboard));
615
        if (group != value) {
616
            eekboard_context_set_group (client->context, value, NULL);
617
618
619
620
        }
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
621
#ifdef HAVE_FAKEKEY
Daiki Ueno's avatar
Daiki Ueno committed
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
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;
}

639
static void
640
641
642
on_key_pressed (EekKeyboard *keyboard,
                EekKey      *key,
                gpointer     user_data)
643
{
644
    EekboardClient *client = user_data;
645
646
647
    EekSymbol *symbol;

    g_assert (client->fakekey);
648

649
    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
650
    if (EEK_IS_KEYSYM(symbol) && !eek_symbol_is_modifier (symbol)) {
Daiki Ueno's avatar
Daiki Ueno committed
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
        guint xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
        guint keycode =
            XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (client->display), xkeysym);
        EekModifierType modifiers =
            eek_keyboard_get_modifiers (client->keyboard);
        FakeKeyModifier fakekey_modifiers = get_fakekey_modifiers (modifiers);

        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               TRUE,
                               fakekey_modifiers);
        fakekey_send_keyevent (client->fakekey,
                               keycode,
                               FALSE,
                               fakekey_modifiers);
666
    }
667
668
669
}

static void
670
671
672
on_key_released (EekKeyboard *keyboard,
                 EekKey      *key,
                 gpointer     user_data)
673
{
674
    EekboardClient *client = user_data;
675
676
677
678
679
680

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

gboolean
681
eekboard_client_enable_fakekey (EekboardClient *client)
682
{
683
684
685
686
    if (!client->display) {
        client->display = gdk_display_get_default ();
    }
    g_assert (client->display);
687

688
689
    if (!client->fakekey) {
        client->fakekey = fakekey_init (GDK_DISPLAY_XDISPLAY (client->display));
690
691
692
    }
    g_assert (client->fakekey);

693
    client->key_pressed_handler =
694
        g_signal_connect (client->keyboard, "key-pressed",
695
                          G_CALLBACK(on_key_pressed), client);
696
    client->key_released_handler =
697
        g_signal_connect (client->keyboard, "key-released",
698
699
700
701
702
703
                          G_CALLBACK(on_key_released), client);

    return TRUE;
}

void
704
eekboard_client_disable_fakekey (EekboardClient *client)
705
706
707
708
{
    if (client->fakekey)
        fakekey_release (client->fakekey);

709
    if (g_signal_handler_is_connected (client->keyboard,
710
                                       client->key_pressed_handler))
711
        g_signal_handler_disconnect (client->keyboard,
712
                                     client->key_pressed_handler);
713
    if (g_signal_handler_is_connected (client->keyboard,
714
                                       client->key_released_handler))
715
        g_signal_handler_disconnect (client->keyboard,
716
                                     client->key_released_handler);
717
}
Daiki Ueno's avatar
Daiki Ueno committed
718
#endif  /* HAVE_FAKEKEY */