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