diff options
Diffstat (limited to 'libs/ui/KeyCharacterMap.cpp')
-rw-r--r-- | libs/ui/KeyCharacterMap.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp new file mode 100644 index 0000000..e891181 --- /dev/null +++ b/libs/ui/KeyCharacterMap.cpp @@ -0,0 +1,263 @@ +#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; +} |