eek-keyboard.c 24.7 KB
Newer Older
Daiki Ueno's avatar
Daiki Ueno committed
1
/* 
Daiki Ueno's avatar
Daiki Ueno committed
2 3
 * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
 * Copyright (C) 2010-2011 Red Hat, Inc.
Daiki Ueno's avatar
Daiki Ueno committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

/**
 * SECTION:eek-keyboard
Daiki Ueno's avatar
Daiki Ueno committed
23
 * @short_description: Base class of a keyboard
Daiki Ueno's avatar
Daiki Ueno committed
24 25
 * @see_also: #EekSection
 *
Daiki Ueno's avatar
Daiki Ueno committed
26 27
 * The #EekKeyboardClass class represents a keyboard, which consists
 * of one or more sections of the #EekSectionClass class.
Daiki Ueno's avatar
Daiki Ueno committed
28 29 30 31 32 33 34
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif  /* HAVE_CONFIG_H */

#include "eek-keyboard.h"
Daiki Ueno's avatar
Daiki Ueno committed
35 36
#include "eek-section.h"
#include "eek-key.h"
37
#include "eek-symbol.h"
Daiki Ueno's avatar
Daiki Ueno committed
38
#include "eek-enumtypes.h"
Daiki Ueno's avatar
Daiki Ueno committed
39 40 41

enum {
    PROP_0,
42
    PROP_LAYOUT,
43
    PROP_MODIFIER_BEHAVIOR,
Daiki Ueno's avatar
Daiki Ueno committed
44 45 46 47 48 49
    PROP_LAST
};

enum {
    KEY_PRESSED,
    KEY_RELEASED,
Daiki Ueno's avatar
Daiki Ueno committed
50 51 52
    KEY_LOCKED,
    KEY_UNLOCKED,
    KEY_CANCELLED,
Daiki Ueno's avatar
Daiki Ueno committed
53 54 55 56 57
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0, };

58
G_DEFINE_TYPE (EekKeyboard, eek_keyboard, EEK_TYPE_CONTAINER);
Daiki Ueno's avatar
Daiki Ueno committed
59 60 61 62 63 64

#define EEK_KEYBOARD_GET_PRIVATE(obj)                                  \
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_KEYBOARD, EekKeyboardPrivate))

struct _EekKeyboardPrivate
{
Daiki Ueno's avatar
Daiki Ueno committed
65
    EekLayout *layout;
66 67
    EekModifierBehavior modifier_behavior;
    EekModifierType modifiers;
Daiki Ueno's avatar
Daiki Ueno committed
68 69
    GList *pressed_keys;
    GList *locked_keys;
70
    GArray *outline_array;
71
    GHashTable *keycodes;
72 73

    /* modifiers dynamically assigned at run time */
74
    EekModifierType num_lock_mask;
75
    EekModifierType alt_gr_mask;
Daiki Ueno's avatar
Daiki Ueno committed
76 77
};

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
G_DEFINE_BOXED_TYPE(EekModifierKey, eek_modifier_key,
                    eek_modifier_key_copy, eek_modifier_key_free);

EekModifierKey *
eek_modifier_key_copy (EekModifierKey *modkey)
{
    return g_slice_dup (EekModifierKey, modkey);
}

void
eek_modifier_key_free (EekModifierKey *modkey)
{
    g_object_unref (modkey->key);
    g_slice_free (EekModifierKey, modkey);
}

Daiki Ueno's avatar
Daiki Ueno committed
94
static void
Daiki Ueno's avatar
Daiki Ueno committed
95 96 97
on_key_pressed (EekSection  *section,
                EekKey      *key,
                EekKeyboard *keyboard)
Daiki Ueno's avatar
Daiki Ueno committed
98
{
Daiki Ueno's avatar
Daiki Ueno committed
99
    g_signal_emit (keyboard, signals[KEY_PRESSED], 0, key);
Daiki Ueno's avatar
Daiki Ueno committed
100 101 102
}

static void
Daiki Ueno's avatar
Daiki Ueno committed
103 104 105
on_key_released (EekSection  *section,
                 EekKey      *key,
                 EekKeyboard *keyboard)
Daiki Ueno's avatar
Daiki Ueno committed
106
{
Daiki Ueno's avatar
Daiki Ueno committed
107
    g_signal_emit (keyboard, signals[KEY_RELEASED], 0, key);
Daiki Ueno's avatar
Daiki Ueno committed
108 109
}

Daiki Ueno's avatar
Daiki Ueno committed
110 111 112 113 114
static void
on_key_locked (EekSection  *section,
                EekKey      *key,
                EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
115
    g_signal_emit (keyboard, signals[KEY_LOCKED], 0, key);
Daiki Ueno's avatar
Daiki Ueno committed
116 117 118 119 120 121 122
}

static void
on_key_unlocked (EekSection  *section,
                 EekKey      *key,
                 EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
123
    g_signal_emit (keyboard, signals[KEY_UNLOCKED], 0, key);
Daiki Ueno's avatar
Daiki Ueno committed
124 125 126 127 128 129 130
}

static void
on_key_cancelled (EekSection  *section,
                 EekKey      *key,
                 EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
131
    g_signal_emit (keyboard, signals[KEY_CANCELLED], 0, key);
Daiki Ueno's avatar
Daiki Ueno committed
132 133
}

134 135 136 137 138 139 140 141 142
static void
on_symbol_index_changed (EekSection *section,
                         gint group,
                         gint level,
                         EekKeyboard *keyboard)
{
    g_signal_emit_by_name (keyboard, "symbol-index-changed", group, level);
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
static void
section_child_added_cb (EekContainer *container,
                        EekElement   *element,
                        EekKeyboard  *keyboard)
{
    guint keycode = eek_key_get_keycode (EEK_KEY(element));
    g_hash_table_insert (keyboard->priv->keycodes,
                         GUINT_TO_POINTER(keycode),
                         element);
}

static void
section_child_removed_cb (EekContainer *container,
                          EekElement   *element,
                          EekKeyboard  *keyboard)
{
    guint keycode = eek_key_get_keycode (EEK_KEY(element));
    g_hash_table_remove (keyboard->priv->keycodes,
                         GUINT_TO_POINTER(keycode));
}

Daiki Ueno's avatar
Daiki Ueno committed
164 165 166 167
static EekSection *
eek_keyboard_real_create_section (EekKeyboard *self)
{
    EekSection *section;
Daiki Ueno's avatar
Daiki Ueno committed
168

Daiki Ueno's avatar
Daiki Ueno committed
169 170 171
    section = g_object_new (EEK_TYPE_SECTION, NULL);
    g_return_val_if_fail (section, NULL);

172 173 174 175 176 177
    g_signal_connect (G_OBJECT(section), "child-added",
                      G_CALLBACK(section_child_added_cb), self);

    g_signal_connect (G_OBJECT(section), "child-removed",
                      G_CALLBACK(section_child_removed_cb), self);

Daiki Ueno's avatar
Daiki Ueno committed
178 179 180 181 182 183
    EEK_CONTAINER_GET_CLASS(self)->add_child (EEK_CONTAINER(self),
                                              EEK_ELEMENT(section));
    return section;
}

static void
Daiki Ueno's avatar
Daiki Ueno committed
184 185 186 187
eek_keyboard_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
                           GParamSpec   *pspec)
Daiki Ueno's avatar
Daiki Ueno committed
188
{
189
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(object);
Daiki Ueno's avatar
Daiki Ueno committed
190 191

    switch (prop_id) {
192 193
    case PROP_LAYOUT:
        priv->layout = g_value_get_object (value);
194 195
        if (priv->layout)
            g_object_ref (priv->layout);
196
        break;
197 198
    case PROP_MODIFIER_BEHAVIOR:
        eek_keyboard_set_modifier_behavior (EEK_KEYBOARD(object),
Daiki Ueno's avatar
Daiki Ueno committed
199
                                            g_value_get_enum (value));
200
        break;
Daiki Ueno's avatar
Daiki Ueno committed
201
    default:
Daiki Ueno's avatar
Daiki Ueno committed
202
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Daiki Ueno's avatar
Daiki Ueno committed
203 204 205 206 207 208 209 210 211 212
        break;
    }
}

static void
eek_keyboard_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
213
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(object);
Daiki Ueno's avatar
Daiki Ueno committed
214 215

    switch (prop_id) {
216 217 218
    case PROP_LAYOUT:
        g_value_set_object (value, priv->layout);
        break;
219
    case PROP_MODIFIER_BEHAVIOR:
Daiki Ueno's avatar
Daiki Ueno committed
220 221
        g_value_set_enum (value,
                          eek_keyboard_get_modifier_behavior (EEK_KEYBOARD(object)));
222
        break;
Daiki Ueno's avatar
Daiki Ueno committed
223
    default:
Daiki Ueno's avatar
Daiki Ueno committed
224
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Daiki Ueno's avatar
Daiki Ueno committed
225 226 227 228
        break;
    }
}

229 230 231 232
static void
set_level_from_modifiers (EekKeyboard *self)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
233
    gint level = 0;
234

235
    if (priv->modifiers & priv->alt_gr_mask)
236 237 238
        level |= 2;
    if (priv->modifiers & EEK_SHIFT_MASK)
        level |= 1;
239
    eek_element_set_level (EEK_ELEMENT(self), level);
240 241
}

Daiki Ueno's avatar
Daiki Ueno committed
242 243 244 245 246 247 248 249 250 251 252 253 254
static void
set_modifiers_with_key (EekKeyboard    *self,
                        EekKey         *key,
                        EekModifierType modifiers)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
    EekModifierType enabled = (priv->modifiers ^ modifiers) & modifiers;
    EekModifierType disabled = (priv->modifiers ^ modifiers) & priv->modifiers;

    if (enabled != 0) {
        if (priv->modifier_behavior != EEK_MODIFIER_BEHAVIOR_NONE) {
            EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
            modifier_key->modifiers = enabled;
255
            modifier_key->key = g_object_ref (key);
Daiki Ueno's avatar
Daiki Ueno committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
            priv->locked_keys =
                g_list_prepend (priv->locked_keys, modifier_key);
            g_signal_emit_by_name (modifier_key->key, "locked");
        }
    } else {
        if (priv->modifier_behavior != EEK_MODIFIER_BEHAVIOR_NONE) {
            GList *head;
            for (head = priv->locked_keys; head; ) {
                EekModifierKey *modifier_key = head->data;
                if (modifier_key->modifiers & disabled) {
                    GList *next = g_list_next (head);
                    priv->locked_keys =
                        g_list_remove_link (priv->locked_keys, head);
                    g_signal_emit_by_name (modifier_key->key, "unlocked");
                    g_list_free1 (head);
                    head = next;
                } else
                    head = g_list_next (head);
            }
        }
    }

    priv->modifiers = modifiers;
}

281 282 283 284 285
static void
eek_keyboard_real_key_pressed (EekKeyboard *self,
                               EekKey      *key)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
286
    EekSymbol *symbol;
287 288
    EekModifierType modifier;

Daiki Ueno's avatar
Daiki Ueno committed
289 290
    priv->pressed_keys = g_list_prepend (priv->pressed_keys, key);

291
    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
292 293 294 295
    if (!symbol)
        return;

    modifier = eek_symbol_get_modifier_mask (symbol);
296
    if (priv->modifier_behavior == EEK_MODIFIER_BEHAVIOR_NONE) {
Daiki Ueno's avatar
Daiki Ueno committed
297
        set_modifiers_with_key (self, key, priv->modifiers | modifier);
298
        set_level_from_modifiers (self);
299 300 301 302 303
    }
}

static void
eek_keyboard_real_key_released (EekKeyboard *self,
Daiki Ueno's avatar
Daiki Ueno committed
304
                                EekKey      *key)
305 306
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
307
    EekSymbol *symbol;
308 309
    EekModifierType modifier;

Daiki Ueno's avatar
Daiki Ueno committed
310 311
    EEK_KEYBOARD_GET_CLASS (self)->key_cancelled (self, key);

312
    symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
313 314 315 316
    if (!symbol)
        return;

    modifier = eek_symbol_get_modifier_mask (symbol);
317 318
    switch (priv->modifier_behavior) {
    case EEK_MODIFIER_BEHAVIOR_NONE:
Daiki Ueno's avatar
Daiki Ueno committed
319
        set_modifiers_with_key (self, key, priv->modifiers & ~modifier);
320 321 322 323 324
        break;
    case EEK_MODIFIER_BEHAVIOR_LOCK:
        priv->modifiers ^= modifier;
        break;
    case EEK_MODIFIER_BEHAVIOR_LATCH:
Daiki Ueno's avatar
Daiki Ueno committed
325 326
        if (modifier)
            set_modifiers_with_key (self, key, priv->modifiers ^ modifier);
327
        else
Daiki Ueno's avatar
Daiki Ueno committed
328 329
            set_modifiers_with_key (self, key,
                                    (priv->modifiers ^ modifier) & modifier);
330
        break;
331 332 333 334
    }
    set_level_from_modifiers (self);
}

Daiki Ueno's avatar
Daiki Ueno committed
335 336 337 338 339 340 341
static void
eek_keyboard_real_key_cancelled (EekKeyboard *self,
                                 EekKey      *key)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
    GList *head;

342 343 344
    for (head = priv->pressed_keys; head; head = g_list_next (head)) {
        if (head->data == key) {
            priv->pressed_keys = g_list_remove_link (priv->pressed_keys, head);
Daiki Ueno's avatar
Daiki Ueno committed
345
            g_list_free1 (head);
346 347
            break;
        }
Daiki Ueno's avatar
Daiki Ueno committed
348 349 350
    }
}

Daiki Ueno's avatar
Daiki Ueno committed
351 352 353 354 355 356 357 358 359 360 361 362 363
static void
eek_keyboard_dispose (GObject *object)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(object);

    if (priv->layout) {
        g_object_unref (priv->layout);
        priv->layout = NULL;
    }

    G_OBJECT_CLASS (eek_keyboard_parent_class)->dispose (object);
}

364 365 366 367 368 369
static void
eek_keyboard_finalize (GObject *object)
{
    EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(object);
    gint i;

Daiki Ueno's avatar
Daiki Ueno committed
370
    g_list_free (priv->pressed_keys);
371 372
    g_list_free_full (priv->locked_keys,
                      (GDestroyNotify) eek_modifier_key_free);
Daiki Ueno's avatar
Daiki Ueno committed
373

374 375
    g_hash_table_destroy (priv->keycodes);

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    for (i = 0; i < priv->outline_array->len; i++) {
        EekOutline *outline = &g_array_index (priv->outline_array,
                                              EekOutline,
                                              i);
        g_slice_free1 (sizeof (EekPoint) * outline->num_points,
                       outline->points);
    }
    g_array_free (priv->outline_array, TRUE);
        
    G_OBJECT_CLASS (eek_keyboard_parent_class)->finalize (object);
}

static void
eek_keyboard_real_child_added (EekContainer *self,
                               EekElement   *element)
{
    g_signal_connect (element, "key-pressed",
                      G_CALLBACK(on_key_pressed), self);
    g_signal_connect (element, "key-released",
                      G_CALLBACK(on_key_released), self);
Daiki Ueno's avatar
Daiki Ueno committed
396 397 398 399 400 401
    g_signal_connect (element, "key-locked",
                      G_CALLBACK(on_key_locked), self);
    g_signal_connect (element, "key-unlocked",
                      G_CALLBACK(on_key_unlocked), self);
    g_signal_connect (element, "key-cancelled",
                      G_CALLBACK(on_key_cancelled), self);
402 403
    g_signal_connect (element, "symbol-index-changed",
                      G_CALLBACK(on_symbol_index_changed), self);
404 405 406 407 408 409 410 411
}

static void
eek_keyboard_real_child_removed (EekContainer *self,
                                 EekElement   *element)
{
    g_signal_handlers_disconnect_by_func (element, on_key_pressed, self);
    g_signal_handlers_disconnect_by_func (element, on_key_released, self);
Daiki Ueno's avatar
Daiki Ueno committed
412 413 414
    g_signal_handlers_disconnect_by_func (element, on_key_locked, self);
    g_signal_handlers_disconnect_by_func (element, on_key_unlocked, self);
    g_signal_handlers_disconnect_by_func (element, on_key_cancelled, self);
415 416
}

Daiki Ueno's avatar
Daiki Ueno committed
417 418 419
static void
eek_keyboard_class_init (EekKeyboardClass *klass)
{
420
    EekContainerClass *container_class = EEK_CONTAINER_CLASS (klass);
Daiki Ueno's avatar
Daiki Ueno committed
421 422 423 424 425 426 427 428
    GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
    GParamSpec        *pspec;

    g_type_class_add_private (gobject_class,
                              sizeof (EekKeyboardPrivate));

    klass->create_section = eek_keyboard_real_create_section;

429 430 431
    /* signals */
    klass->key_pressed = eek_keyboard_real_key_pressed;
    klass->key_released = eek_keyboard_real_key_released;
Daiki Ueno's avatar
Daiki Ueno committed
432
    klass->key_cancelled = eek_keyboard_real_key_cancelled;
Daiki Ueno's avatar
Daiki Ueno committed
433

434 435 436
    container_class->child_added = eek_keyboard_real_child_added;
    container_class->child_removed = eek_keyboard_real_child_removed;

Daiki Ueno's avatar
Daiki Ueno committed
437 438
    gobject_class->get_property = eek_keyboard_get_property;
    gobject_class->set_property = eek_keyboard_set_property;
Daiki Ueno's avatar
Daiki Ueno committed
439
    gobject_class->dispose = eek_keyboard_dispose;
440
    gobject_class->finalize = eek_keyboard_finalize;
Daiki Ueno's avatar
Daiki Ueno committed
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455
    /**
     * EekKeyboard:layout:
     *
     * The layout used to create this #EekKeyboard.
     */
    pspec = g_param_spec_object ("layout",
                                 "Layout",
                                 "Layout used to create the keyboard",
                                 EEK_TYPE_LAYOUT,
                                 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
    g_object_class_install_property (gobject_class,
                                     PROP_LAYOUT,
                                     pspec);

456 457 458 459 460
    /**
     * EekKeyboard:modifier-behavior:
     *
     * The modifier handling mode of #EekKeyboard.
     */
Daiki Ueno's avatar
Daiki Ueno committed
461 462 463 464 465 466
    pspec = g_param_spec_enum ("modifier-behavior",
                               "Modifier behavior",
                               "Modifier handling mode of the keyboard",
                               EEK_TYPE_MODIFIER_BEHAVIOR,
                               EEK_MODIFIER_BEHAVIOR_NONE,
                               G_PARAM_READWRITE);
467 468 469 470
    g_object_class_install_property (gobject_class,
                                     PROP_MODIFIER_BEHAVIOR,
                                     pspec);

Daiki Ueno's avatar
Daiki Ueno committed
471 472 473 474 475 476 477 478
    /**
     * EekKeyboard::key-pressed:
     * @keyboard: an #EekKeyboard
     * @key: an #EekKey
     *
     * The ::key-pressed signal is emitted each time a key in @keyboard
     * is shifted to the pressed state.
     */
Daiki Ueno's avatar
Daiki Ueno committed
479
    signals[KEY_PRESSED] =
480
        g_signal_new (I_("key-pressed"),
Daiki Ueno's avatar
Daiki Ueno committed
481
                      G_TYPE_FROM_CLASS(gobject_class),
482 483
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(EekKeyboardClass, key_pressed),
Daiki Ueno's avatar
Daiki Ueno committed
484 485 486 487 488 489 490
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      EEK_TYPE_KEY);

Daiki Ueno's avatar
Daiki Ueno committed
491 492 493 494 495 496 497 498
    /**
     * EekKeyboard::key-released:
     * @keyboard: an #EekKeyboard
     * @key: an #EekKey
     *
     * The ::key-released signal is emitted each time a key in @keyboard
     * is shifted to the released state.
     */
Daiki Ueno's avatar
Daiki Ueno committed
499
    signals[KEY_RELEASED] =
500
        g_signal_new (I_("key-released"),
Daiki Ueno's avatar
Daiki Ueno committed
501
                      G_TYPE_FROM_CLASS(gobject_class),
502 503
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(EekKeyboardClass, key_released),
Daiki Ueno's avatar
Daiki Ueno committed
504 505 506 507 508 509
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      EEK_TYPE_KEY);
Daiki Ueno's avatar
Daiki Ueno committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569

    /**
     * EekKeyboard::key-locked:
     * @keyboard: an #EekKeyboard
     * @key: an #EekKey
     *
     * The ::key-locked signal is emitted each time a key in @keyboard
     * is shifted to the locked state.
     */
    signals[KEY_LOCKED] =
        g_signal_new (I_("key-locked"),
                      G_TYPE_FROM_CLASS(gobject_class),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(EekKeyboardClass, key_locked),
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      EEK_TYPE_KEY);

    /**
     * EekKeyboard::key-unlocked:
     * @keyboard: an #EekKeyboard
     * @key: an #EekKey
     *
     * The ::key-unlocked signal is emitted each time a key in @keyboard
     * is shifted to the unlocked state.
     */
    signals[KEY_UNLOCKED] =
        g_signal_new (I_("key-unlocked"),
                      G_TYPE_FROM_CLASS(gobject_class),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(EekKeyboardClass, key_unlocked),
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      EEK_TYPE_KEY);

    /**
     * EekKeyboard::key-cancelled:
     * @keyboard: an #EekKeyboard
     * @key: an #EekKey
     *
     * The ::key-cancelled signal is emitted each time a key in @keyboard
     * is shifted to the cancelled state.
     */
    signals[KEY_CANCELLED] =
        g_signal_new (I_("key-cancelled"),
                      G_TYPE_FROM_CLASS(gobject_class),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(EekKeyboardClass, key_cancelled),
                      NULL,
                      NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      EEK_TYPE_KEY);
Daiki Ueno's avatar
Daiki Ueno committed
570 571 572 573 574
}

static void
eek_keyboard_init (EekKeyboard *self)
{
Daiki Ueno's avatar
Daiki Ueno committed
575 576 577
    self->priv = EEK_KEYBOARD_GET_PRIVATE(self);
    self->priv->modifier_behavior = EEK_MODIFIER_BEHAVIOR_NONE;
    self->priv->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
578
    self->priv->keycodes = g_hash_table_new (g_direct_hash, g_direct_equal);
579
    eek_element_set_symbol_index (EEK_ELEMENT(self), 0, 0);
Daiki Ueno's avatar
Daiki Ueno committed
580 581 582 583
}

/**
 * eek_keyboard_create_section:
Daiki Ueno's avatar
Daiki Ueno committed
584
 * @keyboard: an #EekKeyboard
Daiki Ueno's avatar
Daiki Ueno committed
585
 *
Daiki Ueno's avatar
Daiki Ueno committed
586 587 588
 * Create an #EekSection instance and append it to @keyboard.  This
 * function is rarely called by application but called by #EekLayout
 * implementation.
Daiki Ueno's avatar
Daiki Ueno committed
589 590
 */
EekSection *
Daiki Ueno's avatar
Daiki Ueno committed
591
eek_keyboard_create_section (EekKeyboard *keyboard)
Daiki Ueno's avatar
Daiki Ueno committed
592
{
Daiki Ueno's avatar
Daiki Ueno committed
593
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
Daiki Ueno's avatar
Daiki Ueno committed
594
    return EEK_KEYBOARD_GET_CLASS(keyboard)->create_section (keyboard);
Daiki Ueno's avatar
Daiki Ueno committed
595 596
}

Daiki Ueno's avatar
Daiki Ueno committed
597 598 599 600 601 602
/**
 * eek_keyboard_find_key_by_keycode:
 * @keyboard: an #EekKeyboard
 * @keycode: a keycode
 *
 * Find an #EekKey whose keycode is @keycode.
Daiki Ueno's avatar
Daiki Ueno committed
603
 * Return value: (transfer none): #EekKey whose keycode is @keycode
Daiki Ueno's avatar
Daiki Ueno committed
604
 */
Daiki Ueno's avatar
Daiki Ueno committed
605 606 607 608
EekKey *
eek_keyboard_find_key_by_keycode (EekKeyboard *keyboard,
                                  guint        keycode)
{
Daiki Ueno's avatar
Daiki Ueno committed
609
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
610 611
    return g_hash_table_lookup (keyboard->priv->keycodes,
                                GUINT_TO_POINTER(keycode));
Daiki Ueno's avatar
Daiki Ueno committed
612
}
613

Daiki Ueno's avatar
Daiki Ueno committed
614 615 616 617 618 619 620
/**
 * eek_keyboard_get_layout:
 * @keyboard: an #EekKeyboard
 *
 * Get the layout used to create @keyboard.
 * Returns: an #EekLayout
 */
621 622
EekLayout *
eek_keyboard_get_layout (EekKeyboard *keyboard)
623
{
Daiki Ueno's avatar
Daiki Ueno committed
624 625
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
    return keyboard->priv->layout;
626
}
627

Daiki Ueno's avatar
Daiki Ueno committed
628 629 630 631 632 633 634 635
/**
 * eek_keyboard_get_size:
 * @keyboard: an #EekKeyboard
 * @width: width of @keyboard
 * @height: height of @keyboard
 *
 * Get the size of @keyboard.
 */
636 637 638 639 640 641
void
eek_keyboard_get_size (EekKeyboard *keyboard,
                       gdouble     *width,
                       gdouble     *height)
{
    EekBounds bounds;
642

643 644 645
    eek_element_get_bounds (EEK_ELEMENT(keyboard), &bounds);
    *width = bounds.width;
    *height = bounds.height;
646
}
647

Daiki Ueno's avatar
Daiki Ueno committed
648 649 650 651 652 653 654
/**
 * eek_keyboard_set_modifier_behavior:
 * @keyboard: an #EekKeyboard
 * @modifier_behavior: modifier behavior of @keyboard
 *
 * Set the modifier handling mode of @keyboard.
 */
655 656 657 658 659
void
eek_keyboard_set_modifier_behavior (EekKeyboard        *keyboard,
                                    EekModifierBehavior modifier_behavior)
{
    g_return_if_fail (EEK_IS_KEYBOARD(keyboard));
Daiki Ueno's avatar
Daiki Ueno committed
660
    keyboard->priv->modifier_behavior = modifier_behavior;
661 662
}

Daiki Ueno's avatar
Daiki Ueno committed
663 664 665 666 667 668 669
/**
 * eek_keyboard_get_modifier_behavior:
 * @keyboard: an #EekKeyboard
 *
 * Get the modifier handling mode of @keyboard.
 * Returns: #EekModifierBehavior
 */
670 671 672
EekModifierBehavior
eek_keyboard_get_modifier_behavior (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
673 674
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
    return keyboard->priv->modifier_behavior;
675 676
}

Daiki Ueno's avatar
Daiki Ueno committed
677 678 679 680
void
eek_keyboard_set_modifiers (EekKeyboard    *keyboard,
                            EekModifierType modifiers)
{
Daiki Ueno's avatar
Daiki Ueno committed
681 682
    g_return_if_fail (EEK_IS_KEYBOARD(keyboard));
    keyboard->priv->modifiers = modifiers;
Daiki Ueno's avatar
Daiki Ueno committed
683 684 685
    set_level_from_modifiers (keyboard);
}

Daiki Ueno's avatar
Daiki Ueno committed
686 687 688 689 690 691 692
/**
 * eek_keyboard_get_modifiers:
 * @keyboard: an #EekKeyboard
 *
 * Get the current modifier status of @keyboard.
 * Returns: #EekModifierType
 */
693 694 695
EekModifierType
eek_keyboard_get_modifiers (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
696 697
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
    return keyboard->priv->modifiers;
698
}
699 700 701 702 703 704 705

/**
 * eek_keyboard_add_outline:
 * @keyboard: an #EekKeyboard
 * @outline: an #EekOutline
 *
 * Register an outline of @keyboard.
Daiki Ueno's avatar
Daiki Ueno committed
706 707
 * Returns: an unsigned integer ID of the registered outline, for
 * later reference
708
 */
Daiki Ueno's avatar
Daiki Ueno committed
709
guint
710 711 712 713 714
eek_keyboard_add_outline (EekKeyboard *keyboard,
                          EekOutline  *outline)
{
    EekOutline *_outline;

Daiki Ueno's avatar
Daiki Ueno committed
715
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
716 717

    _outline = eek_outline_copy (outline);
Daiki Ueno's avatar
Daiki Ueno committed
718
    g_array_append_val (keyboard->priv->outline_array, *_outline);
Daiki Ueno's avatar
Daiki Ueno committed
719 720
    /* don't use eek_outline_free here, so as to keep _outline->points */
    g_slice_free (EekOutline, _outline);
Daiki Ueno's avatar
Daiki Ueno committed
721
    return keyboard->priv->outline_array->len - 1;
722 723 724 725 726
}

/**
 * eek_keyboard_get_outline:
 * @keyboard: an #EekKeyboard
Daiki Ueno's avatar
Daiki Ueno committed
727
 * @oref: ID of the outline
728 729 730 731 732 733
 *
 * Get an outline associated with @oref in @keyboard.
 * Returns: an #EekOutline, which should not be released
 */
EekOutline *
eek_keyboard_get_outline (EekKeyboard *keyboard,
Daiki Ueno's avatar
Daiki Ueno committed
734
                          guint        oref)
735
{
Daiki Ueno's avatar
Daiki Ueno committed
736
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
737

Daiki Ueno's avatar
Daiki Ueno committed
738
    if (oref > keyboard->priv->outline_array->len)
739 740
        return NULL;

Daiki Ueno's avatar
Daiki Ueno committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    return &g_array_index (keyboard->priv->outline_array, EekOutline, oref);
}

/**
 * eek_keyboard_get_n_outlines:
 * @keyboard: an #EekKeyboard
 *
 * Get the number of outlines defined in @keyboard.
 * Returns: integer
 */
gsize
eek_keyboard_get_n_outlines (EekKeyboard *keyboard)
{
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
    return keyboard->priv->outline_array->len;
756
}
757

758 759 760 761 762 763 764
/**
 * eek_keyboard_set_num_lock_mask:
 * @keyboard: an #EekKeyboard
 * @num_lock_mask: an #EekModifierType
 *
 * Set modifier mask used as Num_Lock.
 */
765 766 767 768
void
eek_keyboard_set_num_lock_mask (EekKeyboard    *keyboard,
                                EekModifierType num_lock_mask)
{
Daiki Ueno's avatar
Daiki Ueno committed
769 770
    g_return_if_fail (EEK_IS_KEYBOARD(keyboard));
    keyboard->priv->num_lock_mask = num_lock_mask;
771 772
}

773 774 775 776 777 778 779
/**
 * eek_keyboard_get_num_lock_mask:
 * @keyboard: an #EekKeyboard
 *
 * Get modifier mask used as Num_Lock.
 * Returns: an #EekModifierType
 */
780 781 782
EekModifierType
eek_keyboard_get_num_lock_mask (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
783 784
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
    return keyboard->priv->num_lock_mask;
785
}
786 787 788 789 790 791 792 793 794 795 796 797

/**
 * eek_keyboard_set_alt_gr_mask:
 * @keyboard: an #EekKeyboard
 * @alt_gr_mask: an #EekModifierType
 *
 * Set modifier mask used as Alt_Gr.
 */
void
eek_keyboard_set_alt_gr_mask (EekKeyboard    *keyboard,
                              EekModifierType alt_gr_mask)
{
Daiki Ueno's avatar
Daiki Ueno committed
798 799
    g_return_if_fail (EEK_IS_KEYBOARD(keyboard));
    keyboard->priv->alt_gr_mask = alt_gr_mask;
800 801 802 803 804 805 806 807 808 809 810 811
}

/**
 * eek_keyboard_get_alt_gr_mask:
 * @keyboard: an #EekKeyboard
 *
 * Get modifier mask used as Alt_Gr.
 * Returns: an #EekModifierType
 */
EekModifierType
eek_keyboard_get_alt_gr_mask (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
812 813
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), 0);
    return keyboard->priv->alt_gr_mask;
814
}
Daiki Ueno's avatar
Daiki Ueno committed
815 816 817 818 819 820

/**
 * eek_keyboard_get_pressed_keys:
 * @keyboard: an #EekKeyboard
 *
 * Get pressed keys.
Daiki Ueno's avatar
Daiki Ueno committed
821 822
 * Returns: (transfer container) (element-type EekKey): A list of
 * pressed keys.
Daiki Ueno's avatar
Daiki Ueno committed
823 824 825 826
 */
GList *
eek_keyboard_get_pressed_keys (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
827
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
828
    return g_list_copy (keyboard->priv->pressed_keys);
Daiki Ueno's avatar
Daiki Ueno committed
829 830 831 832 833 834 835
}

/**
 * eek_keyboard_get_locked_keys:
 * @keyboard: an #EekKeyboard
 *
 * Get locked keys.
836
 * Returns: (transfer container) (element-type Eek.ModifierKey): A list
Daiki Ueno's avatar
Daiki Ueno committed
837 838 839 840 841
 * of locked keys.
 */
GList *
eek_keyboard_get_locked_keys (EekKeyboard *keyboard)
{
Daiki Ueno's avatar
Daiki Ueno committed
842
    g_return_val_if_fail (EEK_IS_KEYBOARD(keyboard), NULL);
843
    return g_list_copy (keyboard->priv->locked_keys);
Daiki Ueno's avatar
Daiki Ueno committed
844
}