/* 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/utils/debug.h" #include "android/utils/bufprint.h" #include "android/utils/system.h" #include "android/android.h" #define DEBUG 1 #if DEBUG # define D(...) VERBOSE_PRINT(keys,__VA_ARGS__) #else # define D(...) ((void)0) #endif /** LAST PRESSED KEYS ** a small buffer of last pressed keys, this is used to properly ** implement the Unicode keyboard mode (SDL key up event always have ** their .unicode field set to 0 **/ typedef struct { int unicode; /* Unicode of last pressed key */ int sym; /* SDL key symbol value (e.g. SDLK_a) */ int mod; /* SDL key modifier value */ } LastKey; #define MAX_LAST_KEYS 16 #define MAX_KEYCODES 256*2 struct SkinKeyboard { const AKeyCharmap* charmap; SkinKeyset* kset; char enabled; char raw_keys; char last_count; int keycode_count; SkinRotation rotation; SkinKeyCommandFunc command_func; void* command_opaque; SkinKeyEventFunc press_func; void* press_opaque; LastKey last_keys[ MAX_LAST_KEYS ]; int keycodes[ MAX_KEYCODES ]; }; void skin_keyboard_set_keyset( SkinKeyboard* keyboard, SkinKeyset* kset ) { if (kset == NULL) return; if (keyboard->kset && keyboard->kset != android_keyset) { skin_keyset_free(keyboard->kset); } keyboard->kset = kset; } const char* skin_keyboard_charmap_name( SkinKeyboard* keyboard ) { if (keyboard && keyboard->charmap) return keyboard->charmap->name; return "qwerty"; } 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 ) { if (code != 0 && kb->keycode_count < MAX_KEYCODES) { //dprint("add keycode %d, down %d\n", code % 0x1ff, down ); kb->keycodes[(int)kb->keycode_count++] = ( (code & 0x1ff) | (down ? 0x200 : 0) ); } } void skin_keyboard_flush( SkinKeyboard* kb ) { if (kb->keycode_count > 0) { if (VERBOSE_CHECK(keys)) { int nn; printf(">> KEY" ); for (nn = 0; nn < kb->keycode_count; nn++) { int code = kb->keycodes[nn]; printf(" [0x%03x,%s]", (code & 0x1ff), (code & 0x200) ? "down" : " up " ); } printf( "\n" ); } kbd_put_keycodes(kb->keycodes, kb->keycode_count); kb->keycode_count = 0; } } static void skin_keyboard_cmd( SkinKeyboard* keyboard, SkinKeyCommand command, int param ) { if (keyboard->command_func) { keyboard->command_func( keyboard->command_opaque, command, param ); } } static LastKey* skin_keyboard_find_last( SkinKeyboard* keyboard, int sym ) { LastKey* k = keyboard->last_keys; LastKey* end = k + keyboard->last_count; for ( ; k < end; k++ ) { if (k->sym == sym) return k; } return NULL; } static void skin_keyboard_add_last( SkinKeyboard* keyboard, int sym, int mod, int unicode ) { LastKey* k = keyboard->last_keys + keyboard->last_count; if (keyboard->last_count < MAX_LAST_KEYS) { k->sym = sym; k->mod = mod; k->unicode = unicode; keyboard->last_count += 1; } } static void skin_keyboard_remove_last( SkinKeyboard* keyboard, int sym ) { LastKey* k = keyboard->last_keys; LastKey* end = k + keyboard->last_count; for ( ; k < end; k++ ) { if (k->sym == sym) { /* we don't need a sorted array, so place the last * element in place at the position of the removed * one... */ k[0] = end[-1]; keyboard->last_count -= 1; break; } } } static void skin_keyboard_clear_last( SkinKeyboard* keyboard ) { keyboard->last_count = 0; } static int skin_keyboard_rotate_sym( SkinKeyboard* keyboard, int sym ) { switch (keyboard->rotation) { case SKIN_ROTATION_90: switch (sym) { case SDLK_LEFT: sym = SDLK_DOWN; break; case SDLK_RIGHT: sym = SDLK_UP; break; case SDLK_UP: sym = SDLK_LEFT; break; case SDLK_DOWN: sym = SDLK_RIGHT; break; } break; case SKIN_ROTATION_180: switch (sym) { case SDLK_LEFT: sym = SDLK_RIGHT; break; case SDLK_RIGHT: sym = SDLK_LEFT; break; case SDLK_UP: sym = SDLK_DOWN; break; case SDLK_DOWN: sym = SDLK_UP; break; } break; case SKIN_ROTATION_270: switch (sym) { case SDLK_LEFT: sym = SDLK_UP; break; case SDLK_RIGHT: sym = SDLK_DOWN; break; case SDLK_UP: sym = SDLK_RIGHT; break; case SDLK_DOWN: sym = SDLK_LEFT; break; } break; default: ; } return sym; } static AndroidKeyCode skin_keyboard_key_to_code( SkinKeyboard* keyboard, unsigned sym, int mod, int down ) { AndroidKeyCode code = 0; int mod0 = mod; SkinKeyCommand command; /* first, handle the arrow keys directly */ /* rotate them if necessary */ sym = skin_keyboard_rotate_sym(keyboard, sym); mod &= (KMOD_CTRL | KMOD_ALT | KMOD_SHIFT); switch (sym) { case SDLK_LEFT: code = kKeyCodeDpadLeft; break; case SDLK_RIGHT: code = kKeyCodeDpadRight; break; case SDLK_UP: code = kKeyCodeDpadUp; break; case SDLK_DOWN: code = kKeyCodeDpadDown; break; default: ; } if (code != 0) { D("handling arrow (sym=%d mod=%d)", sym, mod); if (!keyboard->raw_keys) { int doCapL, doCapR, doAltL, doAltR; if (!down) { LastKey* k = skin_keyboard_find_last(keyboard, sym); if (k != NULL) { mod = k->mod; skin_keyboard_remove_last( keyboard, sym ); } } else { skin_keyboard_add_last( keyboard, sym, mod, 0); } doCapL = (mod & 0x7ff) & KMOD_LSHIFT; doCapR = (mod & 0x7ff) & KMOD_RSHIFT; doAltL = (mod & 0x7ff) & KMOD_LALT; doAltR = (mod & 0x7ff) & KMOD_RALT; 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 ((mod0 & KMOD_NUM) != 0) { switch (sym) { case SDLK_KP0: case SDLK_KP1: case SDLK_KP2: case SDLK_KP3: case SDLK_KP4: case SDLK_KP5: case SDLK_KP6: case SDLK_KP7: case SDLK_KP8: case SDLK_KP9: case SDLK_KP_PLUS: case SDLK_KP_MINUS: case SDLK_KP_MULTIPLY: case SDLK_KP_DIVIDE: case SDLK_KP_EQUALS: case SDLK_KP_PERIOD: case SDLK_KP_ENTER: return 0; } } /* now try all keyset combos */ command = skin_keyset_get_command( keyboard->kset, sym, mod ); if (command != SKIN_KEY_COMMAND_NONE) { D("handling command %s from (sym=%d, mod=%d, str=%s)", skin_key_command_to_str(command), sym, mod, skin_key_symmod_to_str(sym,mod)); skin_keyboard_cmd( keyboard, command, down ); return 0; } D("could not handle (sym=%d, mod=%d, str=%s)", sym, mod, skin_key_symmod_to_str(sym,mod)); return -1; } /* this gets called only if the reverse unicode mapping didn't work * or wasn't used (when in raw keys mode) */ static AndroidKeyCode skin_keyboard_raw_key_to_code(SkinKeyboard* kb, unsigned sym, int down) { switch(sym){ case SDLK_1: return kKeyCode1; case SDLK_2: return kKeyCode2; case SDLK_3: return kKeyCode3; case SDLK_4: return kKeyCode4; case SDLK_5: return kKeyCode5; case SDLK_6: return kKeyCode6; case SDLK_7: return kKeyCode7; case SDLK_8: return kKeyCode8; case SDLK_9: return kKeyCode9; case SDLK_0: return kKeyCode0; case SDLK_q: return kKeyCodeQ; case SDLK_w: return kKeyCodeW; case SDLK_e: return kKeyCodeE; case SDLK_r: return kKeyCodeR; case SDLK_t: return kKeyCodeT; case SDLK_y: return kKeyCodeY; case SDLK_u: return kKeyCodeU; case SDLK_i: return kKeyCodeI; case SDLK_o: return kKeyCodeO; case SDLK_p: return kKeyCodeP; case SDLK_a: return kKeyCodeA; case SDLK_s: return kKeyCodeS; case SDLK_d: return kKeyCodeD; case SDLK_f: return kKeyCodeF; case SDLK_g: return kKeyCodeG; case SDLK_h: return kKeyCodeH; case SDLK_j: return kKeyCodeJ; case SDLK_k: return kKeyCodeK; case SDLK_l: return kKeyCodeL; case SDLK_z: return kKeyCodeZ; case SDLK_x: return kKeyCodeX; case SDLK_c: return kKeyCodeC; case SDLK_v: return kKeyCodeV; case SDLK_b: return kKeyCodeB; case SDLK_n: return kKeyCodeN; case SDLK_m: return kKeyCodeM; case SDLK_COMMA: return kKeyCodeComma; case SDLK_PERIOD: return kKeyCodePeriod; case SDLK_SPACE: return kKeyCodeSpace; case SDLK_SLASH: return kKeyCodeSlash; case SDLK_RETURN: return kKeyCodeNewline; case SDLK_BACKSPACE: return kKeyCodeDel; /* these are qwerty keys not on a device keyboard */ case SDLK_TAB: return kKeyCodeTab; case SDLK_BACKQUOTE: return kKeyCodeGrave; case SDLK_MINUS: return kKeyCodeMinus; case SDLK_EQUALS: return kKeyCodeEquals; case SDLK_LEFTBRACKET: return kKeyCodeLeftBracket; case SDLK_RIGHTBRACKET: return kKeyCodeRightBracket; case SDLK_BACKSLASH: return kKeyCodeBackslash; case SDLK_SEMICOLON: return kKeyCodeSemicolon; case SDLK_QUOTE: return kKeyCodeApostrophe; case SDLK_RSHIFT: return kKeyCodeCapRight; case SDLK_LSHIFT: return kKeyCodeCapLeft; case SDLK_RMETA: return kKeyCodeSym; case SDLK_LMETA: return kKeyCodeSym; case SDLK_RALT: return kKeyCodeAltRight; case SDLK_LALT: return kKeyCodeAltLeft; case SDLK_RCTRL: return kKeyCodeSym; case SDLK_LCTRL: return kKeyCodeSym; default: /* fprintf(stderr,"* unknown sdl keysym %d *\n", sym); */ return -1; } } static void skin_keyboard_do_key_event( SkinKeyboard* kb, AndroidKeyCode code, int down ) { if (kb->press_func) { kb->press_func( kb->press_opaque, code, down ); } skin_keyboard_add_key_event(kb, code, down); } int skin_keyboard_process_unicode_event( SkinKeyboard* kb, unsigned int unicode, int down ) { const AKeyCharmap* cmap = kb->charmap; int n; if (unicode == 0) return 0; /* check base keys */ for (n = 0; n < cmap->num_entries; n++) { if (cmap->entries[n].base == unicode) { skin_keyboard_add_key_event(kb, cmap->entries[n].code, down); return 1; } } /* check caps + keys */ for (n = 0; n < cmap->num_entries; n++) { if (cmap->entries[n].caps == unicode) { if (down) skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down); skin_keyboard_add_key_event(kb, cmap->entries[n].code, down); if (!down) skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down); return 2; } } /* check fn + keys */ for (n = 0; n < cmap->num_entries; n++) { if (cmap->entries[n].fn == unicode) { if (down) skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down); skin_keyboard_add_key_event(kb, cmap->entries[n].code, down); if (!down) skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down); return 2; } } /* check caps + fn + keys */ for (n = 0; n < cmap->num_entries; n++) { if (cmap->entries[n].caps_fn == unicode) { if (down) { skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down); skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down); } skin_keyboard_add_key_event(kb, cmap->entries[n].code, down); if (!down) { skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down); skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down); } return 3; } } /* no match */ return 0; } void skin_keyboard_enable( SkinKeyboard* keyboard, int enabled ) { keyboard->enabled = enabled; if (enabled) { SDL_EnableUNICODE(!keyboard->raw_keys); SDL_EnableKeyRepeat(0,0); } } void skin_keyboard_process_event( SkinKeyboard* kb, SDL_Event* ev, int down ) { unsigned code; int unicode = ev->key.keysym.unicode; int sym = ev->key.keysym.sym; int mod = ev->key.keysym.mod; /* ignore key events if we're not enabled */ if (!kb->enabled) { printf( "ignoring key event sym=%d mod=0x%x unicode=%d\n", sym, mod, unicode ); return; } /* first, try the keyboard-mode-independent keys */ code = skin_keyboard_key_to_code( kb, sym, 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 (sym == SDLK_k) { int mod2 = mod & 0x7ff; if ( mod2 == KMOD_LCTRL || mod2 == KMOD_RCTRL ) { if (down) { skin_keyboard_clear_last(kb); kb->raw_keys = !kb->raw_keys; SDL_EnableUNICODE(!kb->raw_keys); D( "switching keyboard to %s mode", kb->raw_keys ? "raw" : "unicode" ); } return; } } if (!kb->raw_keys) { /* ev->key.keysym.unicode is only valid on keydown events, and will be 0 * on the corresponding keyup ones, so remember the set of last pressed key * syms to "undo" the job */ if ( !down && unicode == 0 ) { LastKey* k = skin_keyboard_find_last(kb, sym); if (k != NULL) { unicode = k->unicode; skin_keyboard_remove_last(kb, sym); } } } if (!kb->raw_keys && skin_keyboard_process_unicode_event( kb, unicode, down ) > 0) { if (down) skin_keyboard_add_last( kb, sym, mod, unicode ); skin_keyboard_flush( kb ); return; } code = skin_keyboard_raw_key_to_code( kb, sym, down ); if ( !kb->raw_keys && (code == kKeyCodeAltLeft || code == kKeyCodeAltRight || code == kKeyCodeCapLeft || code == kKeyCodeCapRight || code == kKeyCodeSym) ) return; if (code == -1) { D("ignoring keysym %d", sym ); } else if (code > 0) { skin_keyboard_do_key_event(kb, code, down); skin_keyboard_flush(kb); } } static SkinKeyboard* skin_keyboard_create_from_charmap_name(const char* charmap_name, int use_raw_keys) { SkinKeyboard* kb; int nn; ANEW0(kb); // Find charmap by its name in the array of available charmaps. for (nn = 0; nn < android_charmap_count; nn++) { if (!strcmp(android_charmaps[nn]->name, charmap_name)) { kb->charmap = android_charmaps[nn]; break; } } if (!kb->charmap) { // Charmap name was not found. Default to the first charmap in the array. fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n", charmap_name, android_charmaps[0]->name ); kb->charmap = android_charmaps[0]; } kb->raw_keys = use_raw_keys; kb->enabled = 0; /* add default keyset */ if (android_keyset) kb->kset = android_keyset; else kb->kset = skin_keyset_new_from_text( skin_keyset_get_default() ); return kb; } SkinKeyboard* skin_keyboard_create_from_aconfig( AConfig* aconfig, int use_raw_keys ) { const char* charmap_name = "qwerty"; AConfig* node = aconfig_find( aconfig, "keyboard" ); if (node != NULL) { charmap_name = aconfig_str(node, "charmap", charmap_name); } return skin_keyboard_create_from_charmap_name(charmap_name, use_raw_keys); } SkinKeyboard* skin_keyboard_create_from_kcm( const char* kcm_file_path, int use_raw_keys ) { char charmap_name[AKEYCHARMAP_NAME_SIZE]; kcm_extract_charmap_name(kcm_file_path, charmap_name, sizeof(charmap_name)); return skin_keyboard_create_from_charmap_name(charmap_name, use_raw_keys); } void skin_keyboard_free( SkinKeyboard* keyboard ) { if (keyboard) { AFREE(keyboard); } }