diff options
Diffstat (limited to 'widget/gonk/libui/KeyCharacterMap.cpp')
-rw-r--r-- | widget/gonk/libui/KeyCharacterMap.cpp | 1153 |
1 files changed, 0 insertions, 1153 deletions
diff --git a/widget/gonk/libui/KeyCharacterMap.cpp b/widget/gonk/libui/KeyCharacterMap.cpp deleted file mode 100644 index cec0666ce..000000000 --- a/widget/gonk/libui/KeyCharacterMap.cpp +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "KeyCharacterMap" -#include "cutils_log.h" - -#include <stdlib.h> -#include <string.h> -#include "android_keycodes.h" -#include "Keyboard.h" -#include "KeyCharacterMap.h" - -#if HAVE_ANDROID_OS -#include <binder/Parcel.h> -#endif - -#include <utils/Errors.h> -#include "Tokenizer.h" -#include <utils/Timers.h> - -// Enables debug output for the parser. -#define DEBUG_PARSER 0 - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - - -namespace android { - -static const char* WHITESPACE = " \t\r"; -static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; - -struct Modifier { - const char* label; - int32_t metaState; -}; -static const Modifier modifiers[] = { - { "shift", AMETA_SHIFT_ON }, - { "lshift", AMETA_SHIFT_LEFT_ON }, - { "rshift", AMETA_SHIFT_RIGHT_ON }, - { "alt", AMETA_ALT_ON }, - { "lalt", AMETA_ALT_LEFT_ON }, - { "ralt", AMETA_ALT_RIGHT_ON }, - { "ctrl", AMETA_CTRL_ON }, - { "lctrl", AMETA_CTRL_LEFT_ON }, - { "rctrl", AMETA_CTRL_RIGHT_ON }, - { "meta", AMETA_META_ON }, - { "lmeta", AMETA_META_LEFT_ON }, - { "rmeta", AMETA_META_RIGHT_ON }, - { "sym", AMETA_SYM_ON }, - { "fn", AMETA_FUNCTION_ON }, - { "capslock", AMETA_CAPS_LOCK_ON }, - { "numlock", AMETA_NUM_LOCK_ON }, - { "scrolllock", AMETA_SCROLL_LOCK_ON }, -}; - -#if DEBUG_MAPPING -static String8 toString(const char16_t* chars, size_t numChars) { - String8 result; - for (size_t i = 0; i < numChars; i++) { - result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); - } - return result; -} -#endif - - -// --- KeyCharacterMap --- - -sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap(); - -KeyCharacterMap::KeyCharacterMap() : - mType(KEYBOARD_TYPE_UNKNOWN) { -} - -KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : - RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), - mKeysByUsageCode(other.mKeysByUsageCode) { - for (size_t i = 0; i < other.mKeys.size(); i++) { - mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); - } -} - -KeyCharacterMap::~KeyCharacterMap() { - for (size_t i = 0; i < mKeys.size(); i++) { - Key* key = mKeys.editValueAt(i); - delete key; - } -} - -status_t KeyCharacterMap::load(const String8& filename, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.string()); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; - } - return status; -} - -status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key character map.", status); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; - } - return status; -} - -status_t KeyCharacterMap::load(Tokenizer* tokenizer, - Format format, sp<KeyCharacterMap>* outMap) { - status_t status = OK; - sp<KeyCharacterMap> map = new KeyCharacterMap(); - if (!map.get()) { - ALOGE("Error allocating key character map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map.get(), tokenizer, format); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (!status) { - *outMap = map; - } - } - return status; -} - -sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, - const sp<KeyCharacterMap>& overlay) { - if (overlay == NULL) { - return base; - } - if (base == NULL) { - return overlay; - } - - sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get()); - for (size_t i = 0; i < overlay->mKeys.size(); i++) { - int32_t keyCode = overlay->mKeys.keyAt(i); - Key* key = overlay->mKeys.valueAt(i); - ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); - if (oldIndex >= 0) { - delete map->mKeys.valueAt(oldIndex); - map->mKeys.editValueAt(oldIndex) = new Key(*key); - } else { - map->mKeys.add(keyCode, new Key(*key)); - } - } - - for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { - map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), - overlay->mKeysByScanCode.valueAt(i)); - } - - for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { - map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), - overlay->mKeysByUsageCode.valueAt(i)); - } - return map; -} - -sp<KeyCharacterMap> KeyCharacterMap::empty() { - return sEmpty; -} - -int32_t KeyCharacterMap::getKeyboardType() const { - return mType; -} - -char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - result = key->label; - } -#if DEBUG_MAPPING - ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); -#endif - return result; -} - -char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - result = key->number; - } -#if DEBUG_MAPPING - ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); -#endif - return result; -} - -char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { - char16_t result = 0; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { - result = behavior->character; - } -#if DEBUG_MAPPING - ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); -#endif - return result; -} - -bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, - FallbackAction* outFallbackAction) const { - outFallbackAction->keyCode = 0; - outFallbackAction->metaState = 0; - - bool result = false; - const Key* key; - const Behavior* behavior; - if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { - if (behavior->fallbackKeyCode) { - outFallbackAction->keyCode = behavior->fallbackKeyCode; - outFallbackAction->metaState = metaState & ~behavior->metaState; - result = true; - } - } -#if DEBUG_MAPPING - ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " - "fallback keyCode=%d, fallback metaState=0x%08x.", - keyCode, metaState, result ? "true" : "false", - outFallbackAction->keyCode, outFallbackAction->metaState); -#endif - return result; -} - -char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, - int32_t metaState) const { - char16_t result = 0; - const Key* key; - if (getKey(keyCode, &key)) { - // Try to find the most general behavior that maps to this character. - // For example, the base key behavior will usually be last in the list. - // However, if we find a perfect meta state match for one behavior then use that one. - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character) { - for (size_t i = 0; i < numChars; i++) { - if (behavior->character == chars[i]) { - result = behavior->character; - if ((behavior->metaState & metaState) == behavior->metaState) { - goto ExactMatch; - } - break; - } - } - } - } - ExactMatch: ; - } -#if DEBUG_MAPPING - ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", - keyCode, toString(chars, numChars).string(), metaState, result); -#endif - return result; -} - -bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, - Vector<KeyEvent>& outEvents) const { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - for (size_t i = 0; i < numChars; i++) { - int32_t keyCode, metaState; - char16_t ch = chars[i]; - if (!findKey(ch, &keyCode, &metaState)) { -#if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", - deviceId, toString(chars, numChars).string(), ch); -#endif - return false; - } - - int32_t currentMetaState = 0; - addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); - addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); - addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); - addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); - } -#if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", - deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); - for (size_t i = 0; i < outEvents.size(); i++) { - ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", - outEvents[i].getKeyCode(), outEvents[i].getMetaState(), - outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); - } -#endif - return true; -} - -status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { - if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", - scanCode, usageCode, *outKeyCode); -#endif - *outKeyCode = mKeysByUsageCode.valueAt(index); - return OK; - } - } - if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", - scanCode, usageCode, *outKeyCode); -#endif - *outKeyCode = mKeysByScanCode.valueAt(index); - return OK; - } - } - -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif - *outKeyCode = AKEYCODE_UNKNOWN; - return NAME_NOT_FOUND; -} - -bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { - ssize_t index = mKeys.indexOfKey(keyCode); - if (index >= 0) { - *outKey = mKeys.valueAt(index); - return true; - } - return false; -} - -bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, - const Key** outKey, const Behavior** outBehavior) const { - const Key* key; - if (getKey(keyCode, &key)) { - const Behavior* behavior = key->firstBehavior; - while (behavior) { - if (matchesMetaState(metaState, behavior->metaState)) { - *outKey = key; - *outBehavior = behavior; - return true; - } - behavior = behavior->next; - } - } - return false; -} - -bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { - // Behavior must have at least the set of meta states specified. - // And if the key event has CTRL, ALT or META then the behavior must exactly - // match those, taking into account that a behavior can specify that it handles - // one, both or either of a left/right modifier pair. - if ((eventMetaState & behaviorMetaState) == behaviorMetaState) { - const int32_t EXACT_META_STATES = - AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON - | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON - | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON; - int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES; - if (behaviorMetaState & AMETA_CTRL_ON) { - unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_CTRL_ON; - } - if (behaviorMetaState & AMETA_ALT_ON) { - unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_ALT_ON; - } - if (behaviorMetaState & AMETA_META_ON) { - unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { - unmatchedMetaState &= ~AMETA_META_ON; - } - return !unmatchedMetaState; - } - return false; -} - -bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { - if (!ch) { - return false; - } - - for (size_t i = 0; i < mKeys.size(); i++) { - const Key* key = mKeys.valueAt(i); - - // Try to find the most general behavior that maps to this character. - // For example, the base key behavior will usually be last in the list. - const Behavior* found = NULL; - for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { - if (behavior->character == ch) { - found = behavior; - } - } - if (found) { - *outKeyCode = mKeys.keyAt(i); - *outMetaState = found->metaState; - return true; - } - } - return false; -} - -void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { - outEvents.push(); - KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - 0, keyCode, 0, metaState, 0, time, time); -} - -void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t* currentMetaState) { - // Add and remove meta keys symmetrically. - if (down) { - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); - - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, - AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, - AMETA_SHIFT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, - AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, - AMETA_ALT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, - AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, - AMETA_CTRL_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, - AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, - AMETA_META_ON, currentMetaState); - - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, - AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); - } else { - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); - - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, - AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, - AMETA_META_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, - AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, - AMETA_CTRL_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, - AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, - AMETA_ALT_ON, currentMetaState); - addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, - AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, - AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, - AMETA_SHIFT_ON, currentMetaState); - - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); - addLockedMetaKey(outEvents, deviceId, metaState, time, - AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); - } -} - -bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t keyCode, int32_t keyMetaState, - int32_t* currentMetaState) { - if ((metaState & keyMetaState) == keyMetaState) { - *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); - return true; - } - return false; -} - -void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, bool down, nsecs_t time, - int32_t leftKeyCode, int32_t leftKeyMetaState, - int32_t rightKeyCode, int32_t rightKeyMetaState, - int32_t eitherKeyMetaState, - int32_t* currentMetaState) { - bool specific = false; - specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - leftKeyCode, leftKeyMetaState, currentMetaState); - specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - rightKeyCode, rightKeyMetaState, currentMetaState); - - if (!specific) { - addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, - leftKeyCode, eitherKeyMetaState, currentMetaState); - } -} - -void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, - int32_t deviceId, int32_t metaState, nsecs_t time, - int32_t keyCode, int32_t keyMetaState, - int32_t* currentMetaState) { - if ((metaState & keyMetaState) == keyMetaState) { - *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); - *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); - addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); - } -} - -#if HAVE_ANDROID_OS -sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { - sp<KeyCharacterMap> map = new KeyCharacterMap(); - map->mType = parcel->readInt32(); - size_t numKeys = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - for (size_t i = 0; i < numKeys; i++) { - int32_t keyCode = parcel->readInt32(); - char16_t label = parcel->readInt32(); - char16_t number = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - Key* key = new Key(); - key->label = label; - key->number = number; - map->mKeys.add(keyCode, key); - - Behavior* lastBehavior = NULL; - while (parcel->readInt32()) { - int32_t metaState = parcel->readInt32(); - char16_t character = parcel->readInt32(); - int32_t fallbackKeyCode = parcel->readInt32(); - if (parcel->errorCheck()) { - return NULL; - } - - Behavior* behavior = new Behavior(); - behavior->metaState = metaState; - behavior->character = character; - behavior->fallbackKeyCode = fallbackKeyCode; - if (lastBehavior) { - lastBehavior->next = behavior; - } else { - key->firstBehavior = behavior; - } - lastBehavior = behavior; - } - - if (parcel->errorCheck()) { - return NULL; - } - } - return map; -} - -void KeyCharacterMap::writeToParcel(Parcel* parcel) const { - parcel->writeInt32(mType); - - size_t numKeys = mKeys.size(); - parcel->writeInt32(numKeys); - for (size_t i = 0; i < numKeys; i++) { - int32_t keyCode = mKeys.keyAt(i); - const Key* key = mKeys.valueAt(i); - parcel->writeInt32(keyCode); - parcel->writeInt32(key->label); - parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != NULL; - behavior = behavior->next) { - parcel->writeInt32(1); - parcel->writeInt32(behavior->metaState); - parcel->writeInt32(behavior->character); - parcel->writeInt32(behavior->fallbackKeyCode); - } - parcel->writeInt32(0); - } -} -#endif - - -// --- KeyCharacterMap::Key --- - -KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(NULL) { -} - -KeyCharacterMap::Key::Key(const Key& other) : - label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { -} - -KeyCharacterMap::Key::~Key() { - Behavior* behavior = firstBehavior; - while (behavior) { - Behavior* next = behavior->next; - delete behavior; - behavior = next; - } -} - - -// --- KeyCharacterMap::Behavior --- - -KeyCharacterMap::Behavior::Behavior() : - next(NULL), metaState(0), character(0), fallbackKeyCode(0) { -} - -KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : NULL), - metaState(other.metaState), character(other.character), - fallbackKeyCode(other.fallbackKeyCode) { -} - - -// --- KeyCharacterMap::Parser --- - -KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) : - mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) { -} - -KeyCharacterMap::Parser::~Parser() { -} - -status_t KeyCharacterMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - switch (mState) { - case STATE_TOP: { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "type") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseType(); - if (status) return status; - } else if (keywordToken == "map") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseMap(); - if (status) return status; - } else if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseKey(); - if (status) return status; - } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); - return BAD_VALUE; - } - break; - } - - case STATE_KEY: { - status_t status = parseKeyProperty(); - if (status) return status; - break; - } - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - - if (mState != STATE_TOP) { - ALOGE("%s: Unterminated key description at end of file.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { - ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - if (mFormat == FORMAT_BASE) { - if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { - ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } else if (mFormat == FORMAT_OVERLAY) { - if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { - ALOGE("%s: Overlay keyboard layout missing required keyboard " - "'type OVERLAY' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseType() { - if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { - ALOGE("%s: Duplicate keyboard 'type' declaration.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - KeyboardType type; - String8 typeToken = mTokenizer->nextToken(WHITESPACE); - if (typeToken == "NUMERIC") { - type = KEYBOARD_TYPE_NUMERIC; - } else if (typeToken == "PREDICTIVE") { - type = KEYBOARD_TYPE_PREDICTIVE; - } else if (typeToken == "ALPHA") { - type = KEYBOARD_TYPE_ALPHA; - } else if (typeToken == "FULL") { - type = KEYBOARD_TYPE_FULL; - } else if (typeToken == "SPECIAL_FUNCTION") { - type = KEYBOARD_TYPE_SPECIAL_FUNCTION; - } else if (typeToken == "OVERLAY") { - type = KEYBOARD_TYPE_OVERLAY; - } else { - ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), - typeToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed type: type=%d.", type); -#endif - mMap->mType = type; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseMap() { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - return parseMapKey(); - } - ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); - return BAD_VALUE; -} - -status_t KeyCharacterMap::Parser::parseMapKey() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - bool mapUsage = false; - if (codeToken == "usage") { - mapUsage = true; - mTokenizer->skipDelimiters(WHITESPACE); - codeToken = mTokenizer->nextToken(WHITESPACE); - } - - char* end; - int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); - if (*end) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - KeyedVector<int32_t, int32_t>& map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); - if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed map key %s: code=%d, keyCode=%d.", - mapUsage ? "usage" : "scan code", code, keyCode); -#endif - map.add(code, keyCode); - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseKey() { - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); - if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - if (mMap->mKeys.indexOfKey(keyCode) >= 0) { - ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); - if (openBraceToken != "{") { - ALOGE("%s: Expected '{' after key code label, got '%s'.", - mTokenizer->getLocation().string(), openBraceToken.string()); - return BAD_VALUE; - } - -#if DEBUG_PARSER - ALOGD("Parsed beginning of key: keyCode=%d.", keyCode); -#endif - mKeyCode = keyCode; - mMap->mKeys.add(keyCode, new Key()); - mState = STATE_KEY; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseKeyProperty() { - Key* key = mMap->mKeys.valueFor(mKeyCode); - String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - if (token == "}") { - mState = STATE_TOP; - return finishKey(key); - } - - Vector<Property> properties; - - // Parse all comma-delimited property names up to the first colon. - for (;;) { - if (token == "label") { - properties.add(Property(PROPERTY_LABEL)); - } else if (token == "number") { - properties.add(Property(PROPERTY_NUMBER)); - } else { - int32_t metaState; - status_t status = parseModifier(token, &metaState); - if (status) { - ALOGE("%s: Expected a property name or modifier, got '%s'.", - mTokenizer->getLocation().string(), token.string()); - return status; - } - properties.add(Property(PROPERTY_META, metaState)); - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - char ch = mTokenizer->nextChar(); - if (ch == ':') { - break; - } else if (ch == ',') { - mTokenizer->skipDelimiters(WHITESPACE); - token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - continue; - } - } - - ALOGE("%s: Expected ',' or ':' after property name.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - // Parse behavior after the colon. - mTokenizer->skipDelimiters(WHITESPACE); - - Behavior behavior; - bool haveCharacter = false; - bool haveFallback = false; - - do { - char ch = mTokenizer->peekChar(); - if (ch == '\'') { - char16_t character; - status_t status = parseCharacterLiteral(&character); - if (status || !character) { - ALOGE("%s: Invalid character literal for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - if (haveCharacter) { - ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - behavior.character = character; - haveCharacter = true; - } else { - token = mTokenizer->nextToken(WHITESPACE); - if (token == "none") { - if (haveCharacter) { - ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - haveCharacter = true; - } else if (token == "fallback") { - mTokenizer->skipDelimiters(WHITESPACE); - token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); - if (!keyCode) { - ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); - return BAD_VALUE; - } - if (haveFallback) { - ALOGE("%s: Cannot combine multiple fallback key codes.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - behavior.fallbackKeyCode = keyCode; - haveFallback = true; - } else { - ALOGE("%s: Expected a key behavior after ':'.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - - mTokenizer->skipDelimiters(WHITESPACE); - } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#'); - - // Add the behavior. - for (size_t i = 0; i < properties.size(); i++) { - const Property& property = properties.itemAt(i); - switch (property.property) { - case PROPERTY_LABEL: - if (key->label) { - ALOGE("%s: Duplicate label for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - key->label = behavior.character; -#if DEBUG_PARSER - ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); -#endif - break; - case PROPERTY_NUMBER: - if (key->number) { - ALOGE("%s: Duplicate number for key.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - key->number = behavior.character; -#if DEBUG_PARSER - ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); -#endif - break; - case PROPERTY_META: { - for (Behavior* b = key->firstBehavior; b; b = b->next) { - if (b->metaState == property.metaState) { - ALOGE("%s: Duplicate key behavior for modifier.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - } - Behavior* newBehavior = new Behavior(behavior); - newBehavior->metaState = property.metaState; - newBehavior->next = key->firstBehavior; - key->firstBehavior = newBehavior; -#if DEBUG_PARSER - ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, - newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); -#endif - break; - } - } - } - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::finishKey(Key* key) { - // Fill in default number property. - if (!key->number) { - char16_t digit = 0; - char16_t symbol = 0; - for (Behavior* b = key->firstBehavior; b; b = b->next) { - char16_t ch = b->character; - if (ch) { - if (ch >= '0' && ch <= '9') { - digit = ch; - } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*' - || ch == '-' || ch == '+' || ch == ',' || ch == '.' - || ch == '\'' || ch == ':' || ch == ';' || ch == '/') { - symbol = ch; - } - } - } - key->number = digit ? digit : symbol; - } - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { - if (token == "base") { - *outMetaState = 0; - return NO_ERROR; - } - - int32_t combinedMeta = 0; - - const char* str = token.string(); - const char* start = str; - for (const char* cur = str; ; cur++) { - char ch = *cur; - if (ch == '+' || ch == '\0') { - size_t len = cur - start; - int32_t metaState = 0; - for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { - if (strlen(modifiers[i].label) == len - && strncmp(modifiers[i].label, start, len) == 0) { - metaState = modifiers[i].metaState; - break; - } - } - if (!metaState) { - return BAD_VALUE; - } - if (combinedMeta & metaState) { - ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.string()); - return BAD_VALUE; - } - - combinedMeta |= metaState; - start = cur + 1; - - if (ch == '\0') { - break; - } - } - } - *outMetaState = combinedMeta; - return NO_ERROR; -} - -status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { - char ch = mTokenizer->nextChar(); - if (ch != '\'') { - goto Error; - } - - ch = mTokenizer->nextChar(); - if (ch == '\\') { - // Escape sequence. - ch = mTokenizer->nextChar(); - if (ch == 'n') { - *outCharacter = '\n'; - } else if (ch == 't') { - *outCharacter = '\t'; - } else if (ch == '\\') { - *outCharacter = '\\'; - } else if (ch == '\'') { - *outCharacter = '\''; - } else if (ch == '"') { - *outCharacter = '"'; - } else if (ch == 'u') { - *outCharacter = 0; - for (int i = 0; i < 4; i++) { - ch = mTokenizer->nextChar(); - int digit; - if (ch >= '0' && ch <= '9') { - digit = ch - '0'; - } else if (ch >= 'A' && ch <= 'F') { - digit = ch - 'A' + 10; - } else if (ch >= 'a' && ch <= 'f') { - digit = ch - 'a' + 10; - } else { - goto Error; - } - *outCharacter = (*outCharacter << 4) | digit; - } - } else { - goto Error; - } - } else if (ch >= 32 && ch <= 126 && ch != '\'') { - // ASCII literal character. - *outCharacter = ch; - } else { - goto Error; - } - - ch = mTokenizer->nextChar(); - if (ch != '\'') { - goto Error; - } - - // Ensure that we consumed the entire token. - if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { - return NO_ERROR; - } - -Error: - ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); - return BAD_VALUE; -} - -} // namespace android |