| #define LOG_TAG "KeyCharacterMap" |
| |
| #include <ui/KeyCharacterMap.h> |
| #include <cutils/properties.h> |
| |
| #include <utils/Log.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <string.h> |
| |
| struct Header |
| { |
| char magic[8]; |
| unsigned int endian; |
| unsigned int version; |
| unsigned int keycount; |
| unsigned char kbdtype; |
| char padding[11]; |
| }; |
| |
| KeyCharacterMap::KeyCharacterMap() |
| { |
| } |
| |
| KeyCharacterMap::~KeyCharacterMap() |
| { |
| free(m_keys); |
| } |
| |
| unsigned short |
| KeyCharacterMap::get(int keycode, int meta) |
| { |
| Key* k = find_key(keycode); |
| if (k != NULL) { |
| return k->data[meta & META_MASK]; |
| } |
| return 0; |
| } |
| |
| unsigned short |
| KeyCharacterMap::getNumber(int keycode) |
| { |
| Key* k = find_key(keycode); |
| if (k != NULL) { |
| return k->number; |
| } |
| return 0; |
| } |
| |
| unsigned short |
| KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, |
| int charsize, uint32_t modifiers) |
| { |
| Key* k = find_key(keycode); |
| modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it |
| if (k != NULL) { |
| const uint16_t* data = k->data; |
| for (int j=0; j<charsize; j++) { |
| uint16_t c = chars[j]; |
| for (int i=0; i<(META_MASK + 1); i++) { |
| if ((modifiers == 0) || ((modifiers & i) != 0)) { |
| if (c == data[i]) { |
| return c; |
| } |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| unsigned short |
| KeyCharacterMap::getDisplayLabel(int keycode) |
| { |
| Key* k = find_key(keycode); |
| if (k != NULL) { |
| return k->display_label; |
| } |
| return 0; |
| } |
| |
| bool |
| KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, |
| unsigned short *number, unsigned short* results) |
| { |
| Key* k = find_key(keycode); |
| if (k != NULL) { |
| memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); |
| *number = k->number; |
| *displayLabel = k->display_label; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool |
| KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) |
| { |
| uint32_t N = m_keyCount; |
| for (int j=0; j<(META_MASK + 1); j++) { |
| Key const* keys = m_keys; |
| for (uint32_t i=0; i<N; i++) { |
| if (keys->data[j] == c) { |
| *key = keys->keycode; |
| *mods = j; |
| return true; |
| } |
| keys++; |
| } |
| } |
| return false; |
| } |
| |
| bool |
| KeyCharacterMap::getEvents(uint16_t* chars, size_t len, |
| Vector<int32_t>* keys, Vector<uint32_t>* modifiers) |
| { |
| for (size_t i=0; i<len; i++) { |
| uint32_t k, mods; |
| if (find_char(chars[i], &k, &mods)) { |
| keys->add(k); |
| modifiers->add(mods); |
| } else { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| KeyCharacterMap::Key* |
| KeyCharacterMap::find_key(int keycode) |
| { |
| Key* keys = m_keys; |
| int low = 0; |
| int high = m_keyCount - 1; |
| int mid; |
| int n; |
| while (low <= high) { |
| mid = (low + high) / 2; |
| n = keys[mid].keycode; |
| if (keycode < n) { |
| high = mid - 1; |
| } else if (keycode > n) { |
| low = mid + 1; |
| } else { |
| return keys + mid; |
| } |
| } |
| return NULL; |
| } |
| |
| KeyCharacterMap* |
| KeyCharacterMap::load(int id) |
| { |
| KeyCharacterMap* rv = NULL; |
| char path[PATH_MAX]; |
| char propName[100]; |
| char dev[PROPERTY_VALUE_MAX]; |
| char tmpfn[PROPERTY_VALUE_MAX]; |
| int err; |
| const char* root = getenv("ANDROID_ROOT"); |
| |
| sprintf(propName, "hw.keyboards.%u.devname", id); |
| err = property_get(propName, dev, ""); |
| if (err > 0) { |
| // replace all the spaces with underscores |
| strcpy(tmpfn, dev); |
| for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) |
| *p = '_'; |
| snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn); |
| //LOGD("load: dev='%s' path='%s'\n", dev, path); |
| rv = try_file(path); |
| if (rv != NULL) { |
| return rv; |
| } |
| LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); |
| } else { |
| LOGW("No keyboard for id %d", id); |
| } |
| |
| snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); |
| rv = try_file(path); |
| if (rv == NULL) { |
| LOGE("Can't find any keycharmaps (also tried %s)", path); |
| return NULL; |
| } |
| LOGW("Using default keymap: %s", path); |
| |
| return rv; |
| } |
| |
| KeyCharacterMap* |
| KeyCharacterMap::try_file(const char* filename) |
| { |
| KeyCharacterMap* rv = NULL; |
| Key* keys; |
| int fd; |
| off_t filesize; |
| Header header; |
| int err; |
| |
| fd = open(filename, O_RDONLY); |
| if (fd == -1) { |
| LOGW("Can't open keycharmap file"); |
| return NULL; |
| } |
| |
| filesize = lseek(fd, 0, SEEK_END); |
| lseek(fd, 0, SEEK_SET); |
| |
| // validate the header |
| if (filesize <= (off_t)sizeof(header)) { |
| LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); |
| goto cleanup1; |
| } |
| |
| err = read(fd, &header, sizeof(header)); |
| if (err == -1) { |
| LOGW("Error reading keycharmap file"); |
| goto cleanup1; |
| } |
| |
| if (0 != memcmp(header.magic, "keychar", 8)) { |
| LOGW("Bad keycharmap magic token"); |
| goto cleanup1; |
| } |
| if (header.endian != 0x12345678) { |
| LOGW("Bad keycharmap endians"); |
| goto cleanup1; |
| } |
| if ((header.version & 0xff) != 2) { |
| LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); |
| goto cleanup1; |
| } |
| if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { |
| LOGW("Bad keycharmap file size\n"); |
| goto cleanup1; |
| } |
| |
| // read the key data |
| keys = (Key*)malloc(sizeof(Key)*header.keycount); |
| err = read(fd, keys, sizeof(Key)*header.keycount); |
| if (err == -1) { |
| LOGW("Error reading keycharmap file"); |
| free(keys); |
| goto cleanup1; |
| } |
| |
| // return the object |
| rv = new KeyCharacterMap; |
| rv->m_keyCount = header.keycount; |
| rv->m_keys = keys; |
| rv->m_type = header.kbdtype; |
| |
| cleanup1: |
| close(fd); |
| |
| return rv; |
| } |