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
153
154// --- KeyLayoutMap::Parser ---
155
156KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
157        mMap(map), mTokenizer(tokenizer) {
158}
159
160KeyLayoutMap::Parser::~Parser() {
161}
162
163status_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
202status_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
266status_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