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

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 */
Daiki Ueno's avatar
Daiki Ueno committed
130
static gboolean        set_keyboard         (Client         *client,
131
                                             const gchar            *keyboard);
Daiki Ueno's avatar
Daiki Ueno committed
132
static gboolean        set_keyboard_from_xkl (Client         *client);
133
134
#ifdef HAVE_XTEST
static void            update_modifier_keycodes
Daiki Ueno's avatar
Daiki Ueno committed
135
(Client         *client);
136
#endif  /* HAVE_XTEST */
137
138

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

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

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

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

    switch (prop_id) {
Daiki Ueno's avatar
Daiki Ueno committed
183
184
185
    case PROP_EEKBOARD:
        g_value_set_object (value, client->eekboard);
        break;
186
187
188
189
190
191
192
193
194
195
196
    case PROP_CONTEXT:
        g_value_set_object (value, client->context);
        break;
    default:
        g_object_get_property (object,
                               g_param_spec_get_name (pspec),
                               value);
        break;
    }
}

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

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

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

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

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

221
    if (client->context) {
222
        if (client->eekboard) {
Daiki Ueno's avatar
Daiki Ueno committed
223
            eekboard_client_pop_context (client->eekboard, NULL);
224
225
226
227
228
229
        }

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

230
231
232
    if (client->eekboard) {
        g_object_unref (client->eekboard);
        client->eekboard = NULL;
233
234
    }

235
236
237
238
239
    if (client->settings) {
        g_object_unref (client->settings);
        client->settings = NULL;
    }

Daiki Ueno's avatar
Daiki Ueno committed
240
    G_OBJECT_CLASS (client_parent_class)->dispose (object);
241
242
}

Daiki Ueno's avatar
Daiki Ueno committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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);
}

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

Daiki Ueno's avatar
Daiki Ueno committed
264
265
266
    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
267
    gobject_class->finalize = client_finalize;
268
269
270
271
272
273
274
275
276

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

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

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

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

303
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
304
305
client_set_keyboard (Client *client,
                     const gchar    *keyboard)
306
307
308
{
    gboolean retval;
    retval = set_keyboard (client, keyboard);
309
    if (retval && IS_KEYBOARD_VISIBLE (client))
310
311
        eekboard_context_show_keyboard (client->context, NULL);
    return retval;
312
313
}

314
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
315
client_enable_xkl (Client *client)
316
{
Daiki Ueno's avatar
Daiki Ueno committed
317
    GdkDisplay *display = gdk_display_get_default ();
318
319
    gboolean retval;

Daiki Ueno's avatar
Daiki Ueno committed
320
    g_assert (display);
321

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

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

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

350
351
352
353
354
    retval = set_keyboard_from_xkl (client);
    if (IS_KEYBOARD_VISIBLE (client))
        eekboard_context_show_keyboard (client->context, NULL);

    return retval;
355
356
357
}

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

        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;
    }
373
374
}

Daiki Ueno's avatar
Daiki Ueno committed
375
#ifdef HAVE_ATSPI
376
gboolean
Daiki Ueno's avatar
Daiki Ueno committed
377
client_enable_atspi_focus (Client *client)
378
{
Daiki Ueno's avatar
Daiki Ueno committed
379
    GError *error;
380

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

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

Daiki Ueno's avatar
Daiki Ueno committed
399
    client->follows_focus = TRUE;
400
401
402
403
    return TRUE;
}

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

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
426
client_enable_atspi_keystroke (Client *client)
427
{
Daiki Ueno's avatar
Daiki Ueno committed
428
429
    GError *error;

430
    client->keystroke_listener =
Daiki Ueno's avatar
Daiki Ueno committed
431
432
433
        atspi_device_listener_new ((AtspiDeviceListenerCB)keystroke_listener_cb,
                                   NULL,
                                   client);
434

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

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

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

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

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

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

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

Daiki Ueno's avatar
Daiki Ueno committed
560
561
562
563
564
565
566
567
568
569
570
571
    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
572
573
}

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

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

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

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

    return message;
}

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

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

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
638
client_enable_ibus_focus (Client *client)
639
640
641
642
643
644
645
646
647
648
649
650
{
    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
651
652
653
654
655
    client->follows_focus = TRUE;
    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
656
client_disable_ibus_focus (Client *client)
Daiki Ueno's avatar
Daiki Ueno committed
657
658
659
660
661
{
    GDBusConnection *connection;

    client->follows_focus = FALSE;

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

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

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

704
705
    retval = set_keyboard_from_xkl (client);
    g_return_if_fail (retval);
706

707
#ifdef HAVE_XTEST
708
    update_modifier_keycodes (client);
709
#endif  /* HAVE_XTEST */
710
711
}

712
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
713
set_keyboard (Client *client,
Daiki Ueno's avatar
Daiki Ueno committed
714
              const gchar *keyboard)
715
{
Daiki Ueno's avatar
Daiki Ueno committed
716
717
    GSList *keyboards = NULL;
    gchar **strv, **p;
718

Daiki Ueno's avatar
Daiki Ueno committed
719
720
721
    g_return_val_if_fail (keyboard != NULL, FALSE);
    g_return_val_if_fail (*keyboard != '\0', FALSE);

Daiki Ueno's avatar
Daiki Ueno committed
722
723
    if (client->keyboards)
        g_slist_free (client->keyboards);
724

Daiki Ueno's avatar
Daiki Ueno committed
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
    strv = g_strsplit (keyboard, ",", -1);
    for (p = strv; *p != NULL; p++) {
        guint keyboard_id;

        keyboard_id = eekboard_context_add_keyboard (client->context,
                                                     *p,
                                                     NULL);
        if (keyboard_id == 0)
            return FALSE;
        keyboards = g_slist_prepend (keyboards,
                                     GUINT_TO_POINTER(keyboard_id));
    }
    g_strfreev (strv);

    /* make a cycle */
    keyboards = g_slist_reverse (keyboards);
    g_slist_last (keyboards)->next = keyboards;
    client->keyboards = keyboards;

    /* select the first keyboard */
    eekboard_context_set_keyboard (client->context,
                                   GPOINTER_TO_UINT(keyboards->data),
                                   NULL);
Daiki Ueno's avatar
Daiki Ueno committed
748
749
750
    return TRUE;
}

751
static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
752
set_keyboard_from_xkl (Client *client)
753
754
755
{
    XklConfigRec *rec;
    gchar *layout, *keyboard;
Daiki Ueno's avatar
Daiki Ueno committed
756
    guint keyboard_id;
757
758
759
760
761
762
763
764

    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
765
766
767
768

    keyboard_id = eekboard_context_add_keyboard (client->context,
                                                 keyboard,
                                                 NULL);
769
    g_free (keyboard);
Daiki Ueno's avatar
Daiki Ueno committed
770
771
772
    if (keyboard_id == 0)
        return FALSE;
    eekboard_context_set_keyboard (client->context, keyboard_id, NULL);
773

Daiki Ueno's avatar
Daiki Ueno committed
774
    return TRUE;
775
776
}

777
778
779
780
781
782
783
static void
on_xkl_state_changed (XklEngine           *xklengine,
                      XklEngineStateChange type,
                      gint                 value,
                      gboolean             restore,
                      gpointer             user_data)
{
Daiki Ueno's avatar
Daiki Ueno committed
784
    Client *client = user_data;
785

786
787
    if (type == GROUP_CHANGED)
        eekboard_context_set_group (client->context, value, NULL);
788
789
}

790
#ifdef HAVE_XTEST
791
792
793
794
795
796
797
798
/* 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
799
get_replaced_keycode (Client *client)
800
{
Daiki Ueno's avatar
Daiki Ueno committed
801
802
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
803
804
805
806
    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
807
            XKeycodeToKeysym (xdisplay, i, 0) != 0)
808
809
            return i;

Daiki Ueno's avatar
Daiki Ueno committed
810
    return XKeysymToKeycode (xdisplay, 0x0023); /* XK_numbersign */
811
812
813
814
815
816
817
818
819
820
}

/* 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
821
replace_keycode (Client *client,
822
823
824
                 guint          *keycode,
                 guint          *keysym)
{
Daiki Ueno's avatar
Daiki Ueno committed
825
826
    GdkDisplay *display = gdk_display_get_default ();
    Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
827
828
829
830
831
832
833
834
835
836
    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
837
    replaced_keysym = XKeycodeToKeysym (xdisplay, replaced_keycode, 0);
Daiki Ueno's avatar
Daiki Ueno committed
838
    XSync (xdisplay, False);
839
840
841
842
843
844
845
846

    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
847
    XkbChangeMap (xdisplay, client->xkb, &changes);
Daiki Ueno's avatar
Daiki Ueno committed
848
    XSync (xdisplay, False);
849
850
851
852
853
854
855
856

    *keycode = replaced_keycode;
    *keysym = replaced_keysym;

    return TRUE;
}

static gboolean
Daiki Ueno's avatar
Daiki Ueno committed
857
get_keycode_from_gdk_keymap (Client *client,
858
859
860
861
862
                             guint           keysym,
                             guint          *keycode,
                             guint          *modifiers)
{
    GdkKeymap *keymap = gdk_keymap_get_default ();
Daiki Ueno's avatar
Daiki Ueno committed
863
    GdkKeymapKey *keys, *best_match = NULL;
864
865
866
867
868
869
    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++)
870
        if (keys[i].group == eekboard_context_get_group (client->context, NULL))
871
872
            best_match = &keys[i];

Daiki Ueno's avatar
Daiki Ueno committed
873
874
875
876
877
    if (!best_match) {
        g_free (keys);
        return FALSE;
    }

878
879
880
881
882
883
884
    *keycode = best_match->keycode;
    *modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0;

    g_free (keys);
    return TRUE;
}

885
static void
Daiki Ueno's avatar
Daiki Ueno committed
886
send_fake_modifier_key_event (Client *client,
887
888
                              EekModifierType modifiers,
                              gboolean        is_pressed)
Daiki Ueno's avatar
Daiki Ueno committed
889
{
Daiki Ueno's avatar
Daiki Ueno committed
890
    GdkDisplay *display = gdk_display_get_default ();
891
    gint i;
Daiki Ueno's avatar
Daiki Ueno committed
892

893
894
895
    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
896

897
898
            g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
899
            XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
900
901
902
903
904
905
906
907
                               keycode,
                               is_pressed,
                               CurrentTime);
        }
    }
}

static void
Daiki Ueno's avatar
Daiki Ueno committed
908
send_fake_key_event (Client *client,
909
910
                     EekSymbol      *symbol,
                     guint           keyboard_modifiers,
911
912
                     gboolean        is_pressed)
{
Daiki Ueno's avatar
Daiki Ueno committed
913
    GdkDisplay *display = gdk_display_get_default ();
914
    EekModifierType modifiers;
915
    guint xkeysym;
916
    guint keycode, replaced_keysym = 0;
917
918
919
920
921
922
923
924

    /* Ignore special keys and modifiers */
    if (!EEK_IS_KEYSYM(symbol) || eek_symbol_is_modifier (symbol))
        return;

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

925
926
927
928
929
930
931
932
    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;
        }
933
    }
934
935
936
937
    
    /* Clear level shift modifiers */
    keyboard_modifiers &= ~EEK_SHIFT_MASK;
    keyboard_modifiers &= ~EEK_LOCK_MASK;
Daiki Ueno's avatar
Daiki Ueno committed
938
939
940
941
    /* 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;
942
943

    modifiers |= keyboard_modifiers;
944
945

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

Daiki Ueno's avatar
Daiki Ueno committed
948
    keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
949
950
951
                                xkeysym);
    g_return_if_fail (keycode > 0);

Daiki Ueno's avatar
Daiki Ueno committed
952
    XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (display),
953
954
955
                       keycode,
                       is_pressed,
                       CurrentTime);
Daiki Ueno's avatar
Daiki Ueno committed
956
    XSync (GDK_DISPLAY_XDISPLAY (display), False);
957
958
959

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

962
static void
963
on_key_pressed (EekboardContext *context,
Daiki Ueno's avatar
Daiki Ueno committed
964
                const gchar     *keyname,
965
966
967
                EekSymbol       *symbol,
                guint            modifiers,
                gpointer         user_data)
968
{
Daiki Ueno's avatar
Daiki Ueno committed
969
    Client *client = user_data;
Daiki Ueno's avatar
Daiki Ueno committed
970
971
972
973
974
975

    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
976
        return;
Daiki Ueno's avatar
Daiki Ueno committed
977
978
    }

Daiki Ueno's avatar
Daiki Ueno committed
979
980
981
    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
982
        return;
Daiki Ueno's avatar
Daiki Ueno committed
983
984
985
    }


986
987
    send_fake_key_event (client, symbol, modifiers, TRUE);
    send_fake_key_event (client, symbol, modifiers, FALSE);
988
}
989

990
static void
Daiki Ueno's avatar
Daiki Ueno committed
991
update_modifier_keycodes (Client *client)
992
{
Daiki Ueno's avatar
Daiki Ueno committed
993
    GdkDisplay *display = gdk_display_get_default ();
994
995
996
    XModifierKeymap *mods;
    gint i, j;

Daiki Ueno's avatar
Daiki Ueno committed
997
    mods = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (display));
998
999
1000
1001
1002
1003
1004
1005
1006
1007
    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;
            }
        }
    }
1008
    XFreeModifiermap (mods);
1009
1010
1011
}

gboolean
Daiki Ueno's avatar
Daiki Ueno committed
1012
client_enable_xtest (Client *client)
1013
{
Daiki Ueno's avatar
Daiki Ueno committed
1014
    GdkDisplay *display = gdk_display_get_default ();
1015
    int opcode, event_base, error_base, major_version, minor_version;
1016

Daiki Ueno's avatar
Daiki Ueno committed
1017
    g_assert (display);
1018

Daiki Ueno's avatar
Daiki Ueno committed
1019
    if (!XTestQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1020
1021
1022
1023
1024
                              &event_base, &error_base,
                              &major_version, &minor_version)) {
        g_warning ("XTest extension is not available");
        return FALSE;
    }
1025

Daiki Ueno's avatar
Daiki Ueno committed
1026
    if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (display),
1027
1028
1029
1030
1031
1032
1033
                            &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
1034
        client->xkb = XkbGetMap (GDK_DISPLAY_XDISPLAY (display),
1035
1036
                                 XkbKeySymsMask,
                                 XkbUseCoreKbd);
1037
1038
    g_assert (client->xkb);

1039
    update_modifier_keycodes (client);
1040

1041
    client->key_pressed_handler =
1042
        g_signal_connect (client->context, "key-pressed",
1043
1044
1045
1046
1047
1048
                          G_CALLBACK(on_key_pressed), client);

    return TRUE;
}

void
Daiki Ueno's avatar
Daiki Ueno committed
1049
client_disable_xtest (Client *client)
1050
{
1051
1052
1053
1054
    if (client->xkb) {
        XkbFreeKeyboard (client->xkb, 0, TRUE);	/* free_all = TRUE */
        client->xkb = NULL;
    }
Daiki Ueno's avatar
Daiki Ueno committed
1055
}
1056
#endif  /* HAVE_XTEST */