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