Home | History | Annotate | Download | only in androidfw
      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 <androidfw/Keyboard.h>
     24 #include <androidfw/KeycodeLabels.h>
     25 #include <androidfw/KeyLayoutMap.h>
     26 #include <androidfw/KeyCharacterMap.h>
     27 #include <androidfw/InputDevice.h>
     28 #include <utils/Errors.h>
     29 #include <utils/Log.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
    299