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 <https://discourse.libsdl.org/> EVDEV KeyCode 330" (EVDEV KeyCode 330 is BTN_TOUCH.) Fixes Bugzilla #4147.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index f8528e9..e557771 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);