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