blob: e05390aed7eaef3323a68708112484f6562f4f21 [file] [log] [blame]
/* 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);
}
}