client.c 31.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 
 * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
 * Copyright (C) 2010-2011 Red Hat, Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
Daiki Ueno's avatar
Daiki Ueno committed
18
19
20
21
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif  /* HAVE_CONFIG_H */

22
#include <libxklavier/xklavier.h>
Daiki Ueno's avatar
Daiki Ueno committed
23

Daiki Ueno's avatar
Daiki Ueno committed
24
25
26
27
#ifdef HAVE_ATSPI
#include <dbus/dbus.h>
#include <atspi/atspi.h>
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
28

29
30
#include <gdk/gdkx.h>

31
32
#ifdef HAVE_XTEST
#include <X11/extensions/XTest.h>
33
#include <X11/XKBlib.h>
34
#endif  /* HAVE_XTEST */
35

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

40
41
#include "eek/eek.h"
#include "eek/eek-xkl.h"
Daiki Ueno's avatar
Daiki Ueno committed
42
43
#include "eekboard/eekboard-client.h"
#include "eekboard/eekboard-xklutil.h"
44
#include "client.h"
Daiki Ueno's avatar
Daiki Ueno committed
45
#include "preferences-dialog.h"
46

47
48
#include <string.h>

49
50
51
52
53
54
#define CSW 640
#define CSH 480

enum {
    PROP_0,
    PROP_CONNECTION,
Daiki Ueno's avatar
Daiki Ueno committed
55
    PROP_EEKBOARD,
56
    PROP_CONTEXT,
57
58
59
    PROP_LAST
};

Daiki Ueno's avatar
Daiki Ueno committed
60
typedef struct _ClientClass ClientClass;
61

Daiki Ueno's avatar
Daiki Ueno committed
62
struct _Client {
63
64
    GObject parent;

Daiki Ueno's avatar
Daiki Ueno committed
65
    EekboardClient *eekboard;
66
    EekboardContext *context;
67

Daiki Ueno's avatar
Daiki Ueno committed
68
    GSList *keyboards;
69
70
71
    XklEngine *xkl_engine;
    XklConfigRegistry *xkl_config_registry;

72
73
    gulong xkl_config_changed_handler;
    gulong xkl_state_changed_handler;
74

75
76
    gulong key_pressed_handler;
    gulong key_released_handler;
77

Daiki Ueno's avatar
Daiki Ueno committed
78
    gboolean follows_focus;
Daiki Ueno's avatar
Daiki Ueno committed
79
    guint hide_keyboard_timeout_id;
Daiki Ueno's avatar
Daiki Ueno committed
80

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

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

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

    GSettings *settings;
97
98
};

Daiki Ueno's avatar
Daiki Ueno committed
99
struct _ClientClass {
100
101
102
    GObjectClass parent_class;
};

Daiki Ueno's avatar
Daiki Ueno committed
103
G_DEFINE_TYPE (Client, client, G_TYPE_OBJECT);
104

105
106
107
108
109
110
#if ENABLE_FOCUS_LISTENER
#define IS_KEYBOARD_VISIBLE(client) (!client->follows_focus)
#else  /* ENABLE_FOCUS_LISTENER */
#define IS_KEYBOARD_VISIBLE(client) TRUE
#endif  /* !ENABLE_FOCUS_LISTENER */

Daiki Ueno's avatar
Daiki Ueno committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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 */
130
131
132
133
static gboolean        set_keyboards        (Client                 *client,
                                             const gchar           **keyboard);
static gboolean        set_keyboards_from_xkl
                                            (Client                 *client);
134
135
#ifdef HAVE_XTEST
static void            update_modifier_keycodes
136
                                            (Client                 *client);
137
#endif  /* HAVE_XTEST */
138
139

static void
Daiki Ueno's avatar
Daiki Ueno committed
140
141
142
143
client_set_property (GObject      *object,
                     guint         prop_id,
                     const GValue *value,
                     GParamSpec   *pspec)
144
{
Daiki Ueno's avatar
Daiki Ueno committed
145
    Client *client = CLIENT(object);
146
147
148
149
150
    GDBusConnection *connection;

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

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

175
static void
Daiki Ueno's avatar
Daiki Ueno committed
176
177
178
179
client_get_property (GObject    *object,
                     guint       prop_id,
                     GValue     *value,
                     GParamSpec *pspec)
180
{
Daiki Ueno's avatar
Daiki Ueno committed
181
    Client *client = CLIENT(object);
182
183

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

198
static void
Daiki Ueno's avatar
Daiki Ueno committed
199
client_dispose (GObject *object)
200
{
Daiki Ueno's avatar
Daiki Ueno committed
201
    Client *client = CLIENT(object);
202

Daiki Ueno's avatar
Daiki Ueno committed
203
    client_disable_xkl (client);
Daiki Ueno's avatar
Daiki Ueno committed
204

Daiki Ueno's avatar
Daiki Ueno committed
205
#ifdef HAVE_ATSPI
Daiki Ueno's avatar
Daiki Ueno committed
206
207
    client_disable_atspi_focus (client);
    client_disable_atspi_keystroke (client);
Daiki Ueno's avatar
Daiki Ueno committed
208
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
209

Daiki Ueno's avatar
Daiki Ueno committed
210
#ifdef HAVE_IBUS
Daiki Ueno's avatar
Daiki Ueno committed
211
    client_disable_ibus_focus (client);
Daiki Ueno's avatar
Daiki Ueno committed
212
213
214
215
216
217
    if (client->ibus_bus) {
        g_object_unref (client->ibus_bus);
        client->ibus_bus = NULL;
    }
#endif  /* HAVE_IBUS */

218
#ifdef HAVE_XTEST
Daiki Ueno's avatar
Daiki Ueno committed
219
    client_disable_xtest (client);
220
#endif  /* HAVE_XTEST */
221

222
223
224
225
226
    if (client->context) {
        g_object_unref (client->context);
        client->context = NULL;
    }

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

232
233
234
235
236
    if (client->settings) {
        g_object_unref (client->settings);
        client->settings = NULL;
    }

Daiki Ueno's avatar
Daiki Ueno committed
237
    G_OBJECT_CLASS (client_parent_class)->dispose (object);
238
239
}

Daiki Ueno's avatar
Daiki Ueno committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
static void
client_finalize (GObject *object)
{
    Client *client = CLIENT(object);

    if (client->keyboards) {
        GSList *next = client->keyboards->next;
        /* client->keyboards is a ring; break it before free */
        client->keyboards->next = NULL;
        g_slist_free (next);
    }

    G_OBJECT_CLASS (client_parent_class)->finalize (object);
}

255
static void
Daiki Ueno's avatar
Daiki Ueno committed
256
client_class_init (ClientClass *klass)
257
258
259
260
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec *pspec;

Daiki Ueno's avatar
Daiki Ueno committed
261
262
263
    gobject_class->set_property = client_set_property;
    gobject_class->get_property = client_get_property;
    gobject_class->dispose = client_dispose;
Daiki Ueno's avatar
Daiki Ueno committed
264
    gobject_class->finalize = client_finalize;
265
266
267
268
269
270
271
272
273

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

Daiki Ueno's avatar
Daiki Ueno committed
275
276
277
    pspec = g_param_spec_object ("eekboard",
                                 "Eekboard",
                                 "Eekboard",
Daiki Ueno's avatar
Daiki Ueno committed
278
                                 EEKBOARD_TYPE_CLIENT,
Daiki Ueno's avatar
Daiki Ueno committed
279
280
281
282
283
                                 G_PARAM_READABLE);
    g_object_class_install_property (gobject_class,
                                     PROP_EEKBOARD,
                                     pspec);

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

static void
Daiki Ueno's avatar
Daiki Ueno committed
295
client_init (Client *client)
296
{
297
    client->settings = g_settings_new ("org.fedorahosted.eekboard");
298
299
}

300
gboolean
301
302
client_set_keyboards (Client       *client,
                      const gchar **keyboards)
303
304
{
    gboolean retval;
305
    retval = set_keyboards (client, keyboards);
306
    if (retval && IS_KEYBOARD_VISIBLE (client))
307
308
        eekboard_context_show_keyboard (client->context, NULL);
    return retval;
309
310
}

311
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
312
client_enable_xkl (Client *client)
313
{
Daiki Ueno's avatar
Daiki Ueno committed
314
    GdkDisplay *display = gdk_display_get_default ();
315
316
    gboolean retval;

Daiki Ueno's avatar
Daiki Ueno committed
317
    g_assert (display);
318

319
320
    if (!client->xkl_engine) {
        client->xkl_engine =
Daiki Ueno's avatar
Daiki Ueno committed
321
            xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (display));
322
    }
323
    g_assert (client->xkl_engine);
324
325
326
327
328
329
330

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

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

347
    retval = set_keyboards_from_xkl (client);
348
349
350
351
    if (IS_KEYBOARD_VISIBLE (client))
        eekboard_context_show_keyboard (client->context, NULL);

    return retval;
352
353
354
}

void
Daiki Ueno's avatar
Daiki Ueno committed
355
client_disable_xkl (Client *client)
356
{
357
    if (client->xkl_engine) {
358
        xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
359
360
361
362
363
364
365
366
367
368
369

        if (g_signal_handler_is_connected (client->xkl_engine,
                                           client->xkl_config_changed_handler))
            g_signal_handler_disconnect (client->xkl_engine,
                                         client->xkl_config_changed_handler);
        if (g_signal_handler_is_connected (client->xkl_engine,
                                           client->xkl_state_changed_handler))
            g_signal_handler_disconnect (client->xkl_engine,
                                         client->xkl_state_changed_handler);
        client->xkl_engine = NULL;
    }
370
371
}

Daiki Ueno's avatar
Daiki Ueno committed
372
#ifdef HAVE_ATSPI
373
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
374
client_enable_atspi_focus (Client *client)
375
{
Daiki Ueno's avatar
Daiki Ueno committed
376
    GError *error;
377

Daiki Ueno's avatar
Daiki Ueno committed
378
379
380
381
382
383
384
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "object:state-changed:focused",
         &error))
385
386
        return FALSE;

Daiki Ueno's avatar
Daiki Ueno committed
387
388
389
390
391
392
393
    error = NULL;
    if (!atspi_event_listener_register_from_callback
        ((AtspiEventListenerCB)focus_listener_cb,
         client,
         NULL,
         "focus:",
         &error))
394
395
        return FALSE;

Daiki Ueno's avatar
Daiki Ueno committed
396
    client->follows_focus = TRUE;
397
398
399
400
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
401
client_disable_atspi_focus (Client *client)
402
{
Daiki Ueno's avatar
Daiki Ueno committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    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);
420
421
422
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
423
client_enable_atspi_keystroke (Client *client)
424
{
Daiki Ueno's avatar
Daiki Ueno committed
425
426
    GError *error;

427
    client->keystroke_listener =
Daiki Ueno's avatar
Daiki Ueno committed
428
429
430
        atspi_device_listener_new ((AtspiDeviceListenerCB)keystroke_listener_cb,
                                   NULL,
                                   client);
431

Daiki Ueno's avatar
Daiki Ueno committed
432
433
434
435
436
437
438
439
440
441
442
443
    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
444
        (client->keystroke_listener,
Daiki Ueno's avatar
Daiki Ueno committed
445
         NULL,
446
         0,
Daiki Ueno's avatar
Daiki Ueno committed
447
448
449
         ATSPI_KEY_RELEASED,
         ATSPI_KEYLISTENER_NOSYNC,
         &error))
450
451
452
453
454
        return FALSE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
455
client_disable_atspi_keystroke (Client *client)
456
457
{
    if (client->keystroke_listener) {
Daiki Ueno's avatar
Daiki Ueno committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
        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);
475
476
477
478
        client->keystroke_listener = NULL;
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
479
480
481
static void
focus_listener_cb (const AtspiEvent *event,
                   void             *user_data)
482
{
Daiki Ueno's avatar
Daiki Ueno committed
483
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
484
485
486
487
488
489
490
491
492
    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;
493

Daiki Ueno's avatar
Daiki Ueno committed
494
495
    if (atspi_state_set_contains (state_set, ATSPI_STATE_EDITABLE) ||
        role == ATSPI_ROLE_TERMINAL) {
496
        switch (role) {
Daiki Ueno's avatar
Daiki Ueno committed
497
498
499
500
        case ATSPI_ROLE_TEXT:
        case ATSPI_ROLE_PARAGRAPH:
        case ATSPI_ROLE_PASSWORD_TEXT:
        case ATSPI_ROLE_TERMINAL:
Daiki Ueno's avatar
Daiki Ueno committed
501
502
503
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
                eekboard_context_show_keyboard (client->context, NULL);
504
505
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0 && accessible == client->acc) {
Daiki Ueno's avatar
Daiki Ueno committed
506
507
508
509
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
            }
            break;
Daiki Ueno's avatar
Daiki Ueno committed
510
        case ATSPI_ROLE_ENTRY:
Daiki Ueno's avatar
Daiki Ueno committed
511
512
            if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                client->acc = accessible;
513
                eekboard_context_show_keyboard (client->context, NULL);
514
515
            } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                       event->detail1 == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
516
517
                client->acc = NULL;
                eekboard_context_hide_keyboard (client->context, NULL);
518
            }
Daiki Ueno's avatar
Daiki Ueno committed
519
520
            break;
            
521
522
523
        default:
            ;
        }
524
525
526
    } else {
        eekboard_context_hide_keyboard (client->context, NULL);
    }
527
528
}

Daiki Ueno's avatar
Daiki Ueno committed
529
530
531
static gboolean
keystroke_listener_cb (const AtspiDeviceEvent *stroke,
                       void                   *user_data)
532
{
Daiki Ueno's avatar
Daiki Ueno committed
533
    Client *client = user_data;
534

535
536
    switch (stroke->type) {
    case ATSPI_KEY_PRESSED:
Daiki Ueno's avatar
Daiki Ueno committed
537
        eekboard_context_press_keycode (client->context, stroke->hw_code, NULL);
538
539
        break;
    case ATSPI_KEY_RELEASED:
Daiki Ueno's avatar
Daiki Ueno committed
540
        eekboard_context_release_keycode (client->context, stroke->hw_code, NULL);
541
542
543
        break;
    default:
        g_return_val_if_reached (FALSE);
544
    }
545
546
    return TRUE;
}
Daiki Ueno's avatar
Daiki Ueno committed
547
#endif  /* HAVE_ATSPI */
Daiki Ueno's avatar
Daiki Ueno committed
548

Daiki Ueno's avatar
Daiki Ueno committed
549
550
551
552
553
#ifdef HAVE_IBUS
static void
add_match_rule (GDBusConnection *connection,
                const gchar     *match_rule)
{
Daiki Ueno's avatar
Daiki Ueno committed
554
555
    GError *error;
    GDBusMessage *message;
Daiki Ueno's avatar
Daiki Ueno committed
556

Daiki Ueno's avatar
Daiki Ueno committed
557
558
559
560
561
562
563
564
565
566
567
568
    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);
Daiki Ueno's avatar
Daiki Ueno committed
569
570
}

Daiki Ueno's avatar
Daiki Ueno committed
571
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
572
on_hide_keyboard_timeout (Client *client)
Daiki Ueno's avatar
Daiki Ueno committed
573
574
575
576
577
578
{
    eekboard_context_hide_keyboard (client->context, NULL);
    client->hide_keyboard_timeout_id = 0;
    return FALSE;
}

Daiki Ueno's avatar
Daiki Ueno committed
579
580
581
582
583
584
static GDBusMessage *
focus_message_filter (GDBusConnection *connection,
                      GDBusMessage    *message,
                      gboolean         incoming,
                      gpointer         user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
585
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
586
587
588

    if (incoming &&
        g_strcmp0 (g_dbus_message_get_interface (message),
589
                   IBUS_INTERFACE_PANEL) == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
590
591
592
        const gchar *member = g_dbus_message_get_member (message);

        if (g_strcmp0 (member, "FocusIn") == 0) {
Daiki Ueno's avatar
Daiki Ueno committed
593
594
595
596
            if (client->hide_keyboard_timeout_id > 0) {
                g_source_remove (client->hide_keyboard_timeout_id);
                client->hide_keyboard_timeout_id = 0;
            }
Daiki Ueno's avatar
Daiki Ueno committed
597
            eekboard_context_show_keyboard (client->context, NULL);
598
599
        } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                   g_strcmp0 (member, "FocusOut") == 0) {
600
601
            guint delay;
            g_settings_get (client->settings, "auto-hide-delay", "u", &delay);
Daiki Ueno's avatar
Daiki Ueno committed
602
            client->hide_keyboard_timeout_id =
603
                g_timeout_add (delay,
Daiki Ueno's avatar
Daiki Ueno committed
604
605
                               (GSourceFunc)on_hide_keyboard_timeout,
                               client);
Daiki Ueno's avatar
Daiki Ueno committed
606
607
608
609
610
611
        }
    }

    return message;
}

612
613
static void
_ibus_connect_focus_handlers (IBusBus *bus, gpointer user_data)
Daiki Ueno's avatar
Daiki Ueno committed
614
{
Daiki Ueno's avatar
Daiki Ueno committed
615
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
616
617
    GDBusConnection *connection;

618
    connection = ibus_bus_get_connection (bus);
Daiki Ueno's avatar
Daiki Ueno committed
619
620
    add_match_rule (connection,
                    "type='method_call',"
621
                    "interface='" IBUS_INTERFACE_PANEL "',"
Daiki Ueno's avatar
Daiki Ueno committed
622
                    "member='FocusIn'");
623
624
    add_match_rule (connection,
                    "type='method_call',"
625
                    "interface='" IBUS_INTERFACE_PANEL "',"
626
                    "member='FocusOut'");
Daiki Ueno's avatar
Daiki Ueno committed
627
628
629
630
631
    client->ibus_focus_message_filter =
        g_dbus_connection_add_filter (connection,
                                      focus_message_filter,
                                      client,
                                      NULL);
632
633
634
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
635
client_enable_ibus_focus (Client *client)
636
637
638
639
640
641
642
643
644
645
646
647
{
    if (!client->ibus_bus) {
        client->ibus_bus = ibus_bus_new ();
        g_object_ref (client->ibus_bus);
        g_signal_connect (client->ibus_bus, "connected",
                          G_CALLBACK(_ibus_connect_focus_handlers),
                          client);
    }

    if (ibus_bus_is_connected (client->ibus_bus))
        _ibus_connect_focus_handlers (client->ibus_bus, client);

Daiki Ueno's avatar
Daiki Ueno committed
648
649
650
651
652
    client->follows_focus = TRUE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
653
client_disable_ibus_focus (Client *client)
Daiki Ueno's avatar
Daiki Ueno committed
654
655
656
657
658
{
    GDBusConnection *connection;

    client->follows_focus = FALSE;

659
660
661
662
663
664
665
666
667
    if (client->ibus_bus) {
        if (client->ibus_focus_message_filter != 0) {
            connection = ibus_bus_get_connection (client->ibus_bus);
            g_dbus_connection_remove_filter (connection,
                                             client->ibus_focus_message_filter);
        }
        g_object_unref (client->ibus_bus);
        client->ibus_bus = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
668
669
670
}
#endif  /* HAVE_ATSPI */

Daiki Ueno's avatar
Daiki Ueno committed
671
672
Client *
client_new (GDBusConnection *connection)
Daiki Ueno's avatar
Daiki Ueno committed
673
{
Daiki Ueno's avatar
Daiki Ueno committed
674
675
676
    Client *client = g_object_new (TYPE_CLIENT,
                                   "connection", connection,
                                   NULL);
Daiki Ueno's avatar
Daiki Ueno committed
677
678
679
    if (client->context)
        return client;
    return NULL;
Daiki Ueno's avatar
Daiki Ueno committed
680
}
681
682
683
684
685
686

static GdkFilterReturn
filter_xkl_event (GdkXEvent *xev,
                  GdkEvent  *event,
                  gpointer   user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
687
    Client *client = user_data;
688
689
690
691
692
693
694
695
696
697
    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)
{
Daiki Ueno's avatar
Daiki Ueno committed
698
    Client *client = user_data;
699
    gboolean retval;
700

701
    retval = set_keyboards_from_xkl (client);
702
    g_return_if_fail (retval);
703

704
#ifdef HAVE_XTEST
705
    update_modifier_keycodes (client);
706
#endif  /* HAVE_XTEST */
707
708
}

709
static gboolean
710
711
set_keyboards (Client       *client,
               const gchar **keyboards)
712
{
713
714
715
    guint keyboard_id;
    gchar **p;
    GSList *head = NULL;
Daiki Ueno's avatar
Daiki Ueno committed
716

Daiki Ueno's avatar
Daiki Ueno committed
717
718
    if (client->keyboards)
        g_slist_free (client->keyboards);
719

720
721
722
723
    for (p = keyboards; *p != NULL; p++) {
        keyboard_id = eekboard_context_add_keyboard (client->context, *p, NULL);
        if (keyboard_id == 0) {
            g_slist_free (head);
Daiki Ueno's avatar
Daiki Ueno committed
724
            return FALSE;
725
726
        }
        head = g_slist_prepend (head, GUINT_TO_POINTER(keyboard_id));
Daiki Ueno's avatar
Daiki Ueno committed
727
728
729
    }

    /* make a cycle */
730
731
732
    head = g_slist_reverse (head);
    g_slist_last (head)->next = head;
    client->keyboards = head;
Daiki Ueno's avatar
Daiki Ueno committed
733
734
735

    /* select the first keyboard */
    eekboard_context_set_keyboard (client->context,
736
                                   GPOINTER_TO_UINT(head->data),
Daiki Ueno's avatar
Daiki Ueno committed
737
                                   NULL);
Daiki Ueno's avatar
Daiki Ueno committed
738
739
740
    return TRUE;
}

741
static gboolean
742
set_keyboards_from_xkl (Client *client)
743
744
745
{
    XklConfigRec *rec;
    gchar *layout, *keyboard;
Daiki Ueno's avatar
Daiki Ueno committed
746
    guint keyboard_id;
747
748
749
750
751
752
753
754

    rec = xkl_config_rec_new ();
    xkl_config_rec_get_from_server (rec, client->xkl_engine);
    layout = eekboard_xkl_config_rec_to_string (rec);
    g_object_unref (rec);

    keyboard = g_strdup_printf ("xkb:%s", layout);
    g_free (layout);
Daiki Ueno's avatar
Daiki Ueno committed
755
756
757
758

    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 keyboard,
                                                 NULL);
759
    g_free (keyboard);
Daiki Ueno's avatar
Daiki Ueno committed
760
761
762
    if (keyboard_id == 0)
        return FALSE;
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
763

Daiki Ueno's avatar
Daiki Ueno committed
764
    return TRUE;
765
766
}

767
768
769
770
771
772
773
static void
on_xkl_state_changed (XklEngine           *xklengine,
                      XklEngineStateChange type,
                      gint                 value,
                      gboolean             restore,
                      gpointer             user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
774
    Client *client = user_data;
775

776
777
    if (type == GROUP_CHANGED)
        eekboard_context_set_group (client->context, value, NULL);
778
779
}

780
#ifdef HAVE_XTEST
781
782
783
784
785
786
787
788
/* The following functions for keyboard mapping change are direct
   translation of the code in Caribou (in libcaribou/xadapter.vala):

   - get_replaced_keycode (Caribou: get_reserved_keycode)
   - replace_keycode
   - get_keycode_from_gdk_keymap (Caribou: best_keycode_keyval_match)
*/
static guint
Daiki Ueno's avatar
Daiki Ueno committed
789
get_replaced_keycode (Client *client)
790
{
Daiki Ueno's avatar
Daiki Ueno committed
791
792
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
793
794
795
796
    gint i;

    for (i = client->xkb->max_key_code; i >= client->xkb->min_key_code; --i)
        if (client->xkb->map->key_sym_map[i].kt_index[0] == XkbOneLevelIndex &&
Daiki Ueno's avatar
Daiki Ueno committed
797
            XKeycodeToKeysym (xdisplay, i, 0) != 0)
798
799
            return i;

Daiki Ueno's avatar
Daiki Ueno committed
800
    return XKeysymToKeycode (xdisplay, 0x0023); /* XK_numbersign */
801
802
803
804
805
806
807
808
809
810
}

/* Replace keysym assigned to KEYCODE to KEYSYM.  Both args are used
   as in-out.  If KEYCODE points to 0, this function picks a keycode
   from the current map and replace the associated keysym to KEYSYM.
   In that case, the replaced keycode is stored in KEYCODE and the old
   keysym is stored in KEYSYM.  If otherwise (KEYCODE points to
   non-zero keycode), it simply changes the current map with the
   specified KEYCODE and KEYSYM. */
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
811
replace_keycode (Client *client,
812
813
814
                 guint          *keycode,
                 guint          *keysym)
{
Daiki Ueno's avatar
Daiki Ueno committed
815
816
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
817
818
819
820
821
822
823
824
825
826
    guint offset;
    XkbMapChangesRec changes;
    guint replaced_keycode, replaced_keysym;

    g_assert (keycode != NULL);
    g_assert (keysym != NULL && *keysym != 0);

    replaced_keycode = get_replaced_keycode (client);
    if (replaced_keycode == 0)
        return FALSE;
Daiki Ueno's avatar
Daiki Ueno committed
827
    replaced_keysym = XKeycodeToKeysym (xdisplay, replaced_keycode, 0);
Daiki Ueno's avatar
Daiki Ueno committed
828
    XSync (xdisplay, False);
829
830
831
832
833
834
835
836

    offset = client->xkb->map->key_sym_map[replaced_keycode].offset;
    client->xkb->map->syms[offset] = *keysym;

    changes.changed = XkbKeySymsMask;
    changes.first_key_sym = replaced_keycode;
    changes.num_key_syms = 1;

Daiki Ueno's avatar
Daiki Ueno committed
837
    XkbChangeMap (xdisplay, client->xkb, &changes);
Daiki Ueno's avatar
Daiki Ueno committed
838
    XSync (xdisplay, False);
839
840
841
842
843
844
845
846

    *keycode = replaced_keycode;
    *keysym = replaced_keysym;

    return TRUE;
}

static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
847
get_keycode_from_gdk_keymap (Client *client,
848
849
850
851
852
                             guint           keysym,
                             guint          *keycode,
                             guint          *modifiers)
{
    GdkKeymap *keymap = gdk_keymap_get_default ();
Daiki Ueno's avatar
Daiki Ueno committed
853
    GdkKeymapKey *keys, *best_match = NULL;
854
855
856
857
858
859
    gint n_keys, i;

    if (!gdk_keymap_get_entries_for_keyval (keymap, keysym, &keys, &n_keys))
        return FALSE;

    for (i = 0; i < n_keys; i++)
860
        if (keys[i].group == eekboard_context_get_group (client->context, NULL))
861
862
            best_match = &keys[i];

Daiki Ueno's avatar
Daiki Ueno committed
863
864
865
866
867
    if (!best_match) {
        g_free (keys);
        return FALSE;
    }

868
869
870
871
872
873
874
    *keycode = best_match->keycode;
    *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0;

    g_free (keys);
    return TRUE;
}

875
static void
Daiki Ueno's avatar
Daiki Ueno committed
876
send_fake_modifier_key_event (Client *client,
877
878
                              EekModifierType modifiers,
                              gboolean        is_pressed)
Daiki Ueno's avatar
Daiki Ueno committed
879
{
Daiki Ueno's avatar
Daiki Ueno committed
880
    GdkDisplay *display = gdk_display_get_default ();
881
    gint i;
Daiki Ueno's avatar
Daiki Ueno committed
882

883
884
885
    for (i = 0; i < G_N_ELEMENTS(client->modifier_keycodes); i++) {
        if (modifiers & (1 << i)) {
            guint keycode = client->modifier_keycodes[i];
Daiki Ueno's avatar
Daiki Ueno committed
886

887
888
            g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
889
            XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
890
891
892
893
894
895
896
897
                               keycode,
                               is_pressed,
                               CurrentTime);
        }
    }
}

static void
898
899
900
901
send_fake_key_event (Client    *client,
                     EekSymbol *symbol,
                     guint      keyboard_modifiers,
                     gboolean   is_pressed)
902
{
Daiki Ueno's avatar
Daiki Ueno committed
903
    GdkDisplay *display = gdk_display_get_default ();
904
    EekModifierType modifiers;
905
    guint xkeysym;
906
    guint keycode, replaced_keysym = 0;
907

908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
    /* Ignore modifier keys */
    if (eek_symbol_is_modifier (symbol))
        return;

    /* If symbol is a text, convert chars in it to keysym */
    if (EEK_IS_TEXT(symbol)) {
        gchar *utf8 = eek_text_get_text (EEK_TEXT(symbol));
        glong items_written;
        gunichar *ucs4 = g_utf8_to_ucs4_fast (utf8, -1, &items_written);
        gint i;

        for (i = 0; i < items_written; i++) {
            EekKeysym *keysym;
            gchar *name;

            name = g_strdup_printf ("U%04X", ucs4[i]);
            xkeysym = XStringToKeysym (name);
            g_free (name);

            keysym = eek_keysym_new (xkeysym);
            send_fake_key_event (client,
                                 EEK_SYMBOL(keysym),
                                 keyboard_modifiers,
                                 is_pressed);
        }
        g_free (ucs4);
        return;
    }

    /* Ignore special keys */
    if (!EEK_IS_KEYSYM(symbol))
939
940
941
942
943
        return;

    xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
    g_return_if_fail (xkeysym > 0);

944
945
946
947
948
949
950
951
    modifiers = 0;
    if (!get_keycode_from_gdk_keymap (client, xkeysym, &keycode, &modifiers)) {
        keycode = 0;
        replaced_keysym = xkeysym;
        if (!replace_keycode (client, &keycode, &replaced_keysym)) {
            g_warning ("failed to lookup X keysym %X", xkeysym);
            return;
        }
952
    }
953
954
955
956
    
    /* Clear level shift modifiers */
    keyboard_modifiers &= ~EEK_SHIFT_MASK;
    keyboard_modifiers &= ~EEK_LOCK_MASK;
Daiki Ueno's avatar
Daiki Ueno committed
957
958
959
960
    /* FIXME: may need to remap ISO_Level3_Shift and NumLock */
    //keyboard_modifiers &= ~EEK_MOD5_MASK;
    //keyboard_modifiers &= ~client->alt_gr_mask;
    //keyboard_modifiers &= ~client->num_lock_mask;
961
962

    modifiers |= keyboard_modifiers;
963
964

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

Daiki Ueno's avatar
Daiki Ueno committed
967
    keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
968
969
970
                                xkeysym);
    g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
971
    XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
972
973
974
                       keycode,
                       is_pressed,
                       CurrentTime);
Daiki Ueno's avatar
Daiki Ueno committed
975
    XSync (GDK_DISPLAY_XDISPLAY (display), False);
976
977
978

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

981
static void
982
on_key_pressed (EekboardContext *context,
Daiki Ueno's avatar
Daiki Ueno committed
983
                const gchar     *keyname,
984
985
986
                EekSymbol       *symbol,
                guint            modifiers,
                gpointer         user_data)
987
{
Daiki Ueno's avatar
Daiki Ueno committed
988
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
989
990
991
992
993
994

    if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) {
        client->keyboards = g_slist_next (client->keyboards);
        eekboard_context_set_keyboard (client->context,
                                       GPOINTER_TO_UINT(client->keyboards->data),
                                       NULL);
Daiki Ueno's avatar
Daiki Ueno committed
995
        return;
Daiki Ueno's avatar
Daiki Ueno committed
996
997
    }

Daiki Ueno's avatar
Daiki Ueno committed
998
999
1000
    if (g_strcmp0 (eek_symbol_get_name (symbol), "preferences") == 0) {
        PreferencesDialog *dialog = preferences_dialog_new ();
        preferences_dialog_run (dialog);
Daiki Ueno's avatar
Daiki Ueno committed
1001
        return;
Daiki Ueno's avatar
Daiki Ueno committed
1002
1003
1004
    }


1005
1006
    send_fake_key_event (client, symbol, modifiers, TRUE);
    send_fake_key_event (client, symbol, modifiers, FALSE);
1007
}
1008

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

Daiki Ueno's avatar
Daiki Ueno committed
1016
    mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (display));
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
    for (i = 0; i < 8; i++) {
        client->modifier_keycodes[i] = 0;
        for (j = 0; j < mods->max_keypermod; j++) {
            KeyCode keycode = mods->modifiermap[mods->max_keypermod * i + j];
            if (keycode != 0) {
                client->modifier_keycodes[i] = keycode;
                break;
            }
        }
    }
1027
    XFreeModifiermap (mods);
1028
1029
1030
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
1031
client_enable_xtest (Client *client)
1032
{
Daiki Ueno's avatar
Daiki Ueno committed
1033
    GdkDisplay *display = gdk_display_get_default ();
1034
    int opcode, event_base, error_base, major_version, minor_version;
1035

Daiki Ueno's avatar
Daiki Ueno committed
1036
    g_assert (display);
1037

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

Daiki Ueno's avatar
Daiki Ueno committed
1045
    if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1046
1047
1048
1049
1050
1051
1052
                            &opcode, &event_base, &error_base,
                            &major_version, &minor_version)) {
        g_warning ("Xkb extension is not available");
        return FALSE;
    }

    if (!client->xkb)
Daiki Ueno's avatar
Daiki Ueno committed
1053
        client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (display),
1054
1055
                                 XkbKeySymsMask,
                                 XkbUseCoreKbd);
1056
1057
    g_assert (client->xkb);

1058
    update_modifier_keycodes (client);
1059

1060
    client->key_pressed_handler =
1061
        g_signal_connect (client->context, "key-pressed",
1062
1063
1064
1065
1066
1067
                          G_CALLBACK(on_key_pressed), client);

    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
1068
client_disable_xtest (Client *client)
1069
{
1070
1071
1072
1073
    if (client->xkb) {
        XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        client->xkb = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
1074
}
1075
#endif  /* HAVE_XTEST */