blob: 870a45c169aab02f067163f6bd38ae297ceefa9a [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001#define LOG_TAG "KeyCharacterMap"
2
3#include <ui/KeyCharacterMap.h>
4#include <cutils/properties.h>
5
6#include <utils/Log.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <stdlib.h>
10#include <fcntl.h>
11#include <limits.h>
12#include <string.h>
13
14struct Header
15{
16 char magic[8];
17 unsigned int endian;
18 unsigned int version;
19 unsigned int keycount;
20 unsigned char kbdtype;
21 char padding[11];
22};
23
24KeyCharacterMap::KeyCharacterMap()
25{
26}
27
28KeyCharacterMap::~KeyCharacterMap()
29{
30 free(m_keys);
31}
32
33unsigned short
34KeyCharacterMap::get(int keycode, int meta)
35{
36 Key* k = find_key(keycode);
37 if (k != NULL) {
38 return k->data[meta & META_MASK];
39 }
40 return 0;
41}
42
43unsigned short
44KeyCharacterMap::getNumber(int keycode)
45{
46 Key* k = find_key(keycode);
47 if (k != NULL) {
48 return k->number;
49 }
50 return 0;
51}
52
53unsigned short
54KeyCharacterMap::getMatch(int keycode, const unsigned short* chars,
55 int charsize, uint32_t modifiers)
56{
57 Key* k = find_key(keycode);
58 modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it
59 if (k != NULL) {
60 const uint16_t* data = k->data;
61 for (int j=0; j<charsize; j++) {
62 uint16_t c = chars[j];
63 for (int i=0; i<(META_MASK + 1); i++) {
64 if ((modifiers == 0) || ((modifiers & i) != 0)) {
65 if (c == data[i]) {
66 return c;
67 }
68 }
69 }
70 }
71 }
72 return 0;
73}
74
75unsigned short
76KeyCharacterMap::getDisplayLabel(int keycode)
77{
78 Key* k = find_key(keycode);
79 if (k != NULL) {
80 return k->display_label;
81 }
82 return 0;
83}
84
85bool
86KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel,
87 unsigned short *number, unsigned short* results)
88{
89 Key* k = find_key(keycode);
90 if (k != NULL) {
91 memcpy(results, k->data, sizeof(short)*(META_MASK + 1));
92 *number = k->number;
93 *displayLabel = k->display_label;
94 return true;
95 } else {
96 return false;
97 }
98}
99
100bool
101KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods)
102{
103 uint32_t N = m_keyCount;
104 for (int j=0; j<(META_MASK + 1); j++) {
105 Key const* keys = m_keys;
106 for (uint32_t i=0; i<N; i++) {
107 if (keys->data[j] == c) {
108 *key = keys->keycode;
109 *mods = j;
110 return true;
111 }
112 keys++;
113 }
114 }
115 return false;
116}
117
118bool
119KeyCharacterMap::getEvents(uint16_t* chars, size_t len,
120 Vector<int32_t>* keys, Vector<uint32_t>* modifiers)
121{
122 for (size_t i=0; i<len; i++) {
123 uint32_t k, mods;
124 if (find_char(chars[i], &k, &mods)) {
125 keys->add(k);
126 modifiers->add(mods);
127 } else {
128 return false;
129 }
130 }
131 return true;
132}
133
134KeyCharacterMap::Key*
135KeyCharacterMap::find_key(int keycode)
136{
137 Key* keys = m_keys;
138 int low = 0;
139 int high = m_keyCount - 1;
140 int mid;
141 int n;
142 while (low <= high) {
143 mid = (low + high) / 2;
144 n = keys[mid].keycode;
145 if (keycode < n) {
146 high = mid - 1;
147 } else if (keycode > n) {
148 low = mid + 1;
149 } else {
150 return keys + mid;
151 }
152 }
153 return NULL;
154}
155
156KeyCharacterMap*
157KeyCharacterMap::load(int id)
158{
Jeff Brown6a817e22010-09-12 17:55:08 -0700159 KeyCharacterMap* map;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800160 char path[PATH_MAX];
161 char propName[100];
162 char dev[PROPERTY_VALUE_MAX];
Jeff Brown6a817e22010-09-12 17:55:08 -0700163 char fn[PROPERTY_VALUE_MAX];
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800164 int err;
Jeff Brown6a817e22010-09-12 17:55:08 -0700165
166 // Check whether the EventHub has set a key character map filename for us already.
167 sprintf(propName, "hw.keyboards.%u.kcmfile", id);
168 err = property_get(propName, fn, "");
169 if (err > 0) {
170 map = try_file(fn);
171 if (map) {
172 return map;
173 }
174 LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn);
175 }
176
177 // Try using the device name.
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800178 const char* root = getenv("ANDROID_ROOT");
179
180 sprintf(propName, "hw.keyboards.%u.devname", id);
181 err = property_get(propName, dev, "");
182 if (err > 0) {
183 // replace all the spaces with underscores
Jeff Brown6a817e22010-09-12 17:55:08 -0700184 strcpy(fn, dev);
185 for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' '))
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800186 *p = '_';
Jeff Brown6a817e22010-09-12 17:55:08 -0700187 snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn);
188 map = try_file(path);
189 if (map) {
190 return map;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800191 }
192 LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
193 } else {
194 LOGW("No keyboard for id %d", id);
195 }
196
197 snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
Jeff Brown6a817e22010-09-12 17:55:08 -0700198 map = try_file(path);
199 if (map) {
200 LOGW("Using default keymap: %s", path);
201 return map;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800202 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800203
Jeff Brown6a817e22010-09-12 17:55:08 -0700204 LOGE("Can't find any keycharmaps (also tried %s)", path);
205 return NULL;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800206}
207
208KeyCharacterMap*
209KeyCharacterMap::try_file(const char* filename)
210{
211 KeyCharacterMap* rv = NULL;
212 Key* keys;
213 int fd;
214 off_t filesize;
215 Header header;
216 int err;
217
218 fd = open(filename, O_RDONLY);
219 if (fd == -1) {
220 LOGW("Can't open keycharmap file");
221 return NULL;
222 }
223
224 filesize = lseek(fd, 0, SEEK_END);
225 lseek(fd, 0, SEEK_SET);
226
227 // validate the header
228 if (filesize <= (off_t)sizeof(header)) {
229 LOGW("Bad keycharmap - filesize=%d\n", (int)filesize);
230 goto cleanup1;
231 }
232
233 err = read(fd, &header, sizeof(header));
234 if (err == -1) {
235 LOGW("Error reading keycharmap file");
236 goto cleanup1;
237 }
238
239 if (0 != memcmp(header.magic, "keychar", 8)) {
240 LOGW("Bad keycharmap magic token");
241 goto cleanup1;
242 }
243 if (header.endian != 0x12345678) {
244 LOGW("Bad keycharmap endians");
245 goto cleanup1;
246 }
247 if ((header.version & 0xff) != 2) {
248 LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version);
249 goto cleanup1;
250 }
251 if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) {
252 LOGW("Bad keycharmap file size\n");
253 goto cleanup1;
254 }
255
256 // read the key data
257 keys = (Key*)malloc(sizeof(Key)*header.keycount);
258 err = read(fd, keys, sizeof(Key)*header.keycount);
259 if (err == -1) {
260 LOGW("Error reading keycharmap file");
261 free(keys);
262 goto cleanup1;
263 }
264
265 // return the object
266 rv = new KeyCharacterMap;
267 rv->m_keyCount = header.keycount;
268 rv->m_keys = keys;
269 rv->m_type = header.kbdtype;
270
271cleanup1:
272 close(fd);
273
274 return rv;
275}