blob: b0eb2f311309918a810bb230644fac63e701a6ca [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;
SkinRotation rotation;
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 SkinKeyCode
skin_keyboard_key_to_code(SkinKeyboard* keyboard,
int code,
int mod,
int down)
{
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);
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 (doAltR) skin_keyboard_add_key_event(keyboard, kKeyCodeAltRight, 0);
if (doAltL) skin_keyboard_add_key_event(keyboard, kKeyCodeAltLeft, 0);
if (doCapR) skin_keyboard_add_key_event(keyboard, kKeyCodeCapRight, 0);
if (doCapL) skin_keyboard_add_key_event(keyboard, kKeyCodeCapLeft, 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:
;
}
}
D("could not handle (code=%d, mod=%d, str=%s)", code, mod,
skin_key_pair_to_string(code,mod));
return -1;
}
void
skin_keyboard_process_event(SkinKeyboard* kb, SkinEvent* ev, int down)
{
if (ev->type == kEventTextInput) {
// 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_add_key_event(kb, code, down);
skin_keyboard_flush(kb);
return;
}
code = keycode;
if (code == kKeyCodeAltLeft || code == kKeyCodeAltRight ||
code == kKeyCodeCapLeft || code == kKeyCodeCapRight ||
code == kKeyCodeSym) {
return;
}
if (code == KEY_APPSWITCH || code == KEY_PLAYPAUSE ||
code == KEY_BACK || code == KEY_POWER ||
code == KEY_BACKSPACE || code == KEY_SOFT1 ||
code == KEY_CENTER || code == KEY_REWIND ||
code == KEY_ENTER || code == KEY_VOLUMEDOWN ||
code == KEY_FASTFORWARD || code == KEY_VOLUMEUP ||
code == KEY_HOME )
{
skin_keyboard_add_key_event(kb, code, down);
skin_keyboard_flush(kb);
return;
}
D("ignoring keycode %d", keycode);
}
}
void
skin_keyboard_set_rotation( SkinKeyboard* keyboard,
SkinRotation rotation )
{
keyboard->rotation = (rotation & 3);
}
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,
SkinRotation dpad_rotation,
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 );
}
skin_keycode_buffer_init(kb->keycodes, keycode_flush);
skin_keyboard_set_rotation(kb, dpad_rotation);
return kb;
}
SkinKeyboard* skin_keyboard_create(const char* kcm_file_path,
SkinRotation dpad_rotation,
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, dpad_rotation,
keycode_flush);
}
void
skin_keyboard_free( SkinKeyboard* keyboard )
{
if (keyboard) {
AFREE(keyboard);
}
}