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 35 KeyMap::KeyMap() : 36 keyLayoutMap(NULL), keyCharacterMap(NULL) { 37 } 38 39 KeyMap::~KeyMap() { 40 delete keyLayoutMap; 41 delete keyCharacterMap; 42 } 43 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 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 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 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 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 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 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 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 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 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 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 240 int32_t getKeyCodeByLabel(const char* label) { 241 return int32_t(lookupValueByLabel(label, KEYCODES)); 242 } 243 244 uint32_t getKeyFlagByLabel(const char* label) { 245 return uint32_t(lookupValueByLabel(label, FLAGS)); 246 } 247 248 int32_t getAxisByLabel(const char* label) { 249 return int32_t(lookupValueByLabel(label, AXES)); 250 } 251 252 const char* getAxisLabel(int32_t axisId) { 253 return lookupLabelByValue(axisId, AXES); 254 } 255 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 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 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 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