/* * Copyright (C) 2010-2011 Daiki Ueno * Copyright (C) 2010-2011 Red Hat, Inc. * * 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 * @short_description: Base class of a keyboard * @see_also: #EekSection * * The #EekKeyboardClass class represents a keyboard, which consists * of one or more sections of the #EekSectionClass class. */ #include "config.h" #include #include "eek-section.h" #include "eek-enumtypes.h" #include "eekboard/key-emitter.h" #include "keymap.h" #include "src/keyboard.h" #include "src/symbol.h" #include "eek-keyboard.h" enum { PROP_0, PROP_LAST }; enum { KEY_RELEASED, KEY_LOCKED, KEY_UNLOCKED, LAST_SIGNAL }; enum { VIEW_LETTERS_LOWER, VIEW_LETTERS_UPPER, VIEW_NUMBERS, VIEW_SYMBOLS }; #define EEK_KEYBOARD_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_KEYBOARD, EekKeyboardPrivate)) struct _EekKeyboardPrivate { char dummy; // won't run otherwise }; G_DEFINE_TYPE_WITH_PRIVATE (EekKeyboard, eek_keyboard, EEK_TYPE_ELEMENT); 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_slice_free (EekModifierKey, modkey); } struct squeek_row * eek_keyboard_real_create_row (EekKeyboard *self) { struct squeek_row *row = squeek_row_new(0); g_ptr_array_add(self->rows, row); return row; } static void eek_keyboard_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void eek_keyboard_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /// Updates the state of locked keys based on the key that was activated /// FIXME: make independent of what the key are named, /// and instead refer to the contained symbols static guint set_key_states (LevelKeyboard *keyboard, struct squeek_button *button, guint new_level) { struct squeek_key *key = squeek_button_get_key(button); // Keys locking rules hardcoded for the time being... const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key)); // Lock the shift whenever it's pressed on the baselevel // TODO: need to lock shift on the destination level if (g_strcmp0(name, "Shift_L") == 0 && keyboard->level == 0) { EekModifierKey *modifier_key = g_slice_new (EekModifierKey); modifier_key->modifiers = 0; modifier_key->button = button; keyboard->locked_buttons = g_list_prepend (keyboard->locked_buttons, modifier_key); squeek_key_set_locked(key, true); } if (keyboard->level == 1) { // Only shift is locked in this state, unlock on any key press for (GList *head = keyboard->locked_buttons; head; ) { EekModifierKey *modifier_key = head->data; GList *next = g_list_next (head); keyboard->locked_buttons = g_list_remove_link (keyboard->locked_buttons, head); squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false); g_list_free1 (head); head = next; } return 0; } return new_level; } // FIXME: unhardcode, parse some user information as to which key triggers which view (level) static void set_level_from_press (LevelKeyboard *keyboard, struct squeek_button *button) { /* The levels are: 0 Letters, 1 Upper case letters, 2 Numbers, 3 Symbols */ guint level = keyboard->level; /* Handle non-emitting keys */ if (button) { const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(squeek_button_get_key(button))); if (g_strcmp0(name, "show_numbers") == 0) { level = 2; } else if (g_strcmp0(name, "show_letters") == 0) { level = 0; } else if (g_strcmp0(name, "show_symbols") == 0) { level = 3; } else if (g_strcmp0(name, "Shift_L") == 0) { level ^= 1; } } keyboard->level = set_key_states(keyboard, button, level); eek_layout_update_layout(keyboard); } void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp) { struct squeek_key *key = squeek_button_get_key(button); squeek_key_set_pressed(key, TRUE); keyboard->pressed_buttons = g_list_prepend (keyboard->pressed_buttons, button); struct squeek_symbol *symbol = squeek_key_get_symbol(key); if (!symbol) return; // Only take action about setting level *after* the key has taken effect, i.e. on release //set_level_from_press (keyboard, key); // "Borrowed" from eek-context-service; doesn't influence the state but forwards the event guint keycode = squeek_key_get_keycode (key); emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp); } void eek_keyboard_release_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp) { for (GList *head = keyboard->pressed_buttons; head; head = g_list_next (head)) { if (head->data == button) { keyboard->pressed_buttons = g_list_remove_link (keyboard->pressed_buttons, head); g_list_free1 (head); break; } } struct squeek_symbol *symbol = squeek_button_get_symbol(button); if (!symbol) return; set_level_from_press (keyboard, button); // "Borrowed" from eek-context-service; doesn't influence the state but forwards the event guint keycode = squeek_key_get_keycode (squeek_button_get_key(button)); emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp); } static void eek_keyboard_dispose (GObject *object) { G_OBJECT_CLASS (eek_keyboard_parent_class)->dispose (object); } static void eek_keyboard_finalize (GObject *object) { G_OBJECT_CLASS (eek_keyboard_parent_class)->finalize (object); } void level_keyboard_deinit(LevelKeyboard *self) { g_hash_table_destroy (self->names); for (guint i = 0; i < self->outline_array->len; i++) { EekOutline *outline = &g_array_index (self->outline_array, EekOutline, i); g_slice_free1 (sizeof (EekPoint) * outline->num_points, outline->points); } g_array_free (self->outline_array, TRUE); for (guint i = 0; i < 4; i++) { // free self->view[i]; } } void level_keyboard_free(LevelKeyboard *self) { level_keyboard_deinit(self); g_free(self); } static void eek_keyboard_real_child_added (EekContainer *self, EekElement *element) { (void)self; (void)element; } static void eek_keyboard_real_child_removed (EekContainer *self, EekElement *element) { (void)self; (void)element; } static void eek_keyboard_class_init (EekKeyboardClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /* signals */ gobject_class->get_property = eek_keyboard_get_property; gobject_class->set_property = eek_keyboard_set_property; gobject_class->dispose = eek_keyboard_dispose; gobject_class->finalize = eek_keyboard_finalize; } static void eek_keyboard_init (EekKeyboard *self) { self->scale = 1.0; self->rows = g_ptr_array_new(); } void eek_keyboard_foreach (EekKeyboard *keyboard, GFunc func, gpointer user_data) { g_ptr_array_foreach(keyboard->rows, func, user_data); } void level_keyboard_init(LevelKeyboard *self) { self->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline)); } LevelKeyboard *level_keyboard_new(EekboardContextService *manager, EekKeyboard *views[4], GHashTable *name_button_hash) { LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1); level_keyboard_init(keyboard); for (uint i = 0; i < 4; i++) { keyboard->views[i] = views[i]; } keyboard->manager = manager; keyboard->names = name_button_hash; return keyboard; } /** * eek_keyboard_find_key_by_name: * @keyboard: an #EekKeyboard * @name: a key name * * Find an #EekKey whose name is @name. * Return value: (transfer none): #EekKey whose name is @name */ struct squeek_button* eek_keyboard_find_button_by_name (LevelKeyboard *keyboard, const gchar *name) { return g_hash_table_lookup (keyboard->names, name); } /** * eek_keyboard_get_size: * @keyboard: an #EekKeyboard * @width: width of @keyboard * @height: height of @keyboard * * Get the size of @keyboard. */ void eek_keyboard_get_size (EekKeyboard *keyboard, gdouble *width, gdouble *height) { EekBounds bounds; eek_element_get_bounds (EEK_ELEMENT(keyboard), &bounds); *width = bounds.width; *height = bounds.height; } /** * eek_keyboard_get_outline: * @keyboard: an #EekKeyboard * @oref: ID of the outline * * Get an outline associated with @oref in @keyboard. * Returns: an #EekOutline, which should not be released */ EekOutline * level_keyboard_get_outline (LevelKeyboard *keyboard, guint oref) { if (oref > keyboard->outline_array->len) return NULL; return &g_array_index (keyboard->outline_array, EekOutline, oref); } /** * eek_keyboard_get_keymap: * @keyboard: an #EekKeyboard * * Get the keymap for the keyboard. * Returns: a string containing the XKB keymap. */ gchar * eek_keyboard_get_keymap(LevelKeyboard *keyboard) { /* Start the keycodes and symbols sections with their respective headers. */ gchar *keycodes = g_strdup(keymap_keycodes_header); gchar *symbols = g_strdup(keymap_symbols_header); /* Iterate over the keys in the name-to-key hash table. */ GHashTableIter iter; gchar *button_name; gpointer button_ptr; g_hash_table_iter_init(&iter, keyboard->names); while (g_hash_table_iter_next(&iter, (gpointer)&button_name, &button_ptr)) { gchar *current, *line; struct squeek_button *button = button_ptr; struct squeek_key *key = squeek_button_get_key(button); guint keycode = squeek_key_get_keycode(key); /* Don't include invalid keycodes in the keymap. */ if (keycode == EEK_INVALID_KEYCODE) continue; /* Append a key name-to-keycode definition to the keycodes section. */ current = keycodes; line = g_strdup_printf(" <%s> = %i;\n", (char *)button_name, keycode); keycodes = g_strconcat(current, line, NULL); g_free(line); g_free(current); // FIXME: free const char *key_str = squeek_key_to_keymap_entry( (char*)button_name, key ); current = symbols; symbols = g_strconcat(current, key_str, NULL); g_free(current); } /* Assemble the keymap file from the header, sections and footer. */ gchar *keymap = g_strconcat(keymap_header, keycodes, " };\n\n", symbols, " };\n\n", keymap_footer, NULL); g_free(keycodes); g_free(symbols); return keymap; } EekKeyboard *level_keyboard_current(LevelKeyboard *keyboard) { return keyboard->views[keyboard->level]; } struct GetRowData { struct squeek_button *button; struct squeek_row *row; struct squeek_key *needle; }; void find_button_in_row(gpointer item, gpointer user_data) { struct squeek_row *row = item; struct GetRowData *data = user_data; if (data->row) { return; } if (squeek_row_contains(row, data->button)) { data->row = row; } } struct squeek_row *eek_keyboard_get_row(EekKeyboard *keyboard, struct squeek_button *button) { struct GetRowData data = { .button = button, .row = NULL, }; eek_keyboard_foreach(keyboard, find_button_in_row, &data); return data.row; } void find_key_in_row(gpointer item, gpointer user_data) { struct squeek_row *row = item; struct GetRowData *data = user_data; if (data->button) { return; } data->button = squeek_row_find_key(row, data->needle); if (data->button) { data->row = row; } } // TODO: return multiple struct button_place eek_keyboard_get_button_by_state(EekKeyboard *keyboard, struct squeek_key *key) { struct GetRowData data = { .row = NULL, .button = NULL, .needle = key, }; eek_keyboard_foreach(keyboard, find_key_in_row, &data); struct button_place ret = { .row = data.row, .button = data.button, }; return ret; }