|
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 #include "cutils_log.h" |
|
19 |
|
20 #include <stdlib.h> |
|
21 #include "android_keycodes.h" |
|
22 #include "Keyboard.h" |
|
23 #include "KeyLayoutMap.h" |
|
24 #include <utils/Errors.h> |
|
25 #include "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 |
|
38 namespace android { |
|
39 |
|
40 static const char* WHITESPACE = " \t\r"; |
|
41 |
|
42 // --- KeyLayoutMap --- |
|
43 |
|
44 KeyLayoutMap::KeyLayoutMap() { |
|
45 } |
|
46 |
|
47 KeyLayoutMap::~KeyLayoutMap() { |
|
48 } |
|
49 |
|
50 status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { |
|
51 outMap->clear(); |
|
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 sp<KeyLayoutMap> map = new KeyLayoutMap(); |
|
59 if (!map.get()) { |
|
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.get(), 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 *outMap = map; |
|
76 } |
|
77 } |
|
78 delete tokenizer; |
|
79 } |
|
80 return status; |
|
81 } |
|
82 |
|
83 status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, |
|
84 int32_t* outKeyCode, uint32_t* outFlags) const { |
|
85 const Key* key = getKey(scanCode, usageCode); |
|
86 if (!key) { |
|
87 #if DEBUG_MAPPING |
|
88 ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); |
|
89 #endif |
|
90 *outKeyCode = AKEYCODE_UNKNOWN; |
|
91 *outFlags = 0; |
|
92 return NAME_NOT_FOUND; |
|
93 } |
|
94 |
|
95 *outKeyCode = key->keyCode; |
|
96 *outFlags = key->flags; |
|
97 |
|
98 #if DEBUG_MAPPING |
|
99 ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", |
|
100 scanCode, usageCode, *outKeyCode, *outFlags); |
|
101 #endif |
|
102 return NO_ERROR; |
|
103 } |
|
104 |
|
105 const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { |
|
106 if (usageCode) { |
|
107 ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); |
|
108 if (index >= 0) { |
|
109 return &mKeysByUsageCode.valueAt(index); |
|
110 } |
|
111 } |
|
112 if (scanCode) { |
|
113 ssize_t index = mKeysByScanCode.indexOfKey(scanCode); |
|
114 if (index >= 0) { |
|
115 return &mKeysByScanCode.valueAt(index); |
|
116 } |
|
117 } |
|
118 return NULL; |
|
119 } |
|
120 |
|
121 status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { |
|
122 const size_t N = mKeysByScanCode.size(); |
|
123 for (size_t i=0; i<N; i++) { |
|
124 if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { |
|
125 outScanCodes->add(mKeysByScanCode.keyAt(i)); |
|
126 } |
|
127 } |
|
128 return NO_ERROR; |
|
129 } |
|
130 |
|
131 status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { |
|
132 ssize_t index = mAxes.indexOfKey(scanCode); |
|
133 if (index < 0) { |
|
134 #if DEBUG_MAPPING |
|
135 ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); |
|
136 #endif |
|
137 return NAME_NOT_FOUND; |
|
138 } |
|
139 |
|
140 *outAxisInfo = mAxes.valueAt(index); |
|
141 |
|
142 #if DEBUG_MAPPING |
|
143 ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " |
|
144 "splitValue=%d, flatOverride=%d.", |
|
145 scanCode, |
|
146 outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, |
|
147 outAxisInfo->splitValue, outAxisInfo->flatOverride); |
|
148 #endif |
|
149 return NO_ERROR; |
|
150 } |
|
151 |
|
152 |
|
153 // --- KeyLayoutMap::Parser --- |
|
154 |
|
155 KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : |
|
156 mMap(map), mTokenizer(tokenizer) { |
|
157 } |
|
158 |
|
159 KeyLayoutMap::Parser::~Parser() { |
|
160 } |
|
161 |
|
162 status_t KeyLayoutMap::Parser::parse() { |
|
163 while (!mTokenizer->isEof()) { |
|
164 #if DEBUG_PARSER |
|
165 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), |
|
166 mTokenizer->peekRemainderOfLine().string()); |
|
167 #endif |
|
168 |
|
169 mTokenizer->skipDelimiters(WHITESPACE); |
|
170 |
|
171 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { |
|
172 String8 keywordToken = mTokenizer->nextToken(WHITESPACE); |
|
173 if (keywordToken == "key") { |
|
174 mTokenizer->skipDelimiters(WHITESPACE); |
|
175 status_t status = parseKey(); |
|
176 if (status) return status; |
|
177 } else if (keywordToken == "axis") { |
|
178 mTokenizer->skipDelimiters(WHITESPACE); |
|
179 status_t status = parseAxis(); |
|
180 if (status) return status; |
|
181 } else { |
|
182 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), |
|
183 keywordToken.string()); |
|
184 return BAD_VALUE; |
|
185 } |
|
186 |
|
187 mTokenizer->skipDelimiters(WHITESPACE); |
|
188 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { |
|
189 ALOGE("%s: Expected end of line or trailing comment, got '%s'.", |
|
190 mTokenizer->getLocation().string(), |
|
191 mTokenizer->peekRemainderOfLine().string()); |
|
192 return BAD_VALUE; |
|
193 } |
|
194 } |
|
195 |
|
196 mTokenizer->nextLine(); |
|
197 } |
|
198 return NO_ERROR; |
|
199 } |
|
200 |
|
201 status_t KeyLayoutMap::Parser::parseKey() { |
|
202 String8 codeToken = mTokenizer->nextToken(WHITESPACE); |
|
203 bool mapUsage = false; |
|
204 if (codeToken == "usage") { |
|
205 mapUsage = true; |
|
206 mTokenizer->skipDelimiters(WHITESPACE); |
|
207 codeToken = mTokenizer->nextToken(WHITESPACE); |
|
208 } |
|
209 |
|
210 char* end; |
|
211 int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); |
|
212 if (*end) { |
|
213 ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), |
|
214 mapUsage ? "usage" : "scan code", codeToken.string()); |
|
215 return BAD_VALUE; |
|
216 } |
|
217 KeyedVector<int32_t, Key>& map = |
|
218 mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; |
|
219 if (map.indexOfKey(code) >= 0) { |
|
220 ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), |
|
221 mapUsage ? "usage" : "scan code", codeToken.string()); |
|
222 return BAD_VALUE; |
|
223 } |
|
224 |
|
225 mTokenizer->skipDelimiters(WHITESPACE); |
|
226 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); |
|
227 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); |
|
228 if (!keyCode) { |
|
229 ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), |
|
230 keyCodeToken.string()); |
|
231 return BAD_VALUE; |
|
232 } |
|
233 |
|
234 uint32_t flags = 0; |
|
235 for (;;) { |
|
236 mTokenizer->skipDelimiters(WHITESPACE); |
|
237 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; |
|
238 |
|
239 String8 flagToken = mTokenizer->nextToken(WHITESPACE); |
|
240 uint32_t flag = getKeyFlagByLabel(flagToken.string()); |
|
241 if (!flag) { |
|
242 ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), |
|
243 flagToken.string()); |
|
244 return BAD_VALUE; |
|
245 } |
|
246 if (flags & flag) { |
|
247 ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), |
|
248 flagToken.string()); |
|
249 return BAD_VALUE; |
|
250 } |
|
251 flags |= flag; |
|
252 } |
|
253 |
|
254 #if DEBUG_PARSER |
|
255 ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", |
|
256 mapUsage ? "usage" : "scan code", code, keyCode, flags); |
|
257 #endif |
|
258 Key key; |
|
259 key.keyCode = keyCode; |
|
260 key.flags = flags; |
|
261 map.add(code, key); |
|
262 return NO_ERROR; |
|
263 } |
|
264 |
|
265 status_t KeyLayoutMap::Parser::parseAxis() { |
|
266 String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); |
|
267 char* end; |
|
268 int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); |
|
269 if (*end) { |
|
270 ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), |
|
271 scanCodeToken.string()); |
|
272 return BAD_VALUE; |
|
273 } |
|
274 if (mMap->mAxes.indexOfKey(scanCode) >= 0) { |
|
275 ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), |
|
276 scanCodeToken.string()); |
|
277 return BAD_VALUE; |
|
278 } |
|
279 |
|
280 AxisInfo axisInfo; |
|
281 |
|
282 mTokenizer->skipDelimiters(WHITESPACE); |
|
283 String8 token = mTokenizer->nextToken(WHITESPACE); |
|
284 if (token == "invert") { |
|
285 axisInfo.mode = AxisInfo::MODE_INVERT; |
|
286 |
|
287 mTokenizer->skipDelimiters(WHITESPACE); |
|
288 String8 axisToken = mTokenizer->nextToken(WHITESPACE); |
|
289 axisInfo.axis = getAxisByLabel(axisToken.string()); |
|
290 if (axisInfo.axis < 0) { |
|
291 ALOGE("%s: Expected inverted axis label, got '%s'.", |
|
292 mTokenizer->getLocation().string(), axisToken.string()); |
|
293 return BAD_VALUE; |
|
294 } |
|
295 } else if (token == "split") { |
|
296 axisInfo.mode = AxisInfo::MODE_SPLIT; |
|
297 |
|
298 mTokenizer->skipDelimiters(WHITESPACE); |
|
299 String8 splitToken = mTokenizer->nextToken(WHITESPACE); |
|
300 axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); |
|
301 if (*end) { |
|
302 ALOGE("%s: Expected split value, got '%s'.", |
|
303 mTokenizer->getLocation().string(), splitToken.string()); |
|
304 return BAD_VALUE; |
|
305 } |
|
306 |
|
307 mTokenizer->skipDelimiters(WHITESPACE); |
|
308 String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); |
|
309 axisInfo.axis = getAxisByLabel(lowAxisToken.string()); |
|
310 if (axisInfo.axis < 0) { |
|
311 ALOGE("%s: Expected low axis label, got '%s'.", |
|
312 mTokenizer->getLocation().string(), lowAxisToken.string()); |
|
313 return BAD_VALUE; |
|
314 } |
|
315 |
|
316 mTokenizer->skipDelimiters(WHITESPACE); |
|
317 String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); |
|
318 axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); |
|
319 if (axisInfo.highAxis < 0) { |
|
320 ALOGE("%s: Expected high axis label, got '%s'.", |
|
321 mTokenizer->getLocation().string(), highAxisToken.string()); |
|
322 return BAD_VALUE; |
|
323 } |
|
324 } else { |
|
325 axisInfo.axis = getAxisByLabel(token.string()); |
|
326 if (axisInfo.axis < 0) { |
|
327 ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", |
|
328 mTokenizer->getLocation().string(), token.string()); |
|
329 return BAD_VALUE; |
|
330 } |
|
331 } |
|
332 |
|
333 for (;;) { |
|
334 mTokenizer->skipDelimiters(WHITESPACE); |
|
335 if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { |
|
336 break; |
|
337 } |
|
338 String8 keywordToken = mTokenizer->nextToken(WHITESPACE); |
|
339 if (keywordToken == "flat") { |
|
340 mTokenizer->skipDelimiters(WHITESPACE); |
|
341 String8 flatToken = mTokenizer->nextToken(WHITESPACE); |
|
342 axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); |
|
343 if (*end) { |
|
344 ALOGE("%s: Expected flat value, got '%s'.", |
|
345 mTokenizer->getLocation().string(), flatToken.string()); |
|
346 return BAD_VALUE; |
|
347 } |
|
348 } else { |
|
349 ALOGE("%s: Expected keyword 'flat', got '%s'.", |
|
350 mTokenizer->getLocation().string(), keywordToken.string()); |
|
351 return BAD_VALUE; |
|
352 } |
|
353 } |
|
354 |
|
355 #if DEBUG_PARSER |
|
356 ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " |
|
357 "splitValue=%d, flatOverride=%d.", |
|
358 scanCode, |
|
359 axisInfo.mode, axisInfo.axis, axisInfo.highAxis, |
|
360 axisInfo.splitValue, axisInfo.flatOverride); |
|
361 #endif |
|
362 mMap->mAxes.add(scanCode, axisInfo); |
|
363 return NO_ERROR; |
|
364 } |
|
365 |
|
366 }; |