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