From 8e614b89838dda1adff952c0bfbb02721bb5db2b Mon Sep 17 00:00:00 2001 From: Tom Marshall Date: Thu, 27 Mar 2014 09:18:00 -0700 Subject: sr: Touch UI Change-Id: I4ee87f3474aec0496c47bb561ddecc74e151cbbf --- ui.cpp | 426 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 391 insertions(+), 35 deletions(-) (limited to 'ui.cpp') diff --git a/ui.cpp b/ui.cpp index a5896ce..1594580 100644 --- a/ui.cpp +++ b/ui.cpp @@ -31,6 +31,7 @@ #include #include +#include #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()) { -- cgit v1.1