| /* Copyright (C) 2007-2008 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** 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. |
| */ |
| #include "android/skin/keyboard.h" |
| |
| #include "android/skin/charmap.h" |
| #include "android/skin/keycode.h" |
| #include "android/skin/keycode-buffer.h" |
| #include "android/utils/debug.h" |
| #include "android/utils/bufprint.h" |
| #include "android/utils/system.h" |
| #include "android/utils/utf8_utils.h" |
| |
| #include <stdio.h> |
| |
| #define DEBUG 1 |
| |
| #if DEBUG |
| # define D(...) VERBOSE_PRINT(keys,__VA_ARGS__) |
| #else |
| # define D(...) ((void)0) |
| #endif |
| |
| struct SkinKeyboard { |
| const SkinCharmap* charmap; |
| SkinKeyset* kset; |
| char enabled; |
| char raw_keys; |
| |
| SkinRotation rotation; |
| |
| SkinKeyCommandFunc command_func; |
| void* command_opaque; |
| SkinKeyEventFunc press_func; |
| void* press_opaque; |
| |
| SkinKeycodeBuffer keycodes[1]; |
| }; |
| |
| #define DEFAULT_ANDROID_CHARMAP "qwerty2" |
| |
| static bool skin_key_code_is_arrow(int code) { |
| return code == kKeyCodeDpadLeft || |
| code == kKeyCodeDpadRight || |
| code == kKeyCodeDpadUp || |
| code == kKeyCodeDpadDown; |
| } |
| |
| static void |
| skin_keyboard_cmd( SkinKeyboard* keyboard, |
| SkinKeyCommand command, |
| int param ) |
| { |
| if (keyboard->command_func) { |
| keyboard->command_func( keyboard->command_opaque, command, param ); |
| } |
| } |
| |
| static SkinKeyCode |
| skin_keyboard_key_to_code(SkinKeyboard* keyboard, |
| int code, |
| int mod, |
| int down) |
| { |
| SkinKeyCommand command; |
| |
| D("key code=%d mod=%d str=%s", |
| code, |
| mod, |
| skin_key_pair_to_string(code, mod)); |
| |
| /* first, handle the arrow keys directly */ |
| if (skin_key_code_is_arrow(code)) { |
| code = skin_keycode_rotate(code, -keyboard->rotation); |
| D("handling arrow (code=%d mod=%d)", code, mod); |
| if (!keyboard->raw_keys) { |
| int doCapL, doCapR, doAltL, doAltR; |
| |
| doCapL = mod & kKeyModLShift; |
| doCapR = mod & kKeyModRShift; |
| doAltL = mod & kKeyModLAlt; |
| doAltR = mod & kKeyModRAlt; |
| |
| if (down) { |
| if (doAltL) skin_keyboard_add_key_event(keyboard, kKeyCodeAltLeft, 1); |
| if (doAltR) skin_keyboard_add_key_event(keyboard, kKeyCodeAltRight, 1); |
| if (doCapL) skin_keyboard_add_key_event(keyboard, kKeyCodeCapLeft, 1); |
| if (doCapR) skin_keyboard_add_key_event(keyboard, kKeyCodeCapRight, 1); |
| } |
| skin_keyboard_add_key_event(keyboard, code, down); |
| |
| if (!down) { |
| if (doCapR) skin_keyboard_add_key_event(keyboard, kKeyCodeCapRight, 0); |
| if (doCapL) skin_keyboard_add_key_event(keyboard, kKeyCodeCapLeft, 0); |
| if (doAltR) skin_keyboard_add_key_event(keyboard, kKeyCodeAltRight, 0); |
| if (doAltL) skin_keyboard_add_key_event(keyboard, kKeyCodeAltLeft, 0); |
| } |
| code = 0; |
| } |
| return code; |
| } |
| |
| /* special case for keypad keys, ignore them here if numlock is on */ |
| if ((mod & kKeyModNumLock) != 0) { |
| switch ((int)code) { |
| case KEY_KP0: |
| case KEY_KP1: |
| case KEY_KP2: |
| case KEY_KP3: |
| case KEY_KP4: |
| case KEY_KP5: |
| case KEY_KP6: |
| case KEY_KP7: |
| case KEY_KP8: |
| case KEY_KP9: |
| case KEY_KPPLUS: |
| case KEY_KPMINUS: |
| case KEY_KPASTERISK: |
| case KEY_KPSLASH: |
| case KEY_KPEQUAL: |
| case KEY_KPDOT: |
| case KEY_KPENTER: |
| return 0; |
| default: |
| ; |
| } |
| } |
| |
| /* now try all keyset combos */ |
| command = skin_keyset_get_command(keyboard->kset, code, mod); |
| if (command != SKIN_KEY_COMMAND_NONE) { |
| D("handling command %s from (sym=%d, mod=%d, str=%s)", |
| skin_key_command_to_str(command), |
| code, |
| mod, |
| skin_key_pair_to_string(code, mod)); |
| skin_keyboard_cmd(keyboard, command, down); |
| return 0; |
| } |
| D("could not handle (code=%d, mod=%d, str=%s)", code, mod, |
| skin_key_pair_to_string(code,mod)); |
| return -1; |
| } |
| |
| /* this gets called only if the reverse unicode mapping didn't work |
| * or wasn't used (when in raw keys mode) |
| */ |
| |
| void |
| skin_keyboard_enable( SkinKeyboard* keyboard, |
| int enabled ) |
| { |
| keyboard->enabled = enabled; |
| if (enabled) { |
| skin_event_enable_unicode(!keyboard->raw_keys); |
| } |
| } |
| |
| static void |
| skin_keyboard_do_key_event( SkinKeyboard* kb, |
| SkinKeyCode code, |
| int down ) |
| { |
| if (kb->press_func) { |
| kb->press_func( kb->press_opaque, code, down ); |
| } |
| skin_keyboard_add_key_event(kb, code, down); |
| } |
| |
| void |
| skin_keyboard_process_event(SkinKeyboard* kb, SkinEvent* ev, int down) |
| { |
| /* ignore key events if we're not enabled */ |
| if (!kb->enabled) { |
| //printf( "ignoring key event\n"); |
| return; |
| } |
| |
| if (ev->type == kEventTextInput) { |
| if (!kb->raw_keys) { |
| // TODO(digit): For each Unicode value in the input text. |
| const uint8_t* text = ev->u.text.text; |
| const uint8_t* end = text + sizeof(ev->u.text.text); |
| while (text < end && *text) { |
| uint32_t codepoint = 0; |
| int len = android_utf8_decode(text, end - text, &codepoint); |
| if (len < 0) { |
| break; |
| } |
| skin_keyboard_process_unicode_event(kb, codepoint, 1); |
| skin_keyboard_process_unicode_event(kb, codepoint, 0); |
| text += len; |
| } |
| skin_keyboard_flush(kb); |
| } |
| } else if (ev->type == kEventKeyDown || ev->type == kEventKeyUp) { |
| int keycode = ev->u.key.keycode; |
| int mod = ev->u.key.mod; |
| |
| /* first, try the keyboard-mode-independent keys */ |
| int code = skin_keyboard_key_to_code(kb, keycode, mod, down); |
| if (code == 0) { |
| return; |
| } |
| if ((int)code > 0) { |
| skin_keyboard_do_key_event(kb, code, down); |
| skin_keyboard_flush(kb); |
| return; |
| } |
| |
| /* Ctrl-K is used to switch between 'unicode' and 'raw' modes */ |
| if (keycode == kKeyCodeK) { |
| if (mod == kKeyModLCtrl || mod == kKeyModRCtrl) { |
| if (down) { |
| kb->raw_keys = !kb->raw_keys; |
| skin_event_enable_unicode(!kb->raw_keys); |
| D( "switching keyboard to %s mode", kb->raw_keys ? "raw" : "unicode" ); |
| } |
| return; |
| } |
| } |
| |
| code = keycode; |
| |
| if ( !kb->raw_keys && |
| (code == kKeyCodeAltLeft || code == kKeyCodeAltRight || |
| code == kKeyCodeCapLeft || code == kKeyCodeCapRight || |
| code == kKeyCodeSym) ) { |
| return; |
| } |
| |
| if (code == KEY_BACKSPACE || code == KEY_ENTER) { |
| skin_keyboard_do_key_event(kb, code, down); |
| skin_keyboard_flush(kb); |
| return; |
| } |
| D("ignoring keycode %d", keycode); |
| } |
| } |
| |
| void |
| skin_keyboard_set_keyset( SkinKeyboard* keyboard, SkinKeyset* kset ) |
| { |
| if (kset == NULL) |
| return; |
| if (keyboard->kset && keyboard->kset != skin_keyset_get_default()) { |
| skin_keyset_free(keyboard->kset); |
| } |
| keyboard->kset = kset; |
| } |
| |
| |
| void |
| skin_keyboard_set_rotation( SkinKeyboard* keyboard, |
| SkinRotation rotation ) |
| { |
| keyboard->rotation = (rotation & 3); |
| } |
| |
| void |
| skin_keyboard_on_command( SkinKeyboard* keyboard, SkinKeyCommandFunc cmd_func, void* cmd_opaque ) |
| { |
| keyboard->command_func = cmd_func; |
| keyboard->command_opaque = cmd_opaque; |
| } |
| |
| void |
| skin_keyboard_on_key_press( SkinKeyboard* keyboard, SkinKeyEventFunc press_func, void* press_opaque ) |
| { |
| keyboard->press_func = press_func; |
| keyboard->press_opaque = press_opaque; |
| } |
| |
| void |
| skin_keyboard_add_key_event( SkinKeyboard* kb, |
| unsigned code, |
| unsigned down ) |
| { |
| skin_keycode_buffer_add(kb->keycodes, code, down); |
| } |
| |
| |
| void |
| skin_keyboard_flush( SkinKeyboard* kb ) |
| { |
| skin_keycode_buffer_flush(kb->keycodes); |
| } |
| |
| |
| int |
| skin_keyboard_process_unicode_event( SkinKeyboard* kb, unsigned int unicode, int down ) |
| { |
| return skin_charmap_reverse_map_unicode(kb->charmap, unicode, down, |
| kb->keycodes); |
| } |
| |
| |
| static SkinKeyboard* |
| skin_keyboard_create_from_charmap_name(const char* charmap_name, |
| int use_raw_keys, |
| SkinKeyCodeFlushFunc keycode_flush) |
| { |
| SkinKeyboard* kb; |
| |
| ANEW0(kb); |
| |
| kb->charmap = skin_charmap_get_by_name(charmap_name); |
| if (!kb->charmap) { |
| // Charmap name was not found. Default to "qwerty2" */ |
| kb->charmap = skin_charmap_get_by_name(DEFAULT_ANDROID_CHARMAP); |
| fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n", |
| charmap_name, kb->charmap->name ); |
| } |
| kb->raw_keys = use_raw_keys; |
| kb->enabled = 0; |
| |
| /* add default keyset */ |
| if (skin_keyset_get_default()) { |
| kb->kset = skin_keyset_get_default(); |
| } else { |
| kb->kset = skin_keyset_new_from_text( |
| skin_keyset_get_default_text()); |
| } |
| skin_keycode_buffer_init(kb->keycodes, keycode_flush); |
| return kb; |
| } |
| |
| SkinKeyboard* |
| skin_keyboard_create(const char* kcm_file_path, |
| int use_raw_keys, |
| SkinKeyCodeFlushFunc keycode_flush) |
| { |
| const char* charmap_name = DEFAULT_ANDROID_CHARMAP; |
| char cmap_buff[SKIN_CHARMAP_NAME_SIZE]; |
| |
| if (kcm_file_path != NULL) { |
| kcm_extract_charmap_name(kcm_file_path, cmap_buff, sizeof cmap_buff); |
| charmap_name = cmap_buff; |
| } |
| return skin_keyboard_create_from_charmap_name( |
| charmap_name, use_raw_keys, keycode_flush); |
| } |
| |
| void |
| skin_keyboard_free( SkinKeyboard* keyboard ) |
| { |
| if (keyboard) { |
| AFREE(keyboard); |
| } |
| } |