blob: 25a6023a4f7bbec91220edf233a37c6e5b70312e [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2007 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#include <stdio.h>
18#include <stdlib.h>
19#include <fcntl.h>
20#include <dirent.h>
21#include <sys/poll.h>
Christopher Laisac731c82010-11-24 18:49:52 -060022#include <limits.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080023
24#include <linux/input.h>
25
Christopher Laisac731c82010-11-24 18:49:52 -060026#include "../common.h"
27
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080028#include "minui.h"
29
30#define MAX_DEVICES 16
31
Christopher Lais44bd4942010-11-27 13:36:23 -060032#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable"
33#define VIBRATOR_TIME_MS 50
34
Christopher Laisac731c82010-11-24 18:49:52 -060035enum {
36 DOWN_NOT,
37 DOWN_SENT,
38 DOWN_RELEASED,
39};
40
41struct virtualkey {
42 int scancode;
43 int centerx, centery;
44 int width, height;
45};
46
47struct position {
48 int x, y;
49 int synced;
50 struct input_absinfo xi, yi;
51};
52
53struct ev {
54 struct pollfd *fd;
55
56 struct virtualkey *vks;
57 int vk_count;
58
59 struct position p, mt_p;
60 int down;
61};
62
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080063static struct pollfd ev_fds[MAX_DEVICES];
Christopher Laisac731c82010-11-24 18:49:52 -060064static struct ev evs[MAX_DEVICES];
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080065static unsigned ev_count = 0;
66
Christopher Laisac731c82010-11-24 18:49:52 -060067static inline int ABS(int x) {
68 return x<0?-x:x;
69}
70
Christopher Lais44bd4942010-11-27 13:36:23 -060071int vibrate(int timeout_ms)
72{
73 char str[20];
74 int fd;
75 int ret;
76
77 fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
78 if (fd < 0)
79 return -1;
80
81 ret = snprintf(str, sizeof(str), "%d", timeout_ms);
82 ret = write(fd, str, ret);
83 close(fd);
84
85 if (ret < 0)
86 return -1;
87
88 return 0;
89}
90
Christopher Laisac731c82010-11-24 18:49:52 -060091/* Returns empty tokens */
92static char *vk_strtok_r(char *str, const char *delim, char **save_str)
93{
94 if(!str) {
95 if(!*save_str) return NULL;
96 str = (*save_str) + 1;
97 }
98 *save_str = strpbrk(str, delim);
99 if(*save_str) **save_str = '\0';
100 return str;
101}
102
103static int vk_init(struct ev *e)
104{
105 char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
106 char vks[2048], *ts;
107 ssize_t len;
108 int vk_fd;
109 int i;
110
111 e->vk_count = 0;
112
113 len = strlen(vk_path);
114 len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len);
115 if (len <= 0)
116 return -1;
117
118 vk_fd = open(vk_path, O_RDONLY);
119 if (vk_fd < 0)
120 return -1;
121
122 len = read(vk_fd, vks, sizeof(vks)-1);
123 close(vk_fd);
124 if (len <= 0)
125 return -1;
126
127 vks[len] = '\0';
128
129 /* Parse a line like:
130 keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
131 */
132 for (ts = vks, e->vk_count = 1; *ts; ++ts) {
133 if (*ts == ':')
134 ++e->vk_count;
135 }
136
137 if (e->vk_count % 6) {
138 LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
139 }
140 e->vk_count /= 6;
141 if (e->vk_count <= 0)
142 return -1;
143
144 e->down = DOWN_NOT;
145
146 ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
147 ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
148 e->p.synced = 0;
149
150 ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
151 ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
152 e->mt_p.synced = 0;
153
154 e->vks = malloc(sizeof(*e->vks) * e->vk_count);
155
156 for (i = 0; i < e->vk_count; ++i) {
157 char *token[6];
158 int j;
159
160 for (j = 0; j < 6; ++j) {
161 token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
162 }
163
164 if (strcmp(token[0], "0x01") != 0) {
165 /* Java does string compare, so we do too. */
166 LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
167 continue;
168 }
169
170 e->vks[i].scancode = strtol(token[1], NULL, 0);
171 e->vks[i].centerx = strtol(token[2], NULL, 0);
172 e->vks[i].centery = strtol(token[3], NULL, 0);
173 e->vks[i].width = strtol(token[4], NULL, 0);
174 e->vks[i].height = strtol(token[5], NULL, 0);
175 }
176
177 return 0;
178}
179
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800180int ev_init(void)
181{
182 DIR *dir;
183 struct dirent *de;
184 int fd;
185
186 dir = opendir("/dev/input");
187 if(dir != 0) {
188 while((de = readdir(dir))) {
189// fprintf(stderr,"/dev/input/%s\n", de->d_name);
190 if(strncmp(de->d_name,"event",5)) continue;
191 fd = openat(dirfd(dir), de->d_name, O_RDONLY);
192 if(fd < 0) continue;
193
194 ev_fds[ev_count].fd = fd;
195 ev_fds[ev_count].events = POLLIN;
Christopher Laisac731c82010-11-24 18:49:52 -0600196 evs[ev_count].fd = &ev_fds[ev_count];
197
198 /* Load virtualkeys if there are any */
199 vk_init(&evs[ev_count]);
200
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800201 ev_count++;
202 if(ev_count == MAX_DEVICES) break;
203 }
204 }
205
206 return 0;
207}
208
209void ev_exit(void)
210{
Christopher Laisac731c82010-11-24 18:49:52 -0600211 while (ev_count-- > 0) {
212 if (evs[ev_count].vk_count) {
213 free(evs[ev_count].vks);
214 evs[ev_count].vk_count = 0;
215 }
216 close(ev_fds[ev_count].fd);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800217 }
218}
219
Christopher Laisac731c82010-11-24 18:49:52 -0600220static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
221{
222 int screen_pos;
223
224 if (info->minimum == info->maximum)
225 return 0;
226
227 screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
228 return (screen_pos >= 0 && screen_pos < screen_size);
229}
230
231static int vk_tp_to_screen(struct position *p, int *x, int *y)
232{
233 if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
234 return 0;
235
236 *x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum);
237 *y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum);
238
239 if (*x >= 0 && *x < gr_fb_width() &&
240 *y >= 0 && *y < gr_fb_height()) {
241 return 0;
242 }
243
244 return 1;
245}
246
247/* Translate a virtual key in to a real key event, if needed */
248/* Returns non-zero when the event should be consumed */
249static int vk_modify(struct ev *e, struct input_event *ev)
250{
251 int i;
252 int x, y;
253
254 if (ev->type == EV_KEY) {
255 if (ev->code == BTN_TOUCH && !ev->value)
256 e->down = DOWN_RELEASED;
257 return 0;
258 }
259
260 if (ev->type == EV_ABS) {
261 switch (ev->code) {
262 case ABS_X:
263 e->p.synced = 1;
264 e->p.x = ev->value;
265 return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width());
266 case ABS_Y:
267 e->p.synced = 1;
268 e->p.y = ev->value;
269 return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height());
270 case ABS_MT_POSITION_X:
271 if (e->mt_p.synced & 2) return 1;
272 e->mt_p.synced = 1;
273 e->mt_p.x = ev->value;
274 return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width());
275 case ABS_MT_POSITION_Y:
276 if (e->mt_p.synced & 2) return 1;
277 e->mt_p.synced = 1;
278 e->mt_p.y = ev->value;
279 return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height());
280 case ABS_MT_TOUCH_MAJOR:
281 if (e->mt_p.synced & 2) return 1;
282 if (!ev->value) e->down = DOWN_RELEASED;
283 return 0;
284 }
285
286 return 0;
287 }
288
289 if (ev->type != EV_SYN)
290 return 0;
291
292 if (ev->code == SYN_MT_REPORT) {
293 /* Ignore the rest of the points */
294 e->mt_p.synced |= 2;
295 return 0;
296 }
297 if (ev->code != SYN_REPORT)
298 return 0;
299
300 if (e->down == DOWN_RELEASED) {
301 e->down = DOWN_NOT;
302 /* TODO: Send emulated key release? */
303 return 1;
304 }
305
306 if (!(e->p.synced && vk_tp_to_screen(&e->p, &x, &y)) &&
307 !((e->mt_p.synced & 1) && vk_tp_to_screen(&e->mt_p, &x, &y))) {
308 return 0;
309 }
310
311 e->p.synced = e->mt_p.synced = 0;
312
313 if (e->down)
314 return 1;
315
316 for (i = 0; i < e->vk_count; ++i) {
317 int xd = ABS(e->vks[i].centerx - x);
318 int yd = ABS(e->vks[i].centery - y);
319 if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) {
320 /* Fake a key event */
321 e->down = DOWN_SENT;
322
323 ev->type = EV_KEY;
324 ev->code = e->vks[i].scancode;
325 ev->value = 1;
Christopher Lais44bd4942010-11-27 13:36:23 -0600326
327 vibrate(VIBRATOR_TIME_MS);
Christopher Laisac731c82010-11-24 18:49:52 -0600328 return 0;
329 }
330 }
331
332 return 1;
333}
334
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800335int ev_get(struct input_event *ev, unsigned dont_wait)
336{
337 int r;
338 unsigned n;
339
340 do {
341 r = poll(ev_fds, ev_count, dont_wait ? 0 : -1);
342
343 if(r > 0) {
344 for(n = 0; n < ev_count; n++) {
345 if(ev_fds[n].revents & POLLIN) {
346 r = read(ev_fds[n].fd, ev, sizeof(*ev));
Christopher Laisac731c82010-11-24 18:49:52 -0600347 if(r == sizeof(*ev)) {
348 if (!vk_modify(&evs[n], ev))
349 return 0;
350 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800351 }
352 }
353 }
354 } while(dont_wait == 0);
355
356 return -1;
357}