1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "KeyLayoutMap" 18 19#include <stdlib.h> 20#include <android/keycodes.h> 21#include <androidfw/Keyboard.h> 22#include <androidfw/KeyLayoutMap.h> 23#include <utils/Log.h> 24#include <utils/Errors.h> 25#include <utils/Tokenizer.h> 26#include <utils/Timers.h> 27 28// Enables debug output for the parser. 29#define DEBUG_PARSER 0 30 31// Enables debug output for parser performance. 32#define DEBUG_PARSER_PERFORMANCE 0 33 34// Enables debug output for mapping. 35#define DEBUG_MAPPING 0 36 37 38namespace android { 39 40static const char* WHITESPACE = " \t\r"; 41 42// --- KeyLayoutMap --- 43 44KeyLayoutMap::KeyLayoutMap() { 45} 46 47KeyLayoutMap::~KeyLayoutMap() { 48} 49 50status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { 51 outMap->clear(); 52 53 Tokenizer* tokenizer; 54 status_t status = Tokenizer::open(filename, &tokenizer); 55 if (status) { 56 ALOGE("Error %d opening key layout map file %s.", status, filename.string()); 57 } else { 58 sp<KeyLayoutMap> map = new KeyLayoutMap(); 59 if (!map.get()) { 60 ALOGE("Error allocating key layout map."); 61 status = NO_MEMORY; 62 } else { 63#if DEBUG_PARSER_PERFORMANCE 64 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); 65#endif 66 Parser parser(map.get(), tokenizer); 67 status = parser.parse(); 68#if DEBUG_PARSER_PERFORMANCE 69 nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; 70 ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", 71 tokenizer->getFilename().string(), tokenizer->getLineNumber(), 72 elapsedTime / 1000000.0); 73#endif 74 if (!status) { 75 *outMap = map; 76 } 77 } 78 delete tokenizer; 79 } 80 return status; 81} 82 83status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, 84 int32_t* outKeyCode, uint32_t* outFlags) const { 85 const Key* key = getKey(scanCode, usageCode); 86 if (!key) { 87#if DEBUG_MAPPING 88 ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); 89#endif 90 *outKeyCode = AKEYCODE_UNKNOWN; 91 *outFlags = 0; 92 return NAME_NOT_FOUND; 93 } 94 95 *outKeyCode = key->keyCode; 96 *outFlags = key->flags; 97 98#if DEBUG_MAPPING 99 ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", 100 scanCode, usageCode, *outKeyCode, *outFlags); 101#endif 102 return NO_ERROR; 103} 104 105const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { 106 if (usageCode) { 107 ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); 108 if (index >= 0) { 109 return &mKeysByUsageCode.valueAt(index); 110 } 111 } 112 if (scanCode) { 113 ssize_t index = mKeysByScanCode.indexOfKey(scanCode); 114 if (index >= 0) { 115 return &mKeysByScanCode.valueAt(index); 116 } 117 } 118 return NULL; 119} 120 121status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { 122 const size_t N = mKeysByScanCode.size(); 123 for (size_t i=0; i<N; i++) { 124 if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { 125 outScanCodes->add(mKeysByScanCode.keyAt(i)); 126 } 127 } 128 return NO_ERROR; 129} 130 131status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { 132 ssize_t index = mAxes.indexOfKey(scanCode); 133 if (index < 0) { 134#if DEBUG_MAPPING 135 ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); 136#endif 137 return NAME_NOT_FOUND; 138 } 139 140 *outAxisInfo = mAxes.valueAt(index); 141 142#if DEBUG_MAPPING 143 ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " 144 "splitValue=%d, flatOverride=%d.", 145 scanCode, 146 outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, 147 outAxisInfo->splitValue, outAxisInfo->flatOverride); 148#endif 149 return NO_ERROR; 150} 151 152 153// --- KeyLayoutMap::Parser --- 154 155KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : 156 mMap(map), mTokenizer(tokenizer) { 157} 158 159KeyLayoutMap::Parser::~Parser() { 160} 161 162status_t KeyLayoutMap::Parser::parse() { 163 while (!mTokenizer->isEof()) { 164#if DEBUG_PARSER 165 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), 166 mTokenizer->peekRemainderOfLine().string()); 167#endif 168 169 mTokenizer->skipDelimiters(WHITESPACE); 170 171 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { 172 String8 keywordToken = mTokenizer->nextToken(WHITESPACE); 173 if (keywordToken == "key") { 174 mTokenizer->skipDelimiters(WHITESPACE); 175 status_t status = parseKey(); 176 if (status) return status; 177 } else if (keywordToken == "axis") { 178 mTokenizer->skipDelimiters(WHITESPACE); 179 status_t status = parseAxis(); 180 if (status) return status; 181 } else { 182 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), 183 keywordToken.string()); 184 return BAD_VALUE; 185 } 186 187 mTokenizer->skipDelimiters(WHITESPACE); 188 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { 189 ALOGE("%s: Expected end of line or trailing comment, got '%s'.", 190 mTokenizer->getLocation().string(), 191 mTokenizer->peekRemainderOfLine().string()); 192 return BAD_VALUE; 193 } 194 } 195 196 mTokenizer->nextLine(); 197 } 198 return NO_ERROR; 199} 200 201status_t KeyLayoutMap::Parser::parseKey() { 202 String8 codeToken = mTokenizer->nextToken(WHITESPACE); 203 bool mapUsage = false; 204 if (codeToken == "usage") { 205 mapUsage = true; 206 mTokenizer->skipDelimiters(WHITESPACE); 207 codeToken = mTokenizer->nextToken(WHITESPACE); 208 } 209 210 char* end; 211 int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); 212 if (*end) { 213 ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), 214 mapUsage ? "usage" : "scan code", codeToken.string()); 215 return BAD_VALUE; 216 } 217 KeyedVector<int32_t, Key>& map = 218 mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; 219 if (map.indexOfKey(code) >= 0) { 220 ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), 221 mapUsage ? "usage" : "scan code", codeToken.string()); 222 return BAD_VALUE; 223 } 224 225 mTokenizer->skipDelimiters(WHITESPACE); 226 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); 227 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); 228 if (!keyCode) { 229 ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), 230 keyCodeToken.string()); 231 return BAD_VALUE; 232 } 233 234 uint32_t flags = 0; 235 for (;;) { 236 mTokenizer->skipDelimiters(WHITESPACE); 237 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; 238 239 String8 flagToken = mTokenizer->nextToken(WHITESPACE); 240 uint32_t flag = getKeyFlagByLabel(flagToken.string()); 241 if (!flag) { 242 ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), 243 flagToken.string()); 244 return BAD_VALUE; 245 } 246 if (flags & flag) { 247 ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), 248 flagToken.string()); 249 return BAD_VALUE; 250 } 251 flags |= flag; 252 } 253 254#if DEBUG_PARSER 255 ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", 256 mapUsage ? "usage" : "scan code", code, keyCode, flags); 257#endif 258 Key key; 259 key.keyCode = keyCode; 260 key.flags = flags; 261 map.add(code, key); 262 return NO_ERROR; 263} 264 265status_t KeyLayoutMap::Parser::parseAxis() { 266 String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); 267 char* end; 268 int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); 269 if (*end) { 270 ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), 271 scanCodeToken.string()); 272 return BAD_VALUE; 273 } 274 if (mMap->mAxes.indexOfKey(scanCode) >= 0) { 275 ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), 276 scanCodeToken.string()); 277 return BAD_VALUE; 278 } 279 280 AxisInfo axisInfo; 281 282 mTokenizer->skipDelimiters(WHITESPACE); 283 String8 token = mTokenizer->nextToken(WHITESPACE); 284 if (token == "invert") { 285 axisInfo.mode = AxisInfo::MODE_INVERT; 286 287 mTokenizer->skipDelimiters(WHITESPACE); 288 String8 axisToken = mTokenizer->nextToken(WHITESPACE); 289 axisInfo.axis = getAxisByLabel(axisToken.string()); 290 if (axisInfo.axis < 0) { 291 ALOGE("%s: Expected inverted axis label, got '%s'.", 292 mTokenizer->getLocation().string(), axisToken.string()); 293 return BAD_VALUE; 294 } 295 } else if (token == "split") { 296 axisInfo.mode = AxisInfo::MODE_SPLIT; 297 298 mTokenizer->skipDelimiters(WHITESPACE); 299 String8 splitToken = mTokenizer->nextToken(WHITESPACE); 300 axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); 301 if (*end) { 302 ALOGE("%s: Expected split value, got '%s'.", 303 mTokenizer->getLocation().string(), splitToken.string()); 304 return BAD_VALUE; 305 } 306 307 mTokenizer->skipDelimiters(WHITESPACE); 308 String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); 309 axisInfo.axis = getAxisByLabel(lowAxisToken.string()); 310 if (axisInfo.axis < 0) { 311 ALOGE("%s: Expected low axis label, got '%s'.", 312 mTokenizer->getLocation().string(), lowAxisToken.string()); 313 return BAD_VALUE; 314 } 315 316 mTokenizer->skipDelimiters(WHITESPACE); 317 String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); 318 axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); 319 if (axisInfo.highAxis < 0) { 320 ALOGE("%s: Expected high axis label, got '%s'.", 321 mTokenizer->getLocation().string(), highAxisToken.string()); 322 return BAD_VALUE; 323 } 324 } else { 325 axisInfo.axis = getAxisByLabel(token.string()); 326 if (axisInfo.axis < 0) { 327 ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", 328 mTokenizer->getLocation().string(), token.string()); 329 return BAD_VALUE; 330 } 331 } 332 333 for (;;) { 334 mTokenizer->skipDelimiters(WHITESPACE); 335 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { 336 break; 337 } 338 String8 keywordToken = mTokenizer->nextToken(WHITESPACE); 339 if (keywordToken == "flat") { 340 mTokenizer->skipDelimiters(WHITESPACE); 341 String8 flatToken = mTokenizer->nextToken(WHITESPACE); 342 axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); 343 if (*end) { 344 ALOGE("%s: Expected flat value, got '%s'.", 345 mTokenizer->getLocation().string(), flatToken.string()); 346 return BAD_VALUE; 347 } 348 } else { 349 ALOGE("%s: Expected keyword 'flat', got '%s'.", 350 mTokenizer->getLocation().string(), keywordToken.string()); 351 return BAD_VALUE; 352 } 353 } 354 355#if DEBUG_PARSER 356 ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " 357 "splitValue=%d, flatOverride=%d.", 358 scanCode, 359 axisInfo.mode, axisInfo.axis, axisInfo.highAxis, 360 axisInfo.splitValue, axisInfo.flatOverride); 361#endif 362 mMap->mAxes.add(scanCode, axisInfo); 363 return NO_ERROR; 364} 365 366}; 367