blob: 8626a03bf87285100440727a2269772f90d37a6e [file] [log] [blame]
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017#define LOG_TAG "KeyLayoutMap"
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019#include <stdlib.h>
Jeff Brown6b53e8d2010-11-10 16:03:06 -080020#include <android/keycodes.h>
21#include <ui/Keyboard.h>
22#include <ui/KeyLayoutMap.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023#include <utils/Log.h>
Jeff Brown6b53e8d2010-11-10 16:03:06 -080024#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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
38namespace android {
39
Jeff Brown6b53e8d2010-11-10 16:03:06 -080040static const char* WHITESPACE = " \t\r";
41
42// --- KeyLayoutMap ---
43
44KeyLayoutMap::KeyLayoutMap() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045}
46
Jeff Brown6b53e8d2010-11-10 16:03:06 -080047KeyLayoutMap::~KeyLayoutMap() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048}
49
Jeff Brown6b53e8d2010-11-10 16:03:06 -080050status_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 LOGE("Error %d opening key layout map file %s.", status, filename.string());
57 } else {
58 KeyLayoutMap* map = new KeyLayoutMap();
59 if (!map) {
60 LOGE("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 LOGD("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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -080080 delete tokenizer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -080082 return status;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083}
84
Jeff Brown6f2fba42011-02-19 01:08:02 -080085status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
Jeff Brown6b53e8d2010-11-10 16:03:06 -080086 ssize_t index = mKeys.indexOfKey(scanCode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 if (index < 0) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -080088#if DEBUG_MAPPING
Jeff Brown6f2fba42011-02-19 01:08:02 -080089 LOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
Jeff Brown6b53e8d2010-11-10 16:03:06 -080090#endif
91 *keyCode = AKEYCODE_UNKNOWN;
92 *flags = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 return NAME_NOT_FOUND;
94 }
95
Jeff Brown6b53e8d2010-11-10 16:03:06 -080096 const Key& k = mKeys.valueAt(index);
97 *keyCode = k.keyCode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 *flags = k.flags;
99
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800100#if DEBUG_MAPPING
Jeff Brown6f2fba42011-02-19 01:08:02 -0800101 LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800102#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 return NO_ERROR;
104}
105
Jeff Brown6f2fba42011-02-19 01:08:02 -0800106status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800107 const size_t N = mKeys.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 for (size_t i=0; i<N; i++) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800109 if (mKeys.valueAt(i).keyCode == keyCode) {
110 outScanCodes->add(mKeys.keyAt(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 }
112 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800113 return NO_ERROR;
114}
115
Jeff Brown3a22fa02011-03-04 13:07:49 -0800116status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
Jeff Brown6f2fba42011-02-19 01:08:02 -0800117 ssize_t index = mAxes.indexOfKey(scanCode);
118 if (index < 0) {
119#if DEBUG_MAPPING
120 LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
121#endif
Jeff Brown6f2fba42011-02-19 01:08:02 -0800122 return NAME_NOT_FOUND;
123 }
124
Jeff Brown3a22fa02011-03-04 13:07:49 -0800125 *outAxisInfo = mAxes.valueAt(index);
Jeff Brown6f2fba42011-02-19 01:08:02 -0800126
127#if DEBUG_MAPPING
Jeff Brown3a22fa02011-03-04 13:07:49 -0800128 LOGD("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);
Jeff Brown6f2fba42011-02-19 01:08:02 -0800133#endif
134 return NO_ERROR;
135}
136
137
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800138// --- 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 LOGD("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;
Jeff Brown6f2fba42011-02-19 01:08:02 -0800162 } else if (keywordToken == "axis") {
163 mTokenizer->skipDelimiters(WHITESPACE);
164 status_t status = parseAxis();
165 if (status) return status;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800166 } else {
167 LOGE("%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 LOGE("%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) {
Jeff Brown6f2fba42011-02-19 01:08:02 -0800191 LOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800192 scanCodeToken.string());
193 return BAD_VALUE;
194 }
195 if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
Jeff Brown6f2fba42011-02-19 01:08:02 -0800196 LOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800197 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 LOGE("%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) {
Jeff Brown6f2fba42011-02-19 01:08:02 -0800218 LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800219 flagToken.string());
220 return BAD_VALUE;
221 }
222 if (flags & flag) {
Jeff Brown6f2fba42011-02-19 01:08:02 -0800223 LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800224 flagToken.string());
225 return BAD_VALUE;
226 }
227 flags |= flag;
228 }
229
230#if DEBUG_PARSER
231 LOGD("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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 return NO_ERROR;
238}
239
Jeff Brown6f2fba42011-02-19 01:08:02 -0800240status_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 LOGE("%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 LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
251 scanCodeToken.string());
252 return BAD_VALUE;
253 }
254
Jeff Brown3a22fa02011-03-04 13:07:49 -0800255 AxisInfo axisInfo;
256
Jeff Brown6f2fba42011-02-19 01:08:02 -0800257 mTokenizer->skipDelimiters(WHITESPACE);
Jeff Brown3a22fa02011-03-04 13:07:49 -0800258 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 LOGE("%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 LOGE("%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 LOGE("%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 LOGE("%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 LOGE("%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 LOGE("%s: Expected flat value, got '%s'.",
320 mTokenizer->getLocation().string(), flatToken.string());
321 return BAD_VALUE;
322 }
323 } else {
324 LOGE("%s: Expected keyword 'flat', got '%s'.",
325 mTokenizer->getLocation().string(), keywordToken.string());
326 return BAD_VALUE;
327 }
Jeff Brown6f2fba42011-02-19 01:08:02 -0800328 }
329
330#if DEBUG_PARSER
Jeff Brown3a22fa02011-03-04 13:07:49 -0800331 LOGD("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);
Jeff Brown6f2fba42011-02-19 01:08:02 -0800336#endif
Jeff Brown3a22fa02011-03-04 13:07:49 -0800337 mMap->mAxes.add(scanCode, axisInfo);
Jeff Brown6f2fba42011-02-19 01:08:02 -0800338 return NO_ERROR;
339}
340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341};