Home | History | Annotate | Download | only in ui
      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