From 2755a505a344e8356ae05ad735847b1c609659e7 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 14 Jan 2019 19:36:54 -0500 Subject: [PATCH] evdev: Add touchscreen mouse emulation and pressure support (thanks, Zach!). This also solves reports of this log message: "INFO: The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list EVDEV KeyCode 330" (EVDEV KeyCode 330 is BTN_TOUCH.) Fixes Bugzilla #4147. --- src/core/linux/SDL_evdev.c | 90 +++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c index f8528e9ea2467..e5577716be487 100644 --- a/src/core/linux/SDL_evdev.c +++ b/src/core/linux/SDL_evdev.c @@ -55,6 +55,7 @@ #define ABS_MT_POSITION_X 0x35 #define ABS_MT_POSITION_Y 0x36 #define ABS_MT_TRACKING_ID 0x39 +#define ABS_MT_PRESSURE 0x3a #endif typedef struct SDL_evdevlist_item @@ -74,6 +75,7 @@ typedef struct SDL_evdevlist_item int min_x, max_x, range_x; int min_y, max_y, range_y; + int min_pressure, max_pressure, range_pressure; int max_slots; int current_slot; @@ -85,8 +87,10 @@ typedef struct SDL_evdevlist_item EVDEV_TOUCH_SLOTDELTA_MOVE } delta; int tracking_id; - int x, y; + int x, y, pressure; } * slots; + + int pointerFingerID; } * touchscreen_data; struct SDL_evdevlist_item *next; @@ -229,7 +233,8 @@ SDL_EVDEV_Poll(void) SDL_Scancode scan_code; int mouse_button; SDL_Mouse *mouse; - float norm_x, norm_y; + float norm_x, norm_y, norm_pressure; + int abs_x, abs_y; if (!_this) { return; @@ -264,6 +269,16 @@ SDL_EVDEV_Poll(void) break; } + /* BTH_TOUCH event value 1 indicates there is contact with + a touchscreen or trackpad (earlist finger's current + position is sent in EV_ABS ABS_X/ABS_Y, switching to + next finger after earlist is released) however using it + for virtual mouse SDL_TOUCH_MOUSEID would differ from + other SDL backends which require a new finger touch. */ + if (item->is_touchscreen && events[i].code == BTN_TOUCH) { + break; + } + /* Probably keyboard */ scan_code = SDL_EVDEV_translate_keycode(events[i].code); if (scan_code != SDL_SCANCODE_UNKNOWN) { @@ -308,6 +323,14 @@ SDL_EVDEV_Poll(void) item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; } break; + case ABS_MT_PRESSURE: + if (!item->is_touchscreen) /* FIXME: temp hack */ + break; + item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value; + if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) { + item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; + } + break; case ABS_X: if (item->is_touchscreen) /* FIXME: temp hack */ break; @@ -352,18 +375,45 @@ SDL_EVDEV_Poll(void) norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) / (float)item->touchscreen_data->range_y; + if (item->touchscreen_data->range_pressure > 0) { + norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) / + (float)item->touchscreen_data->range_pressure; + } else { + /* This touchscreen does not support pressure */ + norm_pressure = 1.0f; + } + + abs_x = item->touchscreen_data->slots[j].x; + abs_y = item->touchscreen_data->slots[j].y; + switch(item->touchscreen_data->slots[j].delta) { case EVDEV_TOUCH_SLOTDELTA_DOWN: - SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f); + if (item->touchscreen_data->pointerFingerID == -1) { + SDL_SendMouseMotion(mouse->focus, SDL_TOUCH_MOUSEID, 0, abs_x, abs_y); + + SDL_SendMouseButton(mouse->focus, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT); + item->touchscreen_data->pointerFingerID = item->touchscreen_data->slots[j].tracking_id; + } + + SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, norm_pressure); item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; break; case EVDEV_TOUCH_SLOTDELTA_UP: - SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f); + if (item->touchscreen_data->pointerFingerID == item->touchscreen_data->slots[j].tracking_id) { + SDL_SendMouseButton(mouse->focus, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT); + item->touchscreen_data->pointerFingerID = -1; + } + + SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, norm_pressure); item->touchscreen_data->slots[j].tracking_id = -1; item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; break; case EVDEV_TOUCH_SLOTDELTA_MOVE: - SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f); + if (item->touchscreen_data->pointerFingerID == item->touchscreen_data->slots[j].tracking_id) { + SDL_SendMouseMotion(mouse->focus, SDL_TOUCH_MOUSEID, 0, abs_x, abs_y); + } + + SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, norm_pressure); item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; break; default: @@ -453,6 +503,16 @@ SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item) item->touchscreen_data->max_y = abs_info.maximum; item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum; + ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info); + if (ret < 0) { + SDL_free(item->touchscreen_data->name); + SDL_free(item->touchscreen_data); + return SDL_SetError("Failed to get evdev touchscreen limits"); + } + item->touchscreen_data->min_pressure = abs_info.minimum; + item->touchscreen_data->max_pressure = abs_info.maximum; + item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum; + ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); if (ret < 0) { SDL_free(item->touchscreen_data->name); @@ -474,6 +534,8 @@ SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item) item->touchscreen_data->slots[i].tracking_id = -1; } + item->touchscreen_data->pointerFingerID = -1; + ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */ SDL_TOUCH_DEVICE_DIRECT, item->touchscreen_data->name); @@ -594,6 +656,24 @@ SDL_EVDEV_sync_device(SDL_evdevlist_item *item) } } + *mt_req_code = ABS_MT_PRESSURE; + ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); + if (ret < 0) { + SDL_free(mt_req_code); + return; + } + for(i = 0; i < item->touchscreen_data->max_slots; i++) { + if (item->touchscreen_data->slots[i].tracking_id >= 0 && + item->touchscreen_data->slots[i].pressure != mt_req_values[i]) { + item->touchscreen_data->slots[i].pressure = mt_req_values[i]; + if (item->touchscreen_data->slots[i].delta == + EVDEV_TOUCH_SLOTDELTA_NONE) { + item->touchscreen_data->slots[i].delta = + EVDEV_TOUCH_SLOTDELTA_MOVE; + } + } + } + ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); if (ret < 0) { SDL_free(mt_req_code);