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 "KeyLayoutMap" michael@0: #include "cutils_log.h" michael@0: michael@0: #include michael@0: #include "android_keycodes.h" michael@0: #include "Keyboard.h" michael@0: #include "KeyLayoutMap.h" 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: michael@0: // --- KeyLayoutMap --- michael@0: michael@0: KeyLayoutMap::KeyLayoutMap() { michael@0: } michael@0: michael@0: KeyLayoutMap::~KeyLayoutMap() { michael@0: } michael@0: michael@0: status_t KeyLayoutMap::load(const String8& filename, 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 layout map file %s.", status, filename.string()); michael@0: } else { michael@0: sp map = new KeyLayoutMap(); michael@0: if (!map.get()) { michael@0: ALOGE("Error allocating key layout 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); 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 layout 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: delete tokenizer; michael@0: } michael@0: return status; michael@0: } michael@0: michael@0: status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, michael@0: int32_t* outKeyCode, uint32_t* outFlags) const { michael@0: const Key* key = getKey(scanCode, usageCode); michael@0: if (!key) { 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: *outFlags = 0; michael@0: return NAME_NOT_FOUND; michael@0: } michael@0: michael@0: *outKeyCode = key->keyCode; michael@0: *outFlags = key->flags; michael@0: michael@0: #if DEBUG_MAPPING michael@0: ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", michael@0: scanCode, usageCode, *outKeyCode, *outFlags); michael@0: #endif michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { michael@0: if (usageCode) { michael@0: ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); michael@0: if (index >= 0) { michael@0: return &mKeysByUsageCode.valueAt(index); michael@0: } michael@0: } michael@0: if (scanCode) { michael@0: ssize_t index = mKeysByScanCode.indexOfKey(scanCode); michael@0: if (index >= 0) { michael@0: return &mKeysByScanCode.valueAt(index); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector* outScanCodes) const { michael@0: const size_t N = mKeysByScanCode.size(); michael@0: for (size_t i=0; iadd(mKeysByScanCode.keyAt(i)); michael@0: } michael@0: } michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { michael@0: ssize_t index = mAxes.indexOfKey(scanCode); michael@0: if (index < 0) { michael@0: #if DEBUG_MAPPING michael@0: ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); michael@0: #endif michael@0: return NAME_NOT_FOUND; michael@0: } michael@0: michael@0: *outAxisInfo = mAxes.valueAt(index); michael@0: michael@0: #if DEBUG_MAPPING michael@0: ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " michael@0: "splitValue=%d, flatOverride=%d.", michael@0: scanCode, michael@0: outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, michael@0: outAxisInfo->splitValue, outAxisInfo->flatOverride); michael@0: #endif michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: michael@0: // --- KeyLayoutMap::Parser --- michael@0: michael@0: KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : michael@0: mMap(map), mTokenizer(tokenizer) { michael@0: } michael@0: michael@0: KeyLayoutMap::Parser::~Parser() { michael@0: } michael@0: michael@0: status_t KeyLayoutMap::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: String8 keywordToken = mTokenizer->nextToken(WHITESPACE); michael@0: if (keywordToken == "key") { michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: status_t status = parseKey(); michael@0: if (status) return status; michael@0: } else if (keywordToken == "axis") { michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: status_t status = parseAxis(); 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: 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: return NO_ERROR; michael@0: } michael@0: michael@0: status_t KeyLayoutMap::Parser::parseKey() { 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: uint32_t flags = 0; michael@0: for (;;) { michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; michael@0: michael@0: String8 flagToken = mTokenizer->nextToken(WHITESPACE); michael@0: uint32_t flag = getKeyFlagByLabel(flagToken.string()); michael@0: if (!flag) { michael@0: ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), michael@0: flagToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: if (flags & flag) { michael@0: ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), michael@0: flagToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: flags |= flag; michael@0: } michael@0: michael@0: #if DEBUG_PARSER michael@0: ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", michael@0: mapUsage ? "usage" : "scan code", code, keyCode, flags); michael@0: #endif michael@0: Key key; michael@0: key.keyCode = keyCode; michael@0: key.flags = flags; michael@0: map.add(code, key); michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: status_t KeyLayoutMap::Parser::parseAxis() { michael@0: String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); michael@0: char* end; michael@0: int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); michael@0: if (*end) { michael@0: ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), michael@0: scanCodeToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: if (mMap->mAxes.indexOfKey(scanCode) >= 0) { michael@0: ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), michael@0: scanCodeToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: AxisInfo axisInfo; michael@0: michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 token = mTokenizer->nextToken(WHITESPACE); michael@0: if (token == "invert") { michael@0: axisInfo.mode = AxisInfo::MODE_INVERT; michael@0: michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 axisToken = mTokenizer->nextToken(WHITESPACE); michael@0: axisInfo.axis = getAxisByLabel(axisToken.string()); michael@0: if (axisInfo.axis < 0) { michael@0: ALOGE("%s: Expected inverted axis label, got '%s'.", michael@0: mTokenizer->getLocation().string(), axisToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: } else if (token == "split") { michael@0: axisInfo.mode = AxisInfo::MODE_SPLIT; michael@0: michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 splitToken = mTokenizer->nextToken(WHITESPACE); michael@0: axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); michael@0: if (*end) { michael@0: ALOGE("%s: Expected split value, got '%s'.", michael@0: mTokenizer->getLocation().string(), splitToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); michael@0: axisInfo.axis = getAxisByLabel(lowAxisToken.string()); michael@0: if (axisInfo.axis < 0) { michael@0: ALOGE("%s: Expected low axis label, got '%s'.", michael@0: mTokenizer->getLocation().string(), lowAxisToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); michael@0: axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); michael@0: if (axisInfo.highAxis < 0) { michael@0: ALOGE("%s: Expected high axis label, got '%s'.", michael@0: mTokenizer->getLocation().string(), highAxisToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: } else { michael@0: axisInfo.axis = getAxisByLabel(token.string()); michael@0: if (axisInfo.axis < 0) { michael@0: ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", michael@0: mTokenizer->getLocation().string(), token.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: } michael@0: michael@0: for (;;) { michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { michael@0: break; michael@0: } michael@0: String8 keywordToken = mTokenizer->nextToken(WHITESPACE); michael@0: if (keywordToken == "flat") { michael@0: mTokenizer->skipDelimiters(WHITESPACE); michael@0: String8 flatToken = mTokenizer->nextToken(WHITESPACE); michael@0: axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); michael@0: if (*end) { michael@0: ALOGE("%s: Expected flat value, got '%s'.", michael@0: mTokenizer->getLocation().string(), flatToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: } else { michael@0: ALOGE("%s: Expected keyword 'flat', got '%s'.", michael@0: mTokenizer->getLocation().string(), keywordToken.string()); michael@0: return BAD_VALUE; michael@0: } michael@0: } michael@0: michael@0: #if DEBUG_PARSER michael@0: ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " michael@0: "splitValue=%d, flatOverride=%d.", michael@0: scanCode, michael@0: axisInfo.mode, axisInfo.axis, axisInfo.highAxis, michael@0: axisInfo.splitValue, axisInfo.flatOverride); michael@0: #endif michael@0: mMap->mAxes.add(scanCode, axisInfo); michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: };