blob: 9abbf38bce0104ba3eff26791cc26a7dee61094c [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 "KeyCharacterMap"
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019#include <stdlib.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020#include <string.h>
Jeff Brown6b53e8d2010-11-10 16:03:06 -080021#include <android/keycodes.h>
Mathias Agopianb93a03f82012-02-17 15:34:57 -080022#include <androidfw/Keyboard.h>
23#include <androidfw/KeyCharacterMap.h>
Jeff Brown9f25b7f2012-04-10 14:30:49 -070024
25#if HAVE_ANDROID_OS
26#include <binder/Parcel.h>
27#endif
28
Jeff Brown6b53e8d2010-11-10 16:03:06 -080029#include <utils/Log.h>
30#include <utils/Errors.h>
31#include <utils/Tokenizer.h>
32#include <utils/Timers.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
Jeff Brown6b53e8d2010-11-10 16:03:06 -080034// Enables debug output for the parser.
35#define DEBUG_PARSER 0
36
37// Enables debug output for parser performance.
38#define DEBUG_PARSER_PERFORMANCE 0
39
40// Enables debug output for mapping.
41#define DEBUG_MAPPING 0
42
43
44namespace android {
45
46static const char* WHITESPACE = " \t\r";
47static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
48
49struct Modifier {
50 const char* label;
51 int32_t metaState;
52};
53static const Modifier modifiers[] = {
54 { "shift", AMETA_SHIFT_ON },
55 { "lshift", AMETA_SHIFT_LEFT_ON },
56 { "rshift", AMETA_SHIFT_RIGHT_ON },
57 { "alt", AMETA_ALT_ON },
58 { "lalt", AMETA_ALT_LEFT_ON },
59 { "ralt", AMETA_ALT_RIGHT_ON },
60 { "ctrl", AMETA_CTRL_ON },
61 { "lctrl", AMETA_CTRL_LEFT_ON },
62 { "rctrl", AMETA_CTRL_RIGHT_ON },
63 { "meta", AMETA_META_ON },
64 { "lmeta", AMETA_META_LEFT_ON },
65 { "rmeta", AMETA_META_RIGHT_ON },
66 { "sym", AMETA_SYM_ON },
67 { "fn", AMETA_FUNCTION_ON },
68 { "capslock", AMETA_CAPS_LOCK_ON },
69 { "numlock", AMETA_NUM_LOCK_ON },
70 { "scrolllock", AMETA_SCROLL_LOCK_ON },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071};
72
Jeff Brown6b53e8d2010-11-10 16:03:06 -080073#if DEBUG_MAPPING
74static String8 toString(const char16_t* chars, size_t numChars) {
75 String8 result;
76 for (size_t i = 0; i < numChars; i++) {
77 result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -080079 return result;
80}
81#endif
82
83
84// --- KeyCharacterMap ---
85
Jeff Brown9f25b7f2012-04-10 14:30:49 -070086sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
87
Jeff Brown6b53e8d2010-11-10 16:03:06 -080088KeyCharacterMap::KeyCharacterMap() :
89 mType(KEYBOARD_TYPE_UNKNOWN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090}
91
Jeff Brown6b53e8d2010-11-10 16:03:06 -080092KeyCharacterMap::~KeyCharacterMap() {
93 for (size_t i = 0; i < mKeys.size(); i++) {
94 Key* key = mKeys.editValueAt(i);
95 delete key;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097}
98
Jeff Brown9f25b7f2012-04-10 14:30:49 -070099status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* outMap) {
100 outMap->clear();
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800101
102 Tokenizer* tokenizer;
103 status_t status = Tokenizer::open(filename, &tokenizer);
104 if (status) {
Steve Block3762c312012-01-06 19:20:56 +0000105 ALOGE("Error %d opening key character map file %s.", status, filename.string());
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800106 } else {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700107 sp<KeyCharacterMap> map = new KeyCharacterMap();
108 if (!map.get()) {
Steve Block3762c312012-01-06 19:20:56 +0000109 ALOGE("Error allocating key character map.");
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800110 status = NO_MEMORY;
111 } else {
112#if DEBUG_PARSER_PERFORMANCE
113 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
114#endif
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700115 Parser parser(map.get(), tokenizer);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800116 status = parser.parse();
117#if DEBUG_PARSER_PERFORMANCE
118 nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Steve Block5baa3a62011-12-20 16:23:08 +0000119 ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800120 tokenizer->getFilename().string(), tokenizer->getLineNumber(),
121 elapsedTime / 1000000.0);
122#endif
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700123 if (!status) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800124 *outMap = map;
125 }
126 }
127 delete tokenizer;
128 }
129 return status;
130}
131
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700132sp<KeyCharacterMap> KeyCharacterMap::empty() {
133 return sEmpty;
134}
135
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800136int32_t KeyCharacterMap::getKeyboardType() const {
137 return mType;
138}
139
140char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
141 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800142 const Key* key;
143 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800144 result = key->label;
145 }
146#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000147 ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800148#endif
149 return result;
150}
151
152char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
153 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800154 const Key* key;
155 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800156 result = key->number;
157 }
158#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000159 ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800160#endif
161 return result;
162}
163
164char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
165 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800166 const Key* key;
167 const Behavior* behavior;
168 if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
169 result = behavior->character;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800170 }
171#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000172 ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800173#endif
174 return result;
175}
176
Jeff Brown49ed71d2010-12-06 17:13:33 -0800177bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
178 FallbackAction* outFallbackAction) const {
179 outFallbackAction->keyCode = 0;
180 outFallbackAction->metaState = 0;
181
182 bool result = false;
183 const Key* key;
184 const Behavior* behavior;
185 if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
Jeff Brownfca66d32011-01-14 17:54:38 -0800186 if (behavior->fallbackKeyCode) {
187 outFallbackAction->keyCode = behavior->fallbackKeyCode;
188 outFallbackAction->metaState = metaState & ~behavior->metaState;
189 result = true;
190 }
Jeff Brown49ed71d2010-12-06 17:13:33 -0800191 }
192#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000193 ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
Jeff Brown49ed71d2010-12-06 17:13:33 -0800194 "fallback keyCode=%d, fallback metaState=0x%08x.",
195 keyCode, metaState, result ? "true" : "false",
196 outFallbackAction->keyCode, outFallbackAction->metaState);
197#endif
198 return result;
199}
200
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800201char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
202 int32_t metaState) const {
203 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800204 const Key* key;
205 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800206 // Try to find the most general behavior that maps to this character.
207 // For example, the base key behavior will usually be last in the list.
208 // However, if we find a perfect meta state match for one behavior then use that one.
209 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
210 if (behavior->character) {
211 for (size_t i = 0; i < numChars; i++) {
212 if (behavior->character == chars[i]) {
213 result = behavior->character;
214 if ((behavior->metaState & metaState) == behavior->metaState) {
215 goto ExactMatch;
216 }
217 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219 }
220 }
221 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800222 ExactMatch: ;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800224#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000225 ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800226 keyCode, toString(chars, numChars).string(), metaState, result);
227#endif
228 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229}
230
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800231bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
232 Vector<KeyEvent>& outEvents) const {
233 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
234
235 for (size_t i = 0; i < numChars; i++) {
236 int32_t keyCode, metaState;
237 char16_t ch = chars[i];
238 if (!findKey(ch, &keyCode, &metaState)) {
239#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000240 ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800241 deviceId, toString(chars, numChars).string(), ch);
242#endif
243 return false;
244 }
245
246 int32_t currentMetaState = 0;
247 addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
248 addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
249 addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
250 addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800252#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000253 ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
Jeff Brown49ed71d2010-12-06 17:13:33 -0800254 deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800255 for (size_t i = 0; i < outEvents.size(); i++) {
Steve Block5baa3a62011-12-20 16:23:08 +0000256 ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800257 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
258 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
259 }
260#endif
261 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262}
263
Jeff Brown49ed71d2010-12-06 17:13:33 -0800264bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
265 ssize_t index = mKeys.indexOfKey(keyCode);
266 if (index >= 0) {
267 *outKey = mKeys.valueAt(index);
268 return true;
269 }
270 return false;
271}
272
273bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
274 const Key** outKey, const Behavior** outBehavior) const {
275 const Key* key;
276 if (getKey(keyCode, &key)) {
277 const Behavior* behavior = key->firstBehavior;
278 while (behavior) {
279 if ((behavior->metaState & metaState) == behavior->metaState) {
280 *outKey = key;
281 *outBehavior = behavior;
282 return true;
283 }
284 behavior = behavior->next;
285 }
286 }
287 return false;
288}
289
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800290bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
291 if (!ch) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 return false;
293 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800295 for (size_t i = 0; i < mKeys.size(); i++) {
296 const Key* key = mKeys.valueAt(i);
297
298 // Try to find the most general behavior that maps to this character.
299 // For example, the base key behavior will usually be last in the list.
300 const Behavior* found = NULL;
301 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
302 if (behavior->character == ch) {
303 found = behavior;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800305 }
306 if (found) {
307 *outKeyCode = mKeys.keyAt(i);
308 *outMetaState = found->metaState;
309 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311 }
312 return false;
313}
314
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800315void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
316 int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
317 outEvents.push();
318 KeyEvent& event = outEvents.editTop();
319 event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
320 down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
321 0, keyCode, 0, metaState, 0, time, time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322}
323
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800324void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
325 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
326 int32_t* currentMetaState) {
327 // Add and remove meta keys symmetrically.
328 if (down) {
329 addLockedMetaKey(outEvents, deviceId, metaState, time,
330 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
331 addLockedMetaKey(outEvents, deviceId, metaState, time,
332 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
333 addLockedMetaKey(outEvents, deviceId, metaState, time,
334 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800336 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
337 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
338 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
339 AMETA_SHIFT_ON, currentMetaState);
340 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
341 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
342 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
343 AMETA_ALT_ON, currentMetaState);
344 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
345 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
346 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
347 AMETA_CTRL_ON, currentMetaState);
348 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
349 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
350 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
351 AMETA_META_ON, currentMetaState);
Jeff Brown497a92c2010-09-12 17:55:08 -0700352
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800353 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
354 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
355 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
356 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 } else {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800358 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
359 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
360 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
361 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800363 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
364 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
365 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
366 AMETA_META_ON, currentMetaState);
367 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
368 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
369 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
370 AMETA_CTRL_ON, currentMetaState);
371 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
372 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
373 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
374 AMETA_ALT_ON, currentMetaState);
375 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
376 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
377 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
378 AMETA_SHIFT_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800380 addLockedMetaKey(outEvents, deviceId, metaState, time,
381 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
382 addLockedMetaKey(outEvents, deviceId, metaState, time,
383 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
384 addLockedMetaKey(outEvents, deviceId, metaState, time,
385 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
386 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387}
388
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800389bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
390 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
391 int32_t keyCode, int32_t keyMetaState,
392 int32_t* currentMetaState) {
393 if ((metaState & keyMetaState) == keyMetaState) {
394 *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
395 addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
396 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800398 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399}
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800400
401void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
402 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
403 int32_t leftKeyCode, int32_t leftKeyMetaState,
404 int32_t rightKeyCode, int32_t rightKeyMetaState,
405 int32_t eitherKeyMetaState,
406 int32_t* currentMetaState) {
407 bool specific = false;
408 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
409 leftKeyCode, leftKeyMetaState, currentMetaState);
410 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
411 rightKeyCode, rightKeyMetaState, currentMetaState);
412
413 if (!specific) {
414 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
415 leftKeyCode, eitherKeyMetaState, currentMetaState);
416 }
417}
418
419void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
420 int32_t deviceId, int32_t metaState, nsecs_t time,
421 int32_t keyCode, int32_t keyMetaState,
422 int32_t* currentMetaState) {
423 if ((metaState & keyMetaState) == keyMetaState) {
424 *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
425 addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
426 *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
427 addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
428 }
429}
430
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700431#if HAVE_ANDROID_OS
432sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
433 sp<KeyCharacterMap> map = new KeyCharacterMap();
434 map->mType = parcel->readInt32();
435 size_t numKeys = parcel->readInt32();
436 if (parcel->errorCheck()) {
437 return NULL;
438 }
439
440 for (size_t i = 0; i < numKeys; i++) {
441 int32_t keyCode = parcel->readInt32();
442 char16_t label = parcel->readInt32();
443 char16_t number = parcel->readInt32();
444 if (parcel->errorCheck()) {
445 return NULL;
446 }
447
448 Key* key = new Key();
449 key->label = label;
450 key->number = number;
451 map->mKeys.add(keyCode, key);
452
453 Behavior* lastBehavior = NULL;
454 while (parcel->readInt32()) {
455 int32_t metaState = parcel->readInt32();
456 char16_t character = parcel->readInt32();
457 int32_t fallbackKeyCode = parcel->readInt32();
458 if (parcel->errorCheck()) {
459 return NULL;
460 }
461
462 Behavior* behavior = new Behavior();
463 behavior->metaState = metaState;
464 behavior->character = character;
465 behavior->fallbackKeyCode = fallbackKeyCode;
466 if (lastBehavior) {
467 lastBehavior->next = behavior;
468 } else {
469 key->firstBehavior = behavior;
470 }
471 lastBehavior = behavior;
472 }
473
474 if (parcel->errorCheck()) {
475 return NULL;
476 }
477 }
478 return map;
479}
480
481void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
482 parcel->writeInt32(mType);
483
484 size_t numKeys = mKeys.size();
485 parcel->writeInt32(numKeys);
486 for (size_t i = 0; i < numKeys; i++) {
487 int32_t keyCode = mKeys.keyAt(i);
488 const Key* key = mKeys.valueAt(i);
489 parcel->writeInt32(keyCode);
490 parcel->writeInt32(key->label);
491 parcel->writeInt32(key->number);
492 for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
493 behavior = behavior->next) {
494 parcel->writeInt32(1);
495 parcel->writeInt32(behavior->metaState);
496 parcel->writeInt32(behavior->character);
497 parcel->writeInt32(behavior->fallbackKeyCode);
498 }
499 parcel->writeInt32(0);
500 }
501}
502#endif
503
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800504
505// --- KeyCharacterMap::Key ---
506
507KeyCharacterMap::Key::Key() :
508 label(0), number(0), firstBehavior(NULL) {
509}
510
511KeyCharacterMap::Key::~Key() {
512 Behavior* behavior = firstBehavior;
513 while (behavior) {
514 Behavior* next = behavior->next;
515 delete behavior;
516 behavior = next;
517 }
518}
519
520
521// --- KeyCharacterMap::Behavior ---
522
523KeyCharacterMap::Behavior::Behavior() :
524 next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
525}
526
527
528// --- KeyCharacterMap::Parser ---
529
530KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
531 mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
532}
533
534KeyCharacterMap::Parser::~Parser() {
535}
536
537status_t KeyCharacterMap::Parser::parse() {
538 while (!mTokenizer->isEof()) {
539#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000540 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800541 mTokenizer->peekRemainderOfLine().string());
542#endif
543
544 mTokenizer->skipDelimiters(WHITESPACE);
545
546 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
547 switch (mState) {
548 case STATE_TOP: {
549 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
550 if (keywordToken == "type") {
551 mTokenizer->skipDelimiters(WHITESPACE);
552 status_t status = parseType();
553 if (status) return status;
554 } else if (keywordToken == "key") {
555 mTokenizer->skipDelimiters(WHITESPACE);
556 status_t status = parseKey();
557 if (status) return status;
558 } else {
Steve Block3762c312012-01-06 19:20:56 +0000559 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800560 keywordToken.string());
561 return BAD_VALUE;
562 }
563 break;
564 }
565
566 case STATE_KEY: {
567 status_t status = parseKeyProperty();
568 if (status) return status;
569 break;
570 }
571 }
572
573 mTokenizer->skipDelimiters(WHITESPACE);
574 if (!mTokenizer->isEol()) {
Steve Block3762c312012-01-06 19:20:56 +0000575 ALOGE("%s: Expected end of line, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800576 mTokenizer->getLocation().string(),
577 mTokenizer->peekRemainderOfLine().string());
578 return BAD_VALUE;
579 }
580 }
581
582 mTokenizer->nextLine();
583 }
584
585 if (mState != STATE_TOP) {
Steve Block3762c312012-01-06 19:20:56 +0000586 ALOGE("%s: Unterminated key description at end of file.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800587 mTokenizer->getLocation().string());
588 return BAD_VALUE;
589 }
590
591 if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
Steve Block3762c312012-01-06 19:20:56 +0000592 ALOGE("%s: Missing required keyboard 'type' declaration.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800593 mTokenizer->getLocation().string());
594 return BAD_VALUE;
595 }
596
597 return NO_ERROR;
598}
599
600status_t KeyCharacterMap::Parser::parseType() {
601 if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
Steve Block3762c312012-01-06 19:20:56 +0000602 ALOGE("%s: Duplicate keyboard 'type' declaration.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800603 mTokenizer->getLocation().string());
604 return BAD_VALUE;
605 }
606
607 KeyboardType type;
608 String8 typeToken = mTokenizer->nextToken(WHITESPACE);
609 if (typeToken == "NUMERIC") {
610 type = KEYBOARD_TYPE_NUMERIC;
611 } else if (typeToken == "PREDICTIVE") {
612 type = KEYBOARD_TYPE_PREDICTIVE;
613 } else if (typeToken == "ALPHA") {
614 type = KEYBOARD_TYPE_ALPHA;
615 } else if (typeToken == "FULL") {
616 type = KEYBOARD_TYPE_FULL;
617 } else if (typeToken == "SPECIAL_FUNCTION") {
618 type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
619 } else {
Steve Block3762c312012-01-06 19:20:56 +0000620 ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800621 typeToken.string());
622 return BAD_VALUE;
623 }
624
625#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000626 ALOGD("Parsed type: type=%d.", type);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800627#endif
628 mMap->mType = type;
629 return NO_ERROR;
630}
631
632status_t KeyCharacterMap::Parser::parseKey() {
633 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
634 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
635 if (!keyCode) {
Steve Block3762c312012-01-06 19:20:56 +0000636 ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800637 keyCodeToken.string());
638 return BAD_VALUE;
639 }
640 if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
Steve Block3762c312012-01-06 19:20:56 +0000641 ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800642 keyCodeToken.string());
643 return BAD_VALUE;
644 }
645
646 mTokenizer->skipDelimiters(WHITESPACE);
647 String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
648 if (openBraceToken != "{") {
Steve Block3762c312012-01-06 19:20:56 +0000649 ALOGE("%s: Expected '{' after key code label, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800650 mTokenizer->getLocation().string(), openBraceToken.string());
651 return BAD_VALUE;
652 }
653
654#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000655 ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800656#endif
657 mKeyCode = keyCode;
658 mMap->mKeys.add(keyCode, new Key());
659 mState = STATE_KEY;
660 return NO_ERROR;
661}
662
663status_t KeyCharacterMap::Parser::parseKeyProperty() {
664 String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
665 if (token == "}") {
666 mState = STATE_TOP;
667 return NO_ERROR;
668 }
669
670 Vector<Property> properties;
671
672 // Parse all comma-delimited property names up to the first colon.
673 for (;;) {
674 if (token == "label") {
675 properties.add(Property(PROPERTY_LABEL));
676 } else if (token == "number") {
677 properties.add(Property(PROPERTY_NUMBER));
678 } else {
679 int32_t metaState;
680 status_t status = parseModifier(token, &metaState);
681 if (status) {
Steve Block3762c312012-01-06 19:20:56 +0000682 ALOGE("%s: Expected a property name or modifier, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800683 mTokenizer->getLocation().string(), token.string());
684 return status;
685 }
686 properties.add(Property(PROPERTY_META, metaState));
687 }
688
689 mTokenizer->skipDelimiters(WHITESPACE);
690 if (!mTokenizer->isEol()) {
691 char ch = mTokenizer->nextChar();
692 if (ch == ':') {
693 break;
694 } else if (ch == ',') {
695 mTokenizer->skipDelimiters(WHITESPACE);
696 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
697 continue;
698 }
699 }
700
Steve Block3762c312012-01-06 19:20:56 +0000701 ALOGE("%s: Expected ',' or ':' after property name.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800702 mTokenizer->getLocation().string());
703 return BAD_VALUE;
704 }
705
706 // Parse behavior after the colon.
707 mTokenizer->skipDelimiters(WHITESPACE);
708
709 Behavior behavior;
710 bool haveCharacter = false;
711 bool haveFallback = false;
712
713 do {
714 char ch = mTokenizer->peekChar();
715 if (ch == '\'') {
716 char16_t character;
717 status_t status = parseCharacterLiteral(&character);
718 if (status || !character) {
Steve Block3762c312012-01-06 19:20:56 +0000719 ALOGE("%s: Invalid character literal for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800720 mTokenizer->getLocation().string());
721 return BAD_VALUE;
722 }
723 if (haveCharacter) {
Steve Block3762c312012-01-06 19:20:56 +0000724 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800725 mTokenizer->getLocation().string());
726 return BAD_VALUE;
727 }
728 behavior.character = character;
729 haveCharacter = true;
730 } else {
731 token = mTokenizer->nextToken(WHITESPACE);
732 if (token == "none") {
733 if (haveCharacter) {
Steve Block3762c312012-01-06 19:20:56 +0000734 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800735 mTokenizer->getLocation().string());
736 return BAD_VALUE;
737 }
738 haveCharacter = true;
739 } else if (token == "fallback") {
740 mTokenizer->skipDelimiters(WHITESPACE);
741 token = mTokenizer->nextToken(WHITESPACE);
742 int32_t keyCode = getKeyCodeByLabel(token.string());
743 if (!keyCode) {
Steve Block3762c312012-01-06 19:20:56 +0000744 ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800745 mTokenizer->getLocation().string(),
746 token.string());
747 return BAD_VALUE;
748 }
749 if (haveFallback) {
Steve Block3762c312012-01-06 19:20:56 +0000750 ALOGE("%s: Cannot combine multiple fallback key codes.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800751 mTokenizer->getLocation().string());
752 return BAD_VALUE;
753 }
754 behavior.fallbackKeyCode = keyCode;
755 haveFallback = true;
756 } else {
Steve Block3762c312012-01-06 19:20:56 +0000757 ALOGE("%s: Expected a key behavior after ':'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800758 mTokenizer->getLocation().string());
759 return BAD_VALUE;
760 }
761 }
762
763 mTokenizer->skipDelimiters(WHITESPACE);
764 } while (!mTokenizer->isEol());
765
766 // Add the behavior.
767 Key* key = mMap->mKeys.valueFor(mKeyCode);
768 for (size_t i = 0; i < properties.size(); i++) {
769 const Property& property = properties.itemAt(i);
770 switch (property.property) {
771 case PROPERTY_LABEL:
772 if (key->label) {
Steve Block3762c312012-01-06 19:20:56 +0000773 ALOGE("%s: Duplicate label for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800774 mTokenizer->getLocation().string());
775 return BAD_VALUE;
776 }
777 key->label = behavior.character;
778#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000779 ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800780#endif
781 break;
782 case PROPERTY_NUMBER:
783 if (key->number) {
Steve Block3762c312012-01-06 19:20:56 +0000784 ALOGE("%s: Duplicate number for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800785 mTokenizer->getLocation().string());
786 return BAD_VALUE;
787 }
788 key->number = behavior.character;
789#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000790 ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800791#endif
792 break;
793 case PROPERTY_META: {
794 for (Behavior* b = key->firstBehavior; b; b = b->next) {
795 if (b->metaState == property.metaState) {
Steve Block3762c312012-01-06 19:20:56 +0000796 ALOGE("%s: Duplicate key behavior for modifier.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800797 mTokenizer->getLocation().string());
798 return BAD_VALUE;
799 }
800 }
801 Behavior* newBehavior = new Behavior(behavior);
802 newBehavior->metaState = property.metaState;
803 newBehavior->next = key->firstBehavior;
804 key->firstBehavior = newBehavior;
805#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000806 ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800807 newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
808#endif
809 break;
810 }
811 }
812 }
813 return NO_ERROR;
814}
815
816status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
817 if (token == "base") {
818 *outMetaState = 0;
819 return NO_ERROR;
820 }
821
822 int32_t combinedMeta = 0;
823
824 const char* str = token.string();
825 const char* start = str;
826 for (const char* cur = str; ; cur++) {
827 char ch = *cur;
828 if (ch == '+' || ch == '\0') {
829 size_t len = cur - start;
830 int32_t metaState = 0;
831 for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
832 if (strlen(modifiers[i].label) == len
833 && strncmp(modifiers[i].label, start, len) == 0) {
834 metaState = modifiers[i].metaState;
835 break;
836 }
837 }
838 if (!metaState) {
839 return BAD_VALUE;
840 }
841 if (combinedMeta & metaState) {
Steve Block3762c312012-01-06 19:20:56 +0000842 ALOGE("%s: Duplicate modifier combination '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800843 mTokenizer->getLocation().string(), token.string());
844 return BAD_VALUE;
845 }
846
847 combinedMeta |= metaState;
Jeff Brown061cf752010-11-18 20:52:43 -0800848 start = cur + 1;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800849
850 if (ch == '\0') {
851 break;
852 }
853 }
854 }
855 *outMetaState = combinedMeta;
856 return NO_ERROR;
857}
858
859status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
860 char ch = mTokenizer->nextChar();
861 if (ch != '\'') {
862 goto Error;
863 }
864
865 ch = mTokenizer->nextChar();
866 if (ch == '\\') {
867 // Escape sequence.
868 ch = mTokenizer->nextChar();
869 if (ch == 'n') {
870 *outCharacter = '\n';
871 } else if (ch == 't') {
872 *outCharacter = '\t';
873 } else if (ch == '\\') {
874 *outCharacter = '\\';
875 } else if (ch == '\'') {
876 *outCharacter = '\'';
877 } else if (ch == '"') {
878 *outCharacter = '"';
879 } else if (ch == 'u') {
880 *outCharacter = 0;
881 for (int i = 0; i < 4; i++) {
882 ch = mTokenizer->nextChar();
883 int digit;
884 if (ch >= '0' && ch <= '9') {
885 digit = ch - '0';
886 } else if (ch >= 'A' && ch <= 'F') {
887 digit = ch - 'A' + 10;
888 } else if (ch >= 'a' && ch <= 'f') {
889 digit = ch - 'a' + 10;
890 } else {
891 goto Error;
892 }
893 *outCharacter = (*outCharacter << 4) | digit;
894 }
895 } else {
896 goto Error;
897 }
898 } else if (ch >= 32 && ch <= 126 && ch != '\'') {
899 // ASCII literal character.
900 *outCharacter = ch;
901 } else {
902 goto Error;
903 }
904
905 ch = mTokenizer->nextChar();
906 if (ch != '\'') {
907 goto Error;
908 }
909
910 // Ensure that we consumed the entire token.
911 if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
912 return NO_ERROR;
913 }
914
915Error:
Steve Block3762c312012-01-06 19:20:56 +0000916 ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800917 return BAD_VALUE;
918}
919
920} // namespace android