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