blob: e689c4b0d99409df3b4ec143107599da6b4cb089 [file] [log] [blame]
Jeff Browna3477c82010-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 Projectedbf3b62009-03-03 19:31:44 -080017#define LOG_TAG "KeyCharacterMap"
18
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080019#include <stdlib.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080020#include <string.h>
Jeff Browna3477c82010-11-10 16:03:06 -080021#include <android/keycodes.h>
22#include <ui/Keyboard.h>
23#include <ui/KeyCharacterMap.h>
24#include <utils/Log.h>
25#include <utils/Errors.h>
26#include <utils/Tokenizer.h>
27#include <utils/Timers.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080028
Jeff Browna3477c82010-11-10 16:03:06 -080029// 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";
42static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
43
44struct Modifier {
45 const char* label;
46 int32_t metaState;
47};
48static const Modifier modifiers[] = {
49 { "shift", AMETA_SHIFT_ON },
50 { "lshift", AMETA_SHIFT_LEFT_ON },
51 { "rshift", AMETA_SHIFT_RIGHT_ON },
52 { "alt", AMETA_ALT_ON },
53 { "lalt", AMETA_ALT_LEFT_ON },
54 { "ralt", AMETA_ALT_RIGHT_ON },
55 { "ctrl", AMETA_CTRL_ON },
56 { "lctrl", AMETA_CTRL_LEFT_ON },
57 { "rctrl", AMETA_CTRL_RIGHT_ON },
58 { "meta", AMETA_META_ON },
59 { "lmeta", AMETA_META_LEFT_ON },
60 { "rmeta", AMETA_META_RIGHT_ON },
61 { "sym", AMETA_SYM_ON },
62 { "fn", AMETA_FUNCTION_ON },
63 { "capslock", AMETA_CAPS_LOCK_ON },
64 { "numlock", AMETA_NUM_LOCK_ON },
65 { "scrolllock", AMETA_SCROLL_LOCK_ON },
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080066};
67
Jeff Browna3477c82010-11-10 16:03:06 -080068#if DEBUG_MAPPING
69static String8 toString(const char16_t* chars, size_t numChars) {
70 String8 result;
71 for (size_t i = 0; i < numChars; i++) {
72 result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080073 }
Jeff Browna3477c82010-11-10 16:03:06 -080074 return result;
75}
76#endif
77
78
79// --- KeyCharacterMap ---
80
81KeyCharacterMap::KeyCharacterMap() :
82 mType(KEYBOARD_TYPE_UNKNOWN) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080083}
84
Jeff Browna3477c82010-11-10 16:03:06 -080085KeyCharacterMap::~KeyCharacterMap() {
86 for (size_t i = 0; i < mKeys.size(); i++) {
87 Key* key = mKeys.editValueAt(i);
88 delete key;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080089 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080090}
91
Jeff Browna3477c82010-11-10 16:03:06 -080092status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) {
93 *outMap = NULL;
94
95 Tokenizer* tokenizer;
96 status_t status = Tokenizer::open(filename, &tokenizer);
97 if (status) {
98 LOGE("Error %d opening key character map file %s.", status, filename.string());
99 } else {
100 KeyCharacterMap* map = new KeyCharacterMap();
101 if (!map) {
102 LOGE("Error allocating key character map.");
103 status = NO_MEMORY;
104 } else {
105#if DEBUG_PARSER_PERFORMANCE
106 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
107#endif
108 Parser parser(map, tokenizer);
109 status = parser.parse();
110#if DEBUG_PARSER_PERFORMANCE
111 nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
112 LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
113 tokenizer->getFilename().string(), tokenizer->getLineNumber(),
114 elapsedTime / 1000000.0);
115#endif
116 if (status) {
117 delete map;
118 } else {
119 *outMap = map;
120 }
121 }
122 delete tokenizer;
123 }
124 return status;
125}
126
127status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) {
128 *outMap = NULL;
129
130 String8 filename;
131 status_t result = getKeyCharacterMapFile(deviceId, filename);
132 if (!result) {
133 result = load(filename, outMap);
134 }
135 return result;
136}
137
138int32_t KeyCharacterMap::getKeyboardType() const {
139 return mType;
140}
141
142char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
143 char16_t result = 0;
144 ssize_t index = mKeys.indexOfKey(keyCode);
145 if (index >= 0) {
146 const Key* key = mKeys.valueAt(index);
147 result = key->label;
148 }
149#if DEBUG_MAPPING
150 LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
151#endif
152 return result;
153}
154
155char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
156 char16_t result = 0;
157 ssize_t index = mKeys.indexOfKey(keyCode);
158 if (index >= 0) {
159 const Key* key = mKeys.valueAt(index);
160 result = key->number;
161 }
162#if DEBUG_MAPPING
163 LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
164#endif
165 return result;
166}
167
168char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
169 char16_t result = 0;
170 ssize_t index = mKeys.indexOfKey(keyCode);
171 if (index >= 0) {
172 const Key* key = mKeys.valueAt(index);
173 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
174 if ((behavior->metaState & metaState) == behavior->metaState) {
175 result = behavior->character;
176 break;
177 }
178 }
179 }
180#if DEBUG_MAPPING
181 LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
182#endif
183 return result;
184}
185
186char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
187 int32_t metaState) const {
188 char16_t result = 0;
189 ssize_t index = mKeys.indexOfKey(keyCode);
190 if (index >= 0) {
191 const Key* key = mKeys.valueAt(index);
192
193 // Try to find the most general behavior that maps to this character.
194 // For example, the base key behavior will usually be last in the list.
195 // However, if we find a perfect meta state match for one behavior then use that one.
196 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
197 if (behavior->character) {
198 for (size_t i = 0; i < numChars; i++) {
199 if (behavior->character == chars[i]) {
200 result = behavior->character;
201 if ((behavior->metaState & metaState) == behavior->metaState) {
202 goto ExactMatch;
203 }
204 break;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800205 }
206 }
207 }
208 }
Jeff Browna3477c82010-11-10 16:03:06 -0800209 ExactMatch: ;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800210 }
Jeff Browna3477c82010-11-10 16:03:06 -0800211#if DEBUG_MAPPING
212 LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
213 keyCode, toString(chars, numChars).string(), metaState, result);
214#endif
215 return result;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800216}
217
Jeff Browna3477c82010-11-10 16:03:06 -0800218bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
219 Vector<KeyEvent>& outEvents) const {
220 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
221
222 for (size_t i = 0; i < numChars; i++) {
223 int32_t keyCode, metaState;
224 char16_t ch = chars[i];
225 if (!findKey(ch, &keyCode, &metaState)) {
226#if DEBUG_MAPPING
227 LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
228 deviceId, toString(chars, numChars).string(), ch);
229#endif
230 return false;
231 }
232
233 int32_t currentMetaState = 0;
234 addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
235 addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
236 addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
237 addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800238 }
Jeff Browna3477c82010-11-10 16:03:06 -0800239#if DEBUG_MAPPING
240 LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
241 deviceId, toString(chars, numChars).string(), outEvents.size());
242 for (size_t i = 0; i < outEvents.size(); i++) {
243 LOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
244 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
245 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
246 }
247#endif
248 return true;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800249}
250
Jeff Browna3477c82010-11-10 16:03:06 -0800251bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
252 if (!ch) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800253 return false;
254 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800255
Jeff Browna3477c82010-11-10 16:03:06 -0800256 for (size_t i = 0; i < mKeys.size(); i++) {
257 const Key* key = mKeys.valueAt(i);
258
259 // Try to find the most general behavior that maps to this character.
260 // For example, the base key behavior will usually be last in the list.
261 const Behavior* found = NULL;
262 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
263 if (behavior->character == ch) {
264 found = behavior;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800265 }
Jeff Browna3477c82010-11-10 16:03:06 -0800266 }
267 if (found) {
268 *outKeyCode = mKeys.keyAt(i);
269 *outMetaState = found->metaState;
270 return true;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800271 }
272 }
273 return false;
274}
275
Jeff Browna3477c82010-11-10 16:03:06 -0800276void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
277 int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
278 outEvents.push();
279 KeyEvent& event = outEvents.editTop();
280 event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
281 down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
282 0, keyCode, 0, metaState, 0, time, time);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800283}
284
Jeff Browna3477c82010-11-10 16:03:06 -0800285void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
286 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
287 int32_t* currentMetaState) {
288 // Add and remove meta keys symmetrically.
289 if (down) {
290 addLockedMetaKey(outEvents, deviceId, metaState, time,
291 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
292 addLockedMetaKey(outEvents, deviceId, metaState, time,
293 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
294 addLockedMetaKey(outEvents, deviceId, metaState, time,
295 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800296
Jeff Browna3477c82010-11-10 16:03:06 -0800297 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
298 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
299 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
300 AMETA_SHIFT_ON, currentMetaState);
301 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
302 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
303 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
304 AMETA_ALT_ON, currentMetaState);
305 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
306 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
307 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
308 AMETA_CTRL_ON, currentMetaState);
309 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
310 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
311 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
312 AMETA_META_ON, currentMetaState);
Jeff Brown6a817e22010-09-12 17:55:08 -0700313
Jeff Browna3477c82010-11-10 16:03:06 -0800314 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
315 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
316 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
317 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800318 } else {
Jeff Browna3477c82010-11-10 16:03:06 -0800319 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
320 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
321 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
322 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800323
Jeff Browna3477c82010-11-10 16:03:06 -0800324 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
325 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
326 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
327 AMETA_META_ON, currentMetaState);
328 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
329 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
330 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
331 AMETA_CTRL_ON, currentMetaState);
332 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
333 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
334 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
335 AMETA_ALT_ON, currentMetaState);
336 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
337 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
338 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
339 AMETA_SHIFT_ON, currentMetaState);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800340
Jeff Browna3477c82010-11-10 16:03:06 -0800341 addLockedMetaKey(outEvents, deviceId, metaState, time,
342 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
343 addLockedMetaKey(outEvents, deviceId, metaState, time,
344 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
345 addLockedMetaKey(outEvents, deviceId, metaState, time,
346 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
347 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800348}
349
Jeff Browna3477c82010-11-10 16:03:06 -0800350bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
351 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
352 int32_t keyCode, int32_t keyMetaState,
353 int32_t* currentMetaState) {
354 if ((metaState & keyMetaState) == keyMetaState) {
355 *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
356 addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
357 return true;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800358 }
Jeff Browna3477c82010-11-10 16:03:06 -0800359 return false;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800360}
Jeff Browna3477c82010-11-10 16:03:06 -0800361
362void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
363 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
364 int32_t leftKeyCode, int32_t leftKeyMetaState,
365 int32_t rightKeyCode, int32_t rightKeyMetaState,
366 int32_t eitherKeyMetaState,
367 int32_t* currentMetaState) {
368 bool specific = false;
369 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
370 leftKeyCode, leftKeyMetaState, currentMetaState);
371 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
372 rightKeyCode, rightKeyMetaState, currentMetaState);
373
374 if (!specific) {
375 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
376 leftKeyCode, eitherKeyMetaState, currentMetaState);
377 }
378}
379
380void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
381 int32_t deviceId, int32_t metaState, nsecs_t time,
382 int32_t keyCode, int32_t keyMetaState,
383 int32_t* currentMetaState) {
384 if ((metaState & keyMetaState) == keyMetaState) {
385 *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
386 addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
387 *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
388 addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
389 }
390}
391
392
393// --- KeyCharacterMap::Key ---
394
395KeyCharacterMap::Key::Key() :
396 label(0), number(0), firstBehavior(NULL) {
397}
398
399KeyCharacterMap::Key::~Key() {
400 Behavior* behavior = firstBehavior;
401 while (behavior) {
402 Behavior* next = behavior->next;
403 delete behavior;
404 behavior = next;
405 }
406}
407
408
409// --- KeyCharacterMap::Behavior ---
410
411KeyCharacterMap::Behavior::Behavior() :
412 next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
413}
414
415
416// --- KeyCharacterMap::Parser ---
417
418KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
419 mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
420}
421
422KeyCharacterMap::Parser::~Parser() {
423}
424
425status_t KeyCharacterMap::Parser::parse() {
426 while (!mTokenizer->isEof()) {
427#if DEBUG_PARSER
428 LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
429 mTokenizer->peekRemainderOfLine().string());
430#endif
431
432 mTokenizer->skipDelimiters(WHITESPACE);
433
434 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
435 switch (mState) {
436 case STATE_TOP: {
437 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
438 if (keywordToken == "type") {
439 mTokenizer->skipDelimiters(WHITESPACE);
440 status_t status = parseType();
441 if (status) return status;
442 } else if (keywordToken == "key") {
443 mTokenizer->skipDelimiters(WHITESPACE);
444 status_t status = parseKey();
445 if (status) return status;
446 } else {
447 LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
448 keywordToken.string());
449 return BAD_VALUE;
450 }
451 break;
452 }
453
454 case STATE_KEY: {
455 status_t status = parseKeyProperty();
456 if (status) return status;
457 break;
458 }
459 }
460
461 mTokenizer->skipDelimiters(WHITESPACE);
462 if (!mTokenizer->isEol()) {
463 LOGE("%s: Expected end of line, got '%s'.",
464 mTokenizer->getLocation().string(),
465 mTokenizer->peekRemainderOfLine().string());
466 return BAD_VALUE;
467 }
468 }
469
470 mTokenizer->nextLine();
471 }
472
473 if (mState != STATE_TOP) {
474 LOGE("%s: Unterminated key description at end of file.",
475 mTokenizer->getLocation().string());
476 return BAD_VALUE;
477 }
478
479 if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
480 LOGE("%s: Missing required keyboard 'type' declaration.",
481 mTokenizer->getLocation().string());
482 return BAD_VALUE;
483 }
484
485 return NO_ERROR;
486}
487
488status_t KeyCharacterMap::Parser::parseType() {
489 if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
490 LOGE("%s: Duplicate keyboard 'type' declaration.",
491 mTokenizer->getLocation().string());
492 return BAD_VALUE;
493 }
494
495 KeyboardType type;
496 String8 typeToken = mTokenizer->nextToken(WHITESPACE);
497 if (typeToken == "NUMERIC") {
498 type = KEYBOARD_TYPE_NUMERIC;
499 } else if (typeToken == "PREDICTIVE") {
500 type = KEYBOARD_TYPE_PREDICTIVE;
501 } else if (typeToken == "ALPHA") {
502 type = KEYBOARD_TYPE_ALPHA;
503 } else if (typeToken == "FULL") {
504 type = KEYBOARD_TYPE_FULL;
505 } else if (typeToken == "SPECIAL_FUNCTION") {
506 type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
507 } else {
508 LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
509 typeToken.string());
510 return BAD_VALUE;
511 }
512
513#if DEBUG_PARSER
514 LOGD("Parsed type: type=%d.", type);
515#endif
516 mMap->mType = type;
517 return NO_ERROR;
518}
519
520status_t KeyCharacterMap::Parser::parseKey() {
521 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
522 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
523 if (!keyCode) {
524 LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
525 keyCodeToken.string());
526 return BAD_VALUE;
527 }
528 if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
529 LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
530 keyCodeToken.string());
531 return BAD_VALUE;
532 }
533
534 mTokenizer->skipDelimiters(WHITESPACE);
535 String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
536 if (openBraceToken != "{") {
537 LOGE("%s: Expected '{' after key code label, got '%s'.",
538 mTokenizer->getLocation().string(), openBraceToken.string());
539 return BAD_VALUE;
540 }
541
542#if DEBUG_PARSER
543 LOGD("Parsed beginning of key: keyCode=%d.", keyCode);
544#endif
545 mKeyCode = keyCode;
546 mMap->mKeys.add(keyCode, new Key());
547 mState = STATE_KEY;
548 return NO_ERROR;
549}
550
551status_t KeyCharacterMap::Parser::parseKeyProperty() {
552 String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
553 if (token == "}") {
554 mState = STATE_TOP;
555 return NO_ERROR;
556 }
557
558 Vector<Property> properties;
559
560 // Parse all comma-delimited property names up to the first colon.
561 for (;;) {
562 if (token == "label") {
563 properties.add(Property(PROPERTY_LABEL));
564 } else if (token == "number") {
565 properties.add(Property(PROPERTY_NUMBER));
566 } else {
567 int32_t metaState;
568 status_t status = parseModifier(token, &metaState);
569 if (status) {
570 LOGE("%s: Expected a property name or modifier, got '%s'.",
571 mTokenizer->getLocation().string(), token.string());
572 return status;
573 }
574 properties.add(Property(PROPERTY_META, metaState));
575 }
576
577 mTokenizer->skipDelimiters(WHITESPACE);
578 if (!mTokenizer->isEol()) {
579 char ch = mTokenizer->nextChar();
580 if (ch == ':') {
581 break;
582 } else if (ch == ',') {
583 mTokenizer->skipDelimiters(WHITESPACE);
584 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
585 continue;
586 }
587 }
588
589 LOGE("%s: Expected ',' or ':' after property name.",
590 mTokenizer->getLocation().string());
591 return BAD_VALUE;
592 }
593
594 // Parse behavior after the colon.
595 mTokenizer->skipDelimiters(WHITESPACE);
596
597 Behavior behavior;
598 bool haveCharacter = false;
599 bool haveFallback = false;
600
601 do {
602 char ch = mTokenizer->peekChar();
603 if (ch == '\'') {
604 char16_t character;
605 status_t status = parseCharacterLiteral(&character);
606 if (status || !character) {
607 LOGE("%s: Invalid character literal for key.",
608 mTokenizer->getLocation().string());
609 return BAD_VALUE;
610 }
611 if (haveCharacter) {
612 LOGE("%s: Cannot combine multiple character literals or 'none'.",
613 mTokenizer->getLocation().string());
614 return BAD_VALUE;
615 }
616 behavior.character = character;
617 haveCharacter = true;
618 } else {
619 token = mTokenizer->nextToken(WHITESPACE);
620 if (token == "none") {
621 if (haveCharacter) {
622 LOGE("%s: Cannot combine multiple character literals or 'none'.",
623 mTokenizer->getLocation().string());
624 return BAD_VALUE;
625 }
626 haveCharacter = true;
627 } else if (token == "fallback") {
628 mTokenizer->skipDelimiters(WHITESPACE);
629 token = mTokenizer->nextToken(WHITESPACE);
630 int32_t keyCode = getKeyCodeByLabel(token.string());
631 if (!keyCode) {
632 LOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
633 mTokenizer->getLocation().string(),
634 token.string());
635 return BAD_VALUE;
636 }
637 if (haveFallback) {
638 LOGE("%s: Cannot combine multiple fallback key codes.",
639 mTokenizer->getLocation().string());
640 return BAD_VALUE;
641 }
642 behavior.fallbackKeyCode = keyCode;
643 haveFallback = true;
644 } else {
645 LOGE("%s: Expected a key behavior after ':'.",
646 mTokenizer->getLocation().string());
647 return BAD_VALUE;
648 }
649 }
650
651 mTokenizer->skipDelimiters(WHITESPACE);
652 } while (!mTokenizer->isEol());
653
654 // Add the behavior.
655 Key* key = mMap->mKeys.valueFor(mKeyCode);
656 for (size_t i = 0; i < properties.size(); i++) {
657 const Property& property = properties.itemAt(i);
658 switch (property.property) {
659 case PROPERTY_LABEL:
660 if (key->label) {
661 LOGE("%s: Duplicate label for key.",
662 mTokenizer->getLocation().string());
663 return BAD_VALUE;
664 }
665 key->label = behavior.character;
666#if DEBUG_PARSER
667 LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
668#endif
669 break;
670 case PROPERTY_NUMBER:
671 if (key->number) {
672 LOGE("%s: Duplicate number for key.",
673 mTokenizer->getLocation().string());
674 return BAD_VALUE;
675 }
676 key->number = behavior.character;
677#if DEBUG_PARSER
678 LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
679#endif
680 break;
681 case PROPERTY_META: {
682 for (Behavior* b = key->firstBehavior; b; b = b->next) {
683 if (b->metaState == property.metaState) {
684 LOGE("%s: Duplicate key behavior for modifier.",
685 mTokenizer->getLocation().string());
686 return BAD_VALUE;
687 }
688 }
689 Behavior* newBehavior = new Behavior(behavior);
690 newBehavior->metaState = property.metaState;
691 newBehavior->next = key->firstBehavior;
692 key->firstBehavior = newBehavior;
693#if DEBUG_PARSER
694 LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
695 newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
696#endif
697 break;
698 }
699 }
700 }
701 return NO_ERROR;
702}
703
704status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
705 if (token == "base") {
706 *outMetaState = 0;
707 return NO_ERROR;
708 }
709
710 int32_t combinedMeta = 0;
711
712 const char* str = token.string();
713 const char* start = str;
714 for (const char* cur = str; ; cur++) {
715 char ch = *cur;
716 if (ch == '+' || ch == '\0') {
717 size_t len = cur - start;
718 int32_t metaState = 0;
719 for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
720 if (strlen(modifiers[i].label) == len
721 && strncmp(modifiers[i].label, start, len) == 0) {
722 metaState = modifiers[i].metaState;
723 break;
724 }
725 }
726 if (!metaState) {
727 return BAD_VALUE;
728 }
729 if (combinedMeta & metaState) {
730 LOGE("%s: Duplicate modifier combination '%s'.",
731 mTokenizer->getLocation().string(), token.string());
732 return BAD_VALUE;
733 }
734
735 combinedMeta |= metaState;
Jeff Brownd5fdf7d2010-11-18 20:52:43 -0800736 start = cur + 1;
Jeff Browna3477c82010-11-10 16:03:06 -0800737
738 if (ch == '\0') {
739 break;
740 }
741 }
742 }
743 *outMetaState = combinedMeta;
744 return NO_ERROR;
745}
746
747status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
748 char ch = mTokenizer->nextChar();
749 if (ch != '\'') {
750 goto Error;
751 }
752
753 ch = mTokenizer->nextChar();
754 if (ch == '\\') {
755 // Escape sequence.
756 ch = mTokenizer->nextChar();
757 if (ch == 'n') {
758 *outCharacter = '\n';
759 } else if (ch == 't') {
760 *outCharacter = '\t';
761 } else if (ch == '\\') {
762 *outCharacter = '\\';
763 } else if (ch == '\'') {
764 *outCharacter = '\'';
765 } else if (ch == '"') {
766 *outCharacter = '"';
767 } else if (ch == 'u') {
768 *outCharacter = 0;
769 for (int i = 0; i < 4; i++) {
770 ch = mTokenizer->nextChar();
771 int digit;
772 if (ch >= '0' && ch <= '9') {
773 digit = ch - '0';
774 } else if (ch >= 'A' && ch <= 'F') {
775 digit = ch - 'A' + 10;
776 } else if (ch >= 'a' && ch <= 'f') {
777 digit = ch - 'a' + 10;
778 } else {
779 goto Error;
780 }
781 *outCharacter = (*outCharacter << 4) | digit;
782 }
783 } else {
784 goto Error;
785 }
786 } else if (ch >= 32 && ch <= 126 && ch != '\'') {
787 // ASCII literal character.
788 *outCharacter = ch;
789 } else {
790 goto Error;
791 }
792
793 ch = mTokenizer->nextChar();
794 if (ch != '\'') {
795 goto Error;
796 }
797
798 // Ensure that we consumed the entire token.
799 if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
800 return NO_ERROR;
801 }
802
803Error:
804 LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
805 return BAD_VALUE;
806}
807
808} // namespace android