KeyLayoutMap.cpp revision 74bdd2e7ceabd3c9e74ccf7c2e6bd3dae27ca497
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
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
153status_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
187
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;
216            } else if (keywordToken == "led") {
217                mTokenizer->skipDelimiters(WHITESPACE);
218                status_t status = parseLed();
219                if (status) return status;
220            } 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    }
256    KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
257    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
404status_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}
446};
447