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 
KeyMap()36 KeyMap::KeyMap() {
37 }
38 
~KeyMap()39 KeyMap::~KeyMap() {
40 }
41 
load(const InputDeviceIdentifier & deviceIdenfifier,const PropertyMap * deviceConfiguration)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 
probeKeyMap(const InputDeviceIdentifier & deviceIdentifier,const String8 & keyMapName)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 
loadKeyLayout(const InputDeviceIdentifier & deviceIdentifier,const String8 & name)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 
loadKeyCharacterMap(const InputDeviceIdentifier & deviceIdentifier,const String8 & name)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 
getPath(const InputDeviceIdentifier & deviceIdentifier,const String8 & name,InputDeviceConfigurationFileType type)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 
isEligibleBuiltInKeyboard(const InputDeviceIdentifier & deviceIdentifier,const PropertyMap * deviceConfiguration,const KeyMap * keyMap)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 
lookupValueByLabel(const char * literal,const KeycodeLabel * list)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 
lookupLabelByValue(int value,const KeycodeLabel * list)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 
getKeyCodeByLabel(const char * label)191 int32_t getKeyCodeByLabel(const char* label) {
192     return int32_t(lookupValueByLabel(label, KEYCODES));
193 }
194 
getKeyFlagByLabel(const char * label)195 uint32_t getKeyFlagByLabel(const char* label) {
196     return uint32_t(lookupValueByLabel(label, FLAGS));
197 }
198 
getAxisByLabel(const char * label)199 int32_t getAxisByLabel(const char* label) {
200     return int32_t(lookupValueByLabel(label, AXES));
201 }
202 
getAxisLabel(int32_t axisId)203 const char* getAxisLabel(int32_t axisId) {
204     return lookupLabelByValue(axisId, AXES);
205 }
206 
getLedByLabel(const char * label)207 int32_t getLedByLabel(const char* label) {
208     return int32_t(lookupValueByLabel(label, LEDS));
209 }
setEphemeralMetaState(int32_t mask,bool down,int32_t oldMetaState)210 static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
211     int32_t newMetaState;
212     if (down) {
213         newMetaState = oldMetaState | mask;
214     } else {
215         newMetaState = oldMetaState &
216                 ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
217     }
218 
219     if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
220         newMetaState |= AMETA_ALT_ON;
221     }
222 
223     if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
224         newMetaState |= AMETA_SHIFT_ON;
225     }
226 
227     if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
228         newMetaState |= AMETA_CTRL_ON;
229     }
230 
231     if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
232         newMetaState |= AMETA_META_ON;
233     }
234     return newMetaState;
235 }
236 
toggleLockedMetaState(int32_t mask,bool down,int32_t oldMetaState)237 static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
238     if (down) {
239         return oldMetaState;
240     } else {
241         return oldMetaState ^ mask;
242     }
243 }
244 
updateMetaState(int32_t keyCode,bool down,int32_t oldMetaState)245 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
246     switch (keyCode) {
247     case AKEYCODE_ALT_LEFT:
248         return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
249     case AKEYCODE_ALT_RIGHT:
250         return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
251     case AKEYCODE_SHIFT_LEFT:
252         return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
253     case AKEYCODE_SHIFT_RIGHT:
254         return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
255     case AKEYCODE_SYM:
256         return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
257     case AKEYCODE_FUNCTION:
258         return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
259     case AKEYCODE_CTRL_LEFT:
260         return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
261     case AKEYCODE_CTRL_RIGHT:
262         return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
263     case AKEYCODE_META_LEFT:
264         return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
265     case AKEYCODE_META_RIGHT:
266         return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
267     case AKEYCODE_CAPS_LOCK:
268         return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
269     case AKEYCODE_NUM_LOCK:
270         return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
271     case AKEYCODE_SCROLL_LOCK:
272         return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
273     default:
274         return oldMetaState;
275     }
276 }
277 
isMetaKey(int32_t keyCode)278 bool isMetaKey(int32_t keyCode) {
279     switch (keyCode) {
280     case AKEYCODE_ALT_LEFT:
281     case AKEYCODE_ALT_RIGHT:
282     case AKEYCODE_SHIFT_LEFT:
283     case AKEYCODE_SHIFT_RIGHT:
284     case AKEYCODE_SYM:
285     case AKEYCODE_FUNCTION:
286     case AKEYCODE_CTRL_LEFT:
287     case AKEYCODE_CTRL_RIGHT:
288     case AKEYCODE_META_LEFT:
289     case AKEYCODE_META_RIGHT:
290     case AKEYCODE_CAPS_LOCK:
291     case AKEYCODE_NUM_LOCK:
292     case AKEYCODE_SCROLL_LOCK:
293         return true;
294     default:
295         return false;
296     }
297 }
298 
299 
300 } // namespace android
301