|
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 #include "cutils_log.h" |
|
19 |
|
20 #include <stdlib.h> |
|
21 #include <unistd.h> |
|
22 #include <limits.h> |
|
23 |
|
24 #include "Keyboard.h" |
|
25 #include "KeycodeLabels.h" |
|
26 #include "KeyLayoutMap.h" |
|
27 #include "KeyCharacterMap.h" |
|
28 #include "InputDevice.h" |
|
29 #include <utils/Errors.h> |
|
30 #include <cutils/properties.h> |
|
31 |
|
32 namespace android { |
|
33 |
|
34 // --- KeyMap --- |
|
35 |
|
36 KeyMap::KeyMap() { |
|
37 } |
|
38 |
|
39 KeyMap::~KeyMap() { |
|
40 } |
|
41 |
|
42 status_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 |
|
96 bool 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 |
|
107 status_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 |
|
124 status_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 |
|
142 String8 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 |
|
152 bool 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 |
|
171 static 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 |
|
181 static 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 |
|
191 int32_t getKeyCodeByLabel(const char* label) { |
|
192 return int32_t(lookupValueByLabel(label, KEYCODES)); |
|
193 } |
|
194 |
|
195 uint32_t getKeyFlagByLabel(const char* label) { |
|
196 return uint32_t(lookupValueByLabel(label, FLAGS)); |
|
197 } |
|
198 |
|
199 int32_t getAxisByLabel(const char* label) { |
|
200 return int32_t(lookupValueByLabel(label, AXES)); |
|
201 } |
|
202 |
|
203 const char* getAxisLabel(int32_t axisId) { |
|
204 return lookupLabelByValue(axisId, AXES); |
|
205 } |
|
206 |
|
207 static 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 |
|
234 static 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 |
|
242 int32_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 |
|
276 bool 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 |