aboutsummaryrefslogtreecommitdiffstats
path: root/ui.cpp
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2014-03-27 09:18:00 -0700
committerTom Marshall <tdm@cyngn.com>2015-11-25 15:35:22 -0800
commit8e614b89838dda1adff952c0bfbb02721bb5db2b (patch)
tree1424980ad0021219328b3779eee9bae9380d9bc6 /ui.cpp
parenta48fd33655b2d95c250f033eaf88999cfb66e8ea (diff)
downloadbootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.zip
bootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.tar.gz
bootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.tar.bz2
sr: Touch UI
Change-Id: I4ee87f3474aec0496c47bb561ddecc74e151cbbf
Diffstat (limited to 'ui.cpp')
-rw-r--r--ui.cpp426
1 files changed, 391 insertions, 35 deletions
diff --git a/ui.cpp b/ui.cpp
index a5896ce..1594580 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -31,6 +31,7 @@
#include <cutils/properties.h>
#include <cutils/android_reboot.h>
+#include <cutils/properties.h>
#include "common.h"
#include "roots.h"
@@ -45,6 +46,11 @@
#define UI_WAIT_KEY_TIMEOUT_SEC 120
+/* Some extra input defines */
+#ifndef ABS_MT_ANGLE
+#define ABS_MT_ANGLE 0x38
+#endif
+
static int string_split(char* s, char** fields, int maxfields)
{
int n = 0;
@@ -62,9 +68,14 @@ static int string_split(char* s, char** fields, int maxfields)
return n+1;
}
+struct ms_info {
+ RecoveryUI* ui;
+ MessageSocket* sock;
+};
+
static int message_socket_client_event(int fd, uint32_t epevents, void *data)
{
- MessageSocket* client = (MessageSocket*)data;
+ ms_info* info = (ms_info*)data;
printf("message_socket client event\n");
if (!(epevents & EPOLLIN)) {
@@ -73,12 +84,13 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
char buf[256];
ssize_t nread;
- nread = client->Read(buf, sizeof(buf));
+ nread = info->sock->Read(buf, sizeof(buf));
if (nread <= 0) {
ev_del_fd(fd);
- self->DialogDismiss();
- client->Close();
- delete client;
+ info->ui->DialogDismiss();
+ info->sock->Close();
+ delete info->sock;
+ delete info;
return 0;
}
@@ -96,10 +108,10 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
printf("field[0]=%s, field[1]=%s\n", fields[0], fields[1]);
if (strcmp(fields[0], "dialog") == 0) {
if (strcmp(fields[1], "show") == 0 && nfields > 2) {
- self->DialogShowInfo(fields[2]);
+ info->ui->DialogShowInfo(fields[2]);
}
if (strcmp(fields[1], "dismiss") == 0) {
- self->DialogDismiss();
+ info->ui->DialogDismiss();
}
}
@@ -108,12 +120,15 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
static int message_socket_listen_event(int fd, uint32_t epevents, void *data)
{
- MessageSocket* ms = (MessageSocket*)data;
- MessageSocket* client = ms->Accept();
+ ms_info* info = (ms_info*)data;
+ MessageSocket* sock = info->sock->Accept();
printf("message_socket_listen_event: event on %d\n", fd);
- if (client) {
+ if (sock) {
printf("message_socket client connected\n");
- ev_add_fd(client->fd(), message_socket_client_event, client);
+ ms_info* clientinfo = new ms_info;
+ clientinfo->ui = info->ui;
+ clientinfo->sock = sock;
+ ev_add_fd(sock->fd(), message_socket_client_event, clientinfo);
}
return 0;
}
@@ -160,10 +175,14 @@ static void* InputThreadLoop(void*) {
}
void RecoveryUI::Init() {
+ calibrate_swipe();
ev_init(InputCallback, this);
message_socket.ServerInit();
- ev_add_fd(message_socket.fd(), message_socket_listen_event, &message_socket);
+ ms_info* info = new ms_info;
+ info->ui = this;
+ info->sock = &message_socket;
+ ev_add_fd(message_socket.fd(), message_socket_listen_event, info);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
@@ -176,31 +195,45 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
return -1;
}
- if (ev.type == EV_SYN) {
- return 0;
- } else if (ev.type == EV_REL) {
- if (ev.code == REL_Y) {
- // accumulate the up or down motion reported by
- // the trackball. When it exceeds a threshold
- // (positive or negative), fake an up/down
- // key event.
- rel_sum += ev.value;
- if (rel_sum > 3) {
- ProcessKey(KEY_DOWN, 1); // press down key
- ProcessKey(KEY_DOWN, 0); // and release it
- rel_sum = 0;
- } else if (rel_sum < -3) {
- ProcessKey(KEY_UP, 1); // press up key
- ProcessKey(KEY_UP, 0); // and release it
- rel_sum = 0;
- }
+ input_device* dev = NULL;
+ int n;
+ for (n = 0; n < MAX_NR_INPUT_DEVICES; ++n) {
+ if (input_devices[n].fd == fd) {
+ dev = &input_devices[n];
+ break;
}
- } else {
- rel_sum = 0;
+ if (input_devices[n].fd == -1) {
+ dev = &input_devices[n];
+ memset(dev, 0, sizeof(input_device));
+ dev->fd = fd;
+ dev->tracking_id = -1;
+ calibrate_touch(dev);
+ setup_vkeys(dev);
+ break;
+ }
+ }
+ if (!dev) {
+ LOGE("input_callback: no more available input devices\n");
+ return -1;
+ }
+
+ if (ev.type != EV_REL) {
+ dev->rel_sum = 0;
}
- if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ switch (ev.type) {
+ case EV_SYN:
+ ProcessSyn(dev, ev.code, ev.value);
+ break;
+ case EV_ABS:
+ ProcessAbs(dev, ev.code, ev.value);
+ break;
+ case EV_REL:
+ ProcessRel(dev, ev.code, ev.value);
+ break;
+ case EV_KEY:
+ ProcessKey(dev, ev.code, ev.value);
+ break;
}
return 0;
@@ -218,11 +251,14 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
// a key is registered.
//
// updown == 1 for key down events; 0 for key up events
-void RecoveryUI::ProcessKey(int key_code, int updown) {
+void RecoveryUI::ProcessKey(input_device* dev, int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
+ if (key_code > KEY_MAX)
+ return;
+
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
@@ -272,6 +308,146 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
}
}
+void RecoveryUI::ProcessSyn(input_device* dev, int code, int value) {
+ /*
+ * Type A device release:
+ * 1. Lack of position update
+ * 2. BTN_TOUCH | ABS_PRESSURE | SYN_MT_REPORT
+ * 3. SYN_REPORT
+ *
+ * Type B device release:
+ * 1. ABS_MT_TRACKING_ID == -1 for "first" slot
+ * 2. SYN_REPORT
+ */
+
+ if (code == SYN_MT_REPORT) {
+ if (!dev->in_touch && (dev->saw_pos_x && dev->saw_pos_y)) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a press\n");
+#endif
+ handle_press(dev);
+ }
+ dev->saw_mt_report = true;
+ return;
+ }
+ if (code == SYN_REPORT) {
+ if (dev->in_touch) {
+ handle_gestures(dev);
+ }
+ else {
+ if (dev->saw_tracking_id) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b press\n");
+#endif
+ handle_press(dev);
+ }
+ }
+
+ /* Detect release */
+ if (dev->saw_mt_report) {
+ if (dev->in_touch && !dev->saw_pos_x && !dev->saw_pos_y) {
+ /* type A release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+ else {
+ if (dev->in_touch && dev->saw_tracking_id && dev->tracking_id == -1 &&
+ dev->slot_current == dev->slot_first) {
+ /* type B release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+
+ dev->saw_pos_x = dev->saw_pos_y = false;
+ dev->saw_mt_report = dev->saw_tracking_id = false;
+ }
+}
+
+void RecoveryUI::ProcessAbs(input_device* dev, int code, int value) {
+ if (code == ABS_MT_SLOT) {
+ dev->slot_current = value;
+ if (dev->slot_first == -1) {
+ dev->slot_first = value;
+ }
+ return;
+ }
+ if (code == ABS_MT_TRACKING_ID) {
+ /*
+ * Some devices send an initial ABS_MT_SLOT event before switching
+ * to type B events, so discard any type A state related to slot.
+ */
+ dev->saw_tracking_id = true;
+ dev->slot_first = dev->slot_current = 0;
+
+ if (value != dev->tracking_id) {
+ dev->tracking_id = value;
+ if (dev->tracking_id < 0) {
+ dev->slot_nr_active--;
+ }
+ else {
+ dev->slot_nr_active++;
+ }
+ }
+ return;
+ }
+ /*
+ * For type A devices, we "lock" onto the first coordinates by ignoring
+ * position updates from the time we see a SYN_MT_REPORT until the next
+ * SYN_REPORT
+ *
+ * For type B devices, we "lock" onto the first slot seen until all slots
+ * are released
+ */
+ if (dev->slot_nr_active == 0) {
+ /* type A */
+ if (dev->saw_pos_x && dev->saw_pos_y) {
+ return;
+ }
+ }
+ else {
+ if (dev->slot_current != dev->slot_first) {
+ return;
+ }
+ }
+ if (code == ABS_MT_POSITION_X) {
+ dev->saw_pos_x = true;
+ dev->touch_pos.x = value * fb_dimensions.x / (dev->touch_max.x - dev->touch_min.x);
+ }
+ else if (code == ABS_MT_POSITION_Y) {
+ dev->saw_pos_y = true;
+ dev->touch_pos.y = value * fb_dimensions.y / (dev->touch_max.y - dev->touch_min.y);
+ }
+}
+
+void RecoveryUI::ProcessRel(input_device* dev, int code, int value) {
+#ifdef BOARD_RECOVERY_NEEDS_REL_INPUT
+ if (code == REL_Y) {
+ // accumulate the up or down motion reported by
+ // the trackball. When it exceeds a threshold
+ // (positive or negative), fake an up/down
+ // key event.
+ dev->rel_sum += value;
+ if (dev->rel_sum > 3) {
+ process_key(dev, KEY_DOWN, 1); // press down key
+ process_key(dev, KEY_DOWN, 0); // and release it
+ dev->rel_sum = 0;
+ } else if (dev->rel_sum < -3) {
+ process_key(dev, KEY_UP, 1); // press up key
+ process_key(dev, KEY_UP, 0); // and release it
+ dev->rel_sum = 0;
+ }
+ }
+#endif
+}
+
void* RecoveryUI::time_key_helper(void* cookie) {
key_timer_t* info = (key_timer_t*) cookie;
info->ui->time_key(info->key_code, info->count);
@@ -290,6 +466,186 @@ void RecoveryUI::time_key(int key_code, int count) {
if (long_press) KeyLongPress(key_code);
}
+void RecoveryUI::calibrate_touch(input_device* dev) {
+ fb_dimensions.x = gr_fb_width();
+ fb_dimensions.y = gr_fb_height();
+
+ struct input_absinfo info;
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_X), &info) == 0) {
+ dev->touch_min.x = info.minimum;
+ dev->touch_max.x = info.maximum;
+ dev->touch_pos.x = info.value;
+ }
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_Y), &info) == 0) {
+ dev->touch_min.y = info.minimum;
+ dev->touch_max.y = info.maximum;
+ dev->touch_pos.y = info.value;
+ }
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_touch: fd=%d, (%d,%d)-(%d,%d) pos (%d,%d)\n", dev->fd,
+ dev->touch_min.x, dev->touch_min.y,
+ dev->touch_max.x, dev->touch_max.y,
+ dev->touch_pos.x, dev->touch_pos.y);
+#endif
+}
+
+void RecoveryUI::setup_vkeys(input_device* dev) {
+ int n;
+ char name[256];
+ char path[PATH_MAX];
+ char buf[64*MAX_NR_VKEYS];
+
+ for (n = 0; n < MAX_NR_VKEYS; ++n) {
+ dev->virtual_keys[n].keycode = -1;
+ }
+
+ memset(name, 0, sizeof(name));
+ if (ioctl(dev->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+ LOGI("setup_vkeys: no vkeys\n");
+ return;
+ }
+ sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
+ int vkfd = open(path, O_RDONLY);
+ if (vkfd < 0) {
+ LOGI("setup_vkeys: could not open %s\n", path);
+ return;
+ }
+ ssize_t len = read(vkfd, buf, sizeof(buf));
+ close(vkfd);
+ if (len <= 0) {
+ LOGE("setup_vkeys: could not read %s\n", path);
+ return;
+ }
+ buf[len] = '\0';
+
+ char* p = buf;
+ char* endp;
+ for (n = 0; n < MAX_NR_VKEYS && p < buf+len && *p == '0'; ++n) {
+ int val[6];
+ int f;
+ for (f = 0; *p && f < 6; ++f) {
+ val[f] = strtol(p, &endp, 0);
+ if (p == endp)
+ break;
+ p = endp+1;
+ }
+ if (f != 6 || val[0] != 0x01)
+ break;
+ dev->virtual_keys[n].keycode = val[1];
+ dev->virtual_keys[n].min.x = val[2] - val[4]/2;
+ dev->virtual_keys[n].min.y = val[3] - val[5]/2;
+ dev->virtual_keys[n].max.x = val[2] + val[4]/2;
+ dev->virtual_keys[n].max.y = val[3] + val[5]/2;
+
+#ifdef DEBUG_TOUCH
+ LOGI("vkey: fd=%d, [%d]=(%d,%d)-(%d,%d)\n", dev->fd,
+ dev->virtual_keys[n].keycode,
+ dev->virtual_keys[n].min.x, dev->virtual_keys[n].min.y,
+ dev->virtual_keys[n].max.x, dev->virtual_keys[n].max.y);
+#endif
+ }
+}
+
+void RecoveryUI::calibrate_swipe() {
+ char strvalue[PROPERTY_VALUE_MAX];
+ int intvalue;
+ property_get("ro.sf.lcd_density", strvalue, "160");
+ intvalue = atoi(strvalue);
+ int screen_density = (intvalue >= 160 ? intvalue : 160);
+ min_swipe_px.x = screen_density * 50 / 100; // Roughly 0.5in
+ min_swipe_px.y = screen_density * 30 / 100; // Roughly 0.3in
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_swipe: density=%d, min_swipe=(%d,%d)\n",
+ screen_density, min_swipe_px.x, min_swipe_px.y);
+#endif
+}
+
+void RecoveryUI::handle_press(input_device* dev) {
+ dev->touch_start = dev->touch_track = dev->touch_pos;
+ dev->in_touch = true;
+ dev->in_swipe = false;
+}
+
+void RecoveryUI::handle_release(input_device* dev) {
+ struct point diff = dev->touch_pos - dev->touch_start;
+ bool in_touch = dev->in_touch;
+ bool in_swipe = dev->in_swipe;
+
+ dev->in_touch = dev->in_swipe = false;
+
+ if (!in_swipe) {
+ int n;
+ for (n = 0; dev->virtual_keys[n].keycode != -1 && n < MAX_NR_VKEYS; ++n) {
+ vkey* vk = &dev->virtual_keys[n];
+ if (dev->touch_start.x >= vk->min.x && dev->touch_start.x < vk->max.x &&
+ dev->touch_start.y >= vk->min.y && dev->touch_start.y < vk->max.y) {
+#ifdef DEBUG_TOUCH
+ LOGI("handle_release: vkey %d\n", vk->keycode);
+#endif
+ EnqueueKey(vk->keycode);
+ return;
+ }
+ }
+ }
+
+ if (DialogShowing()) {
+ if (DialogDismissable() && !dev->in_swipe) {
+ DialogDismiss();
+ }
+ return;
+ }
+
+ if (in_swipe) {
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ int key = (diff.x > 0 ? KEY_ENTER : KEY_BACK);
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ else {
+ /* Vertical swipe, handled realtime */
+ }
+ }
+ else {
+ int sel, start_menu_pos;
+ // Make sure touch pos is not less than menu start pos.
+ // No need to check if beyond end of menu items, since
+ // that is checked by get_menu_selection().
+ start_menu_pos = MenuItemStart();
+ if (dev->touch_pos.y >= start_menu_pos) {
+ sel = (dev->touch_pos.y - start_menu_pos)/MenuItemHeight();
+ EnqueueKey(KEY_FLAG_ABS | sel);
+ }
+ }
+}
+
+void RecoveryUI::handle_gestures(input_device* dev) {
+ struct point diff;
+ diff = dev->touch_pos - dev->touch_start;
+
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ /* Horizontal swipe, handle it on release */
+ dev->in_swipe = true;
+ }
+ }
+ else {
+ diff.y = dev->touch_pos.y - dev->touch_track.y;
+ if (abs(diff.y) > MenuItemHeight()) {
+ dev->in_swipe = true;
+ if (!DialogShowing()) {
+ dev->touch_track = dev->touch_pos;
+ int key = (diff.y < 0) ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ }
+}
+
void RecoveryUI::EnqueueKey(int key_code) {
if (DialogShowing()) {
if (DialogDismissable()) {