1/*
2 * Copyright (C) 2010 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 "Keyboard"
18
19#include <stdlib.h>
20#include <unistd.h>
21#include <limits.h>
22
23#include <androidfw/Keyboard.h>
24#include <androidfw/KeycodeLabels.h>
25#include <androidfw/KeyLayoutMap.h>
26#include <androidfw/KeyCharacterMap.h>
27#include <androidfw/InputDevice.h>
28#include <utils/Errors.h>
29#include <utils/Log.h>
30#include <cutils/properties.h>
31
32namespace android {
33
34// --- KeyMap ---
35
36KeyMap::KeyMap() {
37}
38
39KeyMap::~KeyMap() {
40}
41
42status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
43        const PropertyMap* deviceConfiguration) {
44    // Use the configured key layout if available.
45    if (deviceConfiguration) {
46        String8 keyLayoutName;
47        if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
48                keyLayoutName)) {
49            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
50            if (status == NAME_NOT_FOUND) {
51                ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
52                        "it was not found.",
53                        deviceIdenfifier.name.string(), keyLayoutName.string());
54            }
55        }
56
57        String8 keyCharacterMapName;
58        if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
59                keyCharacterMapName)) {
60            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
61            if (status == NAME_NOT_FOUND) {
62                ALOGE("Configuration for keyboard device '%s' requested keyboard character "
63                        "map '%s' but it was not found.",
64                        deviceIdenfifier.name.string(), keyLayoutName.string());
65            }
66        }
67
68        if (isComplete()) {
69            return OK;
70        }
71    }
72
73    // Try searching by device identifier.
74    if (probeKeyMap(deviceIdenfifier, String8::empty())) {
75        return OK;
76    }
77
78    // Fall back on the Generic key map.
79    // TODO Apply some additional heuristics here to figure out what kind of
80    //      generic key map to use (US English, etc.) for typical external keyboards.
81    if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
82        return OK;
83    }
84
85    // Try the Virtual key map as a last resort.
86    if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
87        return OK;
88    }
89
90    // Give up!
91    ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
92            deviceIdenfifier.name.string());
93    return NAME_NOT_FOUND;
94}
95
96bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
97        const String8& keyMapName) {
98    if (!haveKeyLayout()) {
99        loadKeyLayout(deviceIdentifier, keyMapName);
100    }
101    if (!haveKeyCharacterMap()) {
102        loadKeyCharacterMap(deviceIdentifier, keyMapName);
103    }
104    return isComplete();
105}
106
107status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
108        const String8& name) {
109    String8 path(getPath(deviceIdentifier, name,
110            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
111    if (path.isEmpty()) {
112        return NAME_NOT_FOUND;
113    }
114
115    status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
116    if (status) {
117        return status;
118    }
119
120    keyLayoutFile.setTo(path);
121    return OK;
122}
123
124status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
125        const String8& name) {
126    String8 path(getPath(deviceIdentifier, name,
127            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
128    if (path.isEmpty()) {
129        return NAME_NOT_FOUND;
130    }
131
132    status_t status = KeyCharacterMap::load(path,
133            KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
134    if (status) {
135        return status;
136    }
137
138    keyCharacterMapFile.setTo(path);
139    return OK;
140}
141
142String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
143        const String8& name, InputDeviceConfigurationFileType type) {
144    return name.isEmpty()
145            ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
146            : getInputDeviceConfigurationFilePathByName(name, type);
147}
148
149
150// --- Global functions ---
151
152bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
153        const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
154    if (!keyMap->haveKeyCharacterMap()
155            || keyMap->keyCharacterMap->getKeyboardType()
156                    == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
157        return false;
158    }
159
160    if (deviceConfiguration) {
161        bool builtIn = false;
162        if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
163                && builtIn) {
164            return true;
165        }
166    }
167
168    return strstr(deviceIdentifier.name.string(), "-keypad");
169}
170
171static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
172    while (list->literal) {
173        if (strcmp(literal, list->literal) == 0) {
174            return list->value;
175        }
176        list++;
177    }
178    return list->value;
179}
180
181static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
182    while (list->literal) {
183        if (list->value == value) {
184            return list->literal;
185        }
186        list++;
187    }
188    return NULL;
189}
190
191int32_t getKeyCodeByLabel(const char* label) {
192    return int32_t(lookupValueByLabel(label, KEYCODES));
193}
194
195uint32_t getKeyFlagByLabel(const char* label) {
196    return uint32_t(lookupValueByLabel(label, FLAGS));
197}
198
199int32_t getAxisByLabel(const char* label) {
200    return int32_t(lookupValueByLabel(label, AXES));
201}
202
203const char* getAxisLabel(int32_t axisId) {
204    return lookupLabelByValue(axisId, AXES);
205}
206
207static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
208    int32_t newMetaState;
209    if (down) {
210        newMetaState = oldMetaState | mask;
211    } else {
212        newMetaState = oldMetaState &
213                ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
214    }
215
216    if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
217        newMetaState |= AMETA_ALT_ON;
218    }
219
220    if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
221        newMetaState |= AMETA_SHIFT_ON;
222    }
223
224    if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
225        newMetaState |= AMETA_CTRL_ON;
226    }
227
228    if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
229        newMetaState |= AMETA_META_ON;
230    }
231    return newMetaState;
232}
233
234static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
235    if (down) {
236        return oldMetaState;
237    } else {
238        return oldMetaState ^ mask;
239    }
240}
241
242int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
243    int32_t mask;
244    switch (keyCode) {
245    case AKEYCODE_ALT_LEFT:
246        return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
247    case AKEYCODE_ALT_RIGHT:
248        return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
249    case AKEYCODE_SHIFT_LEFT:
250        return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
251    case AKEYCODE_SHIFT_RIGHT:
252        return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
253    case AKEYCODE_SYM:
254        return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
255    case AKEYCODE_FUNCTION:
256        return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
257    case AKEYCODE_CTRL_LEFT:
258        return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
259    case AKEYCODE_CTRL_RIGHT:
260        return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
261    case AKEYCODE_META_LEFT:
262        return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
263    case AKEYCODE_META_RIGHT:
264        return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
265    case AKEYCODE_CAPS_LOCK:
266        return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
267    case AKEYCODE_NUM_LOCK:
268        return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
269    case AKEYCODE_SCROLL_LOCK:
270        return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
271    default:
272        return oldMetaState;
273    }
274}
275
276bool isMetaKey(int32_t keyCode) {
277    switch (keyCode) {
278    case AKEYCODE_ALT_LEFT:
279    case AKEYCODE_ALT_RIGHT:
280    case AKEYCODE_SHIFT_LEFT:
281    case AKEYCODE_SHIFT_RIGHT:
282    case AKEYCODE_SYM:
283    case AKEYCODE_FUNCTION:
284    case AKEYCODE_CTRL_LEFT:
285    case AKEYCODE_CTRL_RIGHT:
286    case AKEYCODE_META_LEFT:
287    case AKEYCODE_META_RIGHT:
288    case AKEYCODE_CAPS_LOCK:
289    case AKEYCODE_NUM_LOCK:
290    case AKEYCODE_SCROLL_LOCK:
291        return true;
292    default:
293        return false;
294    }
295}
296
297
298} // namespace android
299