blob: 0800a31d75299884749491662cbd34bf7f7960ac [file] [log] [blame]
Jeff Brown5912f952013-07-01 19:10:31 -07001/*
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
39namespace android {
40
41static const char* WHITESPACE = " \t\r";
42
43// --- KeyLayoutMap ---
44
45KeyLayoutMap::KeyLayoutMap() {
46}
47
48KeyLayoutMap::~KeyLayoutMap() {
49}
50
51status_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
84status_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
106const 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
122status_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
132status_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
Michael Wright74bdd2e2013-10-17 17:35:53 -0700153status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const {
154 const size_t N = mLedsByScanCode.size();
155 for (size_t i = 0; i < N; i++) {
156 if (mLedsByScanCode.valueAt(i).ledCode == ledCode) {
157 *outScanCode = mLedsByScanCode.keyAt(i);
158#if DEBUG_MAPPING
159 ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode);
160#endif
161 return NO_ERROR;
162 }
163 }
164#if DEBUG_MAPPING
165 ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
166#endif
167 return NAME_NOT_FOUND;
168}
169
170status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const {
171 const size_t N = mLedsByUsageCode.size();
172 for (size_t i = 0; i < N; i++) {
173 if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) {
174 *outUsageCode = mLedsByUsageCode.keyAt(i);
175#if DEBUG_MAPPING
176 ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode);
177#endif
178 return NO_ERROR;
179 }
180 }
181#if DEBUG_MAPPING
182 ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode);
183#endif
184 return NAME_NOT_FOUND;
185}
186
Jeff Brown5912f952013-07-01 19:10:31 -0700187
188// --- KeyLayoutMap::Parser ---
189
190KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
191 mMap(map), mTokenizer(tokenizer) {
192}
193
194KeyLayoutMap::Parser::~Parser() {
195}
196
197status_t KeyLayoutMap::Parser::parse() {
198 while (!mTokenizer->isEof()) {
199#if DEBUG_PARSER
200 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
201 mTokenizer->peekRemainderOfLine().string());
202#endif
203
204 mTokenizer->skipDelimiters(WHITESPACE);
205
206 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
207 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
208 if (keywordToken == "key") {
209 mTokenizer->skipDelimiters(WHITESPACE);
210 status_t status = parseKey();
211 if (status) return status;
212 } else if (keywordToken == "axis") {
213 mTokenizer->skipDelimiters(WHITESPACE);
214 status_t status = parseAxis();
215 if (status) return status;
Michael Wright74bdd2e2013-10-17 17:35:53 -0700216 } else if (keywordToken == "led") {
217 mTokenizer->skipDelimiters(WHITESPACE);
218 status_t status = parseLed();
219 if (status) return status;
Jeff Brown5912f952013-07-01 19:10:31 -0700220 } else {
221 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
222 keywordToken.string());
223 return BAD_VALUE;
224 }
225
226 mTokenizer->skipDelimiters(WHITESPACE);
227 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
228 ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
229 mTokenizer->getLocation().string(),
230 mTokenizer->peekRemainderOfLine().string());
231 return BAD_VALUE;
232 }
233 }
234
235 mTokenizer->nextLine();
236 }
237 return NO_ERROR;
238}
239
240status_t KeyLayoutMap::Parser::parseKey() {
241 String8 codeToken = mTokenizer->nextToken(WHITESPACE);
242 bool mapUsage = false;
243 if (codeToken == "usage") {
244 mapUsage = true;
245 mTokenizer->skipDelimiters(WHITESPACE);
246 codeToken = mTokenizer->nextToken(WHITESPACE);
247 }
248
249 char* end;
250 int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
251 if (*end) {
252 ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
253 mapUsage ? "usage" : "scan code", codeToken.string());
254 return BAD_VALUE;
255 }
Michael Wright74bdd2e2013-10-17 17:35:53 -0700256 KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
Jeff Brown5912f952013-07-01 19:10:31 -0700257 if (map.indexOfKey(code) >= 0) {
258 ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
259 mapUsage ? "usage" : "scan code", codeToken.string());
260 return BAD_VALUE;
261 }
262
263 mTokenizer->skipDelimiters(WHITESPACE);
264 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
265 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
266 if (!keyCode) {
267 ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
268 keyCodeToken.string());
269 return BAD_VALUE;
270 }
271
272 uint32_t flags = 0;
273 for (;;) {
274 mTokenizer->skipDelimiters(WHITESPACE);
275 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
276
277 String8 flagToken = mTokenizer->nextToken(WHITESPACE);
278 uint32_t flag = getKeyFlagByLabel(flagToken.string());
279 if (!flag) {
280 ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
281 flagToken.string());
282 return BAD_VALUE;
283 }
284 if (flags & flag) {
285 ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
286 flagToken.string());
287 return BAD_VALUE;
288 }
289 flags |= flag;
290 }
291
292#if DEBUG_PARSER
293 ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
294 mapUsage ? "usage" : "scan code", code, keyCode, flags);
295#endif
296 Key key;
297 key.keyCode = keyCode;
298 key.flags = flags;
299 map.add(code, key);
300 return NO_ERROR;
301}
302
303status_t KeyLayoutMap::Parser::parseAxis() {
304 String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
305 char* end;
306 int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
307 if (*end) {
308 ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
309 scanCodeToken.string());
310 return BAD_VALUE;
311 }
312 if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
313 ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
314 scanCodeToken.string());
315 return BAD_VALUE;
316 }
317
318 AxisInfo axisInfo;
319
320 mTokenizer->skipDelimiters(WHITESPACE);
321 String8 token = mTokenizer->nextToken(WHITESPACE);
322 if (token == "invert") {
323 axisInfo.mode = AxisInfo::MODE_INVERT;
324
325 mTokenizer->skipDelimiters(WHITESPACE);
326 String8 axisToken = mTokenizer->nextToken(WHITESPACE);
327 axisInfo.axis = getAxisByLabel(axisToken.string());
328 if (axisInfo.axis < 0) {
329 ALOGE("%s: Expected inverted axis label, got '%s'.",
330 mTokenizer->getLocation().string(), axisToken.string());
331 return BAD_VALUE;
332 }
333 } else if (token == "split") {
334 axisInfo.mode = AxisInfo::MODE_SPLIT;
335
336 mTokenizer->skipDelimiters(WHITESPACE);
337 String8 splitToken = mTokenizer->nextToken(WHITESPACE);
338 axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
339 if (*end) {
340 ALOGE("%s: Expected split value, got '%s'.",
341 mTokenizer->getLocation().string(), splitToken.string());
342 return BAD_VALUE;
343 }
344
345 mTokenizer->skipDelimiters(WHITESPACE);
346 String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
347 axisInfo.axis = getAxisByLabel(lowAxisToken.string());
348 if (axisInfo.axis < 0) {
349 ALOGE("%s: Expected low axis label, got '%s'.",
350 mTokenizer->getLocation().string(), lowAxisToken.string());
351 return BAD_VALUE;
352 }
353
354 mTokenizer->skipDelimiters(WHITESPACE);
355 String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
356 axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
357 if (axisInfo.highAxis < 0) {
358 ALOGE("%s: Expected high axis label, got '%s'.",
359 mTokenizer->getLocation().string(), highAxisToken.string());
360 return BAD_VALUE;
361 }
362 } else {
363 axisInfo.axis = getAxisByLabel(token.string());
364 if (axisInfo.axis < 0) {
365 ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
366 mTokenizer->getLocation().string(), token.string());
367 return BAD_VALUE;
368 }
369 }
370
371 for (;;) {
372 mTokenizer->skipDelimiters(WHITESPACE);
373 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
374 break;
375 }
376 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
377 if (keywordToken == "flat") {
378 mTokenizer->skipDelimiters(WHITESPACE);
379 String8 flatToken = mTokenizer->nextToken(WHITESPACE);
380 axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
381 if (*end) {
382 ALOGE("%s: Expected flat value, got '%s'.",
383 mTokenizer->getLocation().string(), flatToken.string());
384 return BAD_VALUE;
385 }
386 } else {
387 ALOGE("%s: Expected keyword 'flat', got '%s'.",
388 mTokenizer->getLocation().string(), keywordToken.string());
389 return BAD_VALUE;
390 }
391 }
392
393#if DEBUG_PARSER
394 ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
395 "splitValue=%d, flatOverride=%d.",
396 scanCode,
397 axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
398 axisInfo.splitValue, axisInfo.flatOverride);
399#endif
400 mMap->mAxes.add(scanCode, axisInfo);
401 return NO_ERROR;
402}
403
Michael Wright74bdd2e2013-10-17 17:35:53 -0700404status_t KeyLayoutMap::Parser::parseLed() {
405 String8 codeToken = mTokenizer->nextToken(WHITESPACE);
406 bool mapUsage = false;
407 if (codeToken == "usage") {
408 mapUsage = true;
409 mTokenizer->skipDelimiters(WHITESPACE);
410 codeToken = mTokenizer->nextToken(WHITESPACE);
411 }
412 char* end;
413 int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
414 if (*end) {
415 ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
416 mapUsage ? "usage" : "scan code", codeToken.string());
417 return BAD_VALUE;
418 }
419
420 KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
421 if (map.indexOfKey(code) >= 0) {
422 ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
423 mapUsage ? "usage" : "scan code", codeToken.string());
424 return BAD_VALUE;
425 }
426
427 mTokenizer->skipDelimiters(WHITESPACE);
428 String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
429 int32_t ledCode = getLedByLabel(ledCodeToken.string());
430 if (ledCode < 0) {
431 ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
432 ledCodeToken.string());
433 return BAD_VALUE;
434 }
435
436#if DEBUG_PARSER
437 ALOGD("Parsed led %s: code=%d, ledCode=%d.",
438 mapUsage ? "usage" : "scan code", code, ledCode);
439#endif
440
441 Led led;
442 led.ledCode = ledCode;
443 map.add(code, led);
444 return NO_ERROR;
445}
Jeff Brown5912f952013-07-01 19:10:31 -0700446};