blob: de76e255202cdf623f7eddf578e2d2af10acca66 [file] [log] [blame]
Jeff Browna3477c82010-11-10 16:03:06 -08001/*
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 <ui/Keyboard.h>
24#include <ui/KeycodeLabels.h>
25#include <utils/Errors.h>
26#include <utils/Log.h>
27#include <cutils/properties.h>
28
29namespace android {
30
31static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
32 if (keyMapInfo.keyMapName.isEmpty()) {
33 keyMapInfo.keyMapName.setTo(keyMapName);
34 keyMapInfo.isDefaultKeyMap = defaultKeyMap;
35 }
36}
37
38static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
39 const char* root = getenv("ANDROID_ROOT");
40
41 // TODO Consider also looking somewhere in a writeable partition like /data for a
42 // custom keymap supplied by the user for this device.
43 bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty();
44 if (!haveKeyLayout) {
45 keyMapInfo.keyLayoutFile.setTo(root);
46 keyMapInfo.keyLayoutFile.append("/usr/keylayout/");
47 keyMapInfo.keyLayoutFile.append(keyMapName);
48 keyMapInfo.keyLayoutFile.append(".kl");
49 if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) {
50 keyMapInfo.keyLayoutFile.clear();
51 } else {
52 haveKeyLayout = true;
53 }
54 }
55
56 bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty();
57 if (!haveKeyCharacterMap) {
58 keyMapInfo.keyCharacterMapFile.setTo(root);
59 keyMapInfo.keyCharacterMapFile.append("/usr/keychars/");
60 keyMapInfo.keyCharacterMapFile.append(keyMapName);
61 keyMapInfo.keyCharacterMapFile.append(".kcm");
62 if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) {
63 keyMapInfo.keyCharacterMapFile.clear();
64 } else {
65 haveKeyCharacterMap = true;
66 }
67 }
68
69 if (haveKeyLayout || haveKeyCharacterMap) {
70 selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap);
71 }
72 return haveKeyLayout && haveKeyCharacterMap;
73}
74
75status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) {
76 // As an initial key map name, try using the device name.
77 String8 keyMapName(deviceName);
78 char* p = keyMapName.lockBuffer(keyMapName.size());
79 while (*p) {
80 if (*p == ' ') *p = '_';
81 p++;
82 }
83 keyMapName.unlockBuffer();
84
85 if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK;
86
87 // TODO Consider allowing the user to configure a specific key map somehow.
88
89 // Try the Generic key map.
90 // TODO Apply some additional heuristics here to figure out what kind of
91 // generic key map to use (US English, etc.).
92 keyMapName.setTo("Generic");
93 if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK;
94
95 // Give up!
96 keyMapName.setTo("unknown");
97 selectKeyMap(outKeyMapInfo, keyMapName, true);
98 LOGE("Could not determine key map for device '%s'.", deviceName.string());
99 return NAME_NOT_FOUND;
100}
101
102void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
103 const KeyMapInfo& keyMapInfo) {
104 char propName[PROPERTY_KEY_MAX];
105 snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
106 property_set(propName, deviceName.string());
107 snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
108 property_set(propName, keyMapInfo.keyMapName.string());
109 snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
110 property_set(propName, keyMapInfo.keyLayoutFile.string());
111 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
112 property_set(propName, keyMapInfo.keyCharacterMapFile.string());
113}
114
115void clearKeyboardProperties(int32_t deviceId) {
116 char propName[PROPERTY_KEY_MAX];
117 snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
118 property_set(propName, "");
119 snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
120 property_set(propName, "");
121 snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
122 property_set(propName, "");
123 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
124 property_set(propName, "");
125}
126
127status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
128 char propName[PROPERTY_KEY_MAX];
129 char fn[PROPERTY_VALUE_MAX];
130 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
131 if (property_get(propName, fn, "") > 0) {
132 outKeyCharacterMapFile.setTo(fn);
133 return OK;
134 }
135
136 const char* root = getenv("ANDROID_ROOT");
137 char path[PATH_MAX];
138 if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
139 snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root);
140 if (!access(path, R_OK)) {
141 outKeyCharacterMapFile.setTo(path);
142 return OK;
143 }
144 }
145
146 snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root);
147 if (!access(path, R_OK)) {
148 outKeyCharacterMapFile.setTo(path);
149 return OK;
150 }
151
152 LOGE("Can't find any key character map files (also tried %s)", path);
153 return NAME_NOT_FOUND;
154}
155
156static int lookupLabel(const char* literal, const KeycodeLabel *list) {
157 while (list->literal) {
158 if (strcmp(literal, list->literal) == 0) {
159 return list->value;
160 }
161 list++;
162 }
163 return list->value;
164}
165
166int32_t getKeyCodeByLabel(const char* label) {
167 return int32_t(lookupLabel(label, KEYCODES));
168}
169
170uint32_t getKeyFlagByLabel(const char* label) {
171 return uint32_t(lookupLabel(label, FLAGS));
172}
173
174static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
175 int32_t newMetaState;
176 if (down) {
177 newMetaState = oldMetaState | mask;
178 } else {
179 newMetaState = oldMetaState &
180 ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
181 }
182
183 if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
184 newMetaState |= AMETA_ALT_ON;
185 }
186
187 if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
188 newMetaState |= AMETA_SHIFT_ON;
189 }
190
191 if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
192 newMetaState |= AMETA_CTRL_ON;
193 }
194
195 if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
196 newMetaState |= AMETA_META_ON;
197 }
198 return newMetaState;
199}
200
201static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
202 if (down) {
203 return oldMetaState;
204 } else {
205 return oldMetaState ^ mask;
206 }
207}
208
209int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
210 int32_t mask;
211 switch (keyCode) {
212 case AKEYCODE_ALT_LEFT:
213 return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
214 case AKEYCODE_ALT_RIGHT:
215 return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
216 case AKEYCODE_SHIFT_LEFT:
217 return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
218 case AKEYCODE_SHIFT_RIGHT:
219 return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
220 case AKEYCODE_SYM:
221 return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
222 case AKEYCODE_FUNCTION:
223 return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
224 case AKEYCODE_CTRL_LEFT:
225 return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
226 case AKEYCODE_CTRL_RIGHT:
227 return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
228 case AKEYCODE_META_LEFT:
229 return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
230 case AKEYCODE_META_RIGHT:
231 return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
232 case AKEYCODE_CAPS_LOCK:
233 return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
234 case AKEYCODE_NUM_LOCK:
235 return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
236 case AKEYCODE_SCROLL_LOCK:
237 return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
238 default:
239 return oldMetaState;
240 }
241}
242
243
244} // namespace android