evdev: Add touchscreen mouse emulation and pressure support (thanks, Zach!).
authorRyan C. Gordon <icculus@icculus.org>
Mon, 14 Jan 2019 19:36:54 -0500
changeset 12547f100ca4dd31e
parent 12546 aab006efb893
child 12548 1ae839748d02
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.
src/core/linux/SDL_evdev.c
     1.1 --- a/src/core/linux/SDL_evdev.c	Mon Jan 14 23:33:48 2019 +0100
     1.2 +++ b/src/core/linux/SDL_evdev.c	Mon Jan 14 19:36:54 2019 -0500
     1.3 @@ -55,6 +55,7 @@
     1.4  #define ABS_MT_POSITION_X   0x35
     1.5  #define ABS_MT_POSITION_Y   0x36
     1.6  #define ABS_MT_TRACKING_ID  0x39
     1.7 +#define ABS_MT_PRESSURE     0x3a
     1.8  #endif
     1.9  
    1.10  typedef struct SDL_evdevlist_item
    1.11 @@ -74,6 +75,7 @@
    1.12  
    1.13          int min_x, max_x, range_x;
    1.14          int min_y, max_y, range_y;
    1.15 +        int min_pressure, max_pressure, range_pressure;
    1.16  
    1.17          int max_slots;
    1.18          int current_slot;
    1.19 @@ -85,8 +87,10 @@
    1.20                  EVDEV_TOUCH_SLOTDELTA_MOVE
    1.21              } delta;
    1.22              int tracking_id;
    1.23 -            int x, y;
    1.24 +            int x, y, pressure;
    1.25          } * slots;
    1.26 +
    1.27 +        int pointerFingerID;
    1.28      } * touchscreen_data;
    1.29  
    1.30      struct SDL_evdevlist_item *next;
    1.31 @@ -229,7 +233,8 @@
    1.32      SDL_Scancode scan_code;
    1.33      int mouse_button;
    1.34      SDL_Mouse *mouse;
    1.35 -    float norm_x, norm_y;
    1.36 +    float norm_x, norm_y, norm_pressure;
    1.37 +    int abs_x, abs_y;
    1.38  
    1.39      if (!_this) {
    1.40          return;
    1.41 @@ -264,6 +269,16 @@
    1.42                          break;
    1.43                      }
    1.44  
    1.45 +                    /* BTH_TOUCH event value 1 indicates there is contact with
    1.46 +                       a touchscreen or trackpad (earlist finger's current
    1.47 +                       position is sent in EV_ABS ABS_X/ABS_Y, switching to
    1.48 +                       next finger after earlist is released) however using it
    1.49 +                       for virtual mouse SDL_TOUCH_MOUSEID would differ from
    1.50 +                       other SDL backends which require a new finger touch. */
    1.51 +                    if (item->is_touchscreen && events[i].code == BTN_TOUCH) {
    1.52 +                        break;
    1.53 +                    }
    1.54 +
    1.55                      /* Probably keyboard */
    1.56                      scan_code = SDL_EVDEV_translate_keycode(events[i].code);
    1.57                      if (scan_code != SDL_SCANCODE_UNKNOWN) {
    1.58 @@ -308,6 +323,14 @@
    1.59                              item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
    1.60                          }
    1.61                          break;
    1.62 +                    case ABS_MT_PRESSURE:
    1.63 +                        if (!item->is_touchscreen) /* FIXME: temp hack */
    1.64 +                            break;
    1.65 +                        item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value;
    1.66 +                        if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
    1.67 +                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
    1.68 +                        }
    1.69 +                        break;
    1.70                      case ABS_X:
    1.71                          if (item->is_touchscreen) /* FIXME: temp hack */
    1.72                              break;
    1.73 @@ -352,18 +375,45 @@
    1.74                              norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
    1.75                                  (float)item->touchscreen_data->range_y;
    1.76  
    1.77 +                            if (item->touchscreen_data->range_pressure > 0) {
    1.78 +                                norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
    1.79 +                                    (float)item->touchscreen_data->range_pressure;
    1.80 +                            } else {
    1.81 +                                /* This touchscreen does not support pressure */
    1.82 +                                norm_pressure = 1.0f;
    1.83 +                            }
    1.84 +
    1.85 +                            abs_x = item->touchscreen_data->slots[j].x;
    1.86 +                            abs_y = item->touchscreen_data->slots[j].y;
    1.87 +
    1.88                              switch(item->touchscreen_data->slots[j].delta) {
    1.89                              case EVDEV_TOUCH_SLOTDELTA_DOWN:
    1.90 -                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f);
    1.91 +                                if (item->touchscreen_data->pointerFingerID == -1) {
    1.92 +                                    SDL_SendMouseMotion(mouse->focus, SDL_TOUCH_MOUSEID, 0, abs_x, abs_y);
    1.93 +
    1.94 +                                    SDL_SendMouseButton(mouse->focus, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
    1.95 +                                    item->touchscreen_data->pointerFingerID = item->touchscreen_data->slots[j].tracking_id;
    1.96 +                                }
    1.97 +
    1.98 +                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, norm_pressure);
    1.99                                  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   1.100                                  break;
   1.101                              case EVDEV_TOUCH_SLOTDELTA_UP:
   1.102 -                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f);
   1.103 +                                if (item->touchscreen_data->pointerFingerID == item->touchscreen_data->slots[j].tracking_id) {
   1.104 +                                    SDL_SendMouseButton(mouse->focus, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   1.105 +                                    item->touchscreen_data->pointerFingerID = -1;
   1.106 +                                }
   1.107 +
   1.108 +                                SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, norm_pressure);
   1.109                                  item->touchscreen_data->slots[j].tracking_id = -1;
   1.110                                  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   1.111                                  break;
   1.112                              case EVDEV_TOUCH_SLOTDELTA_MOVE:
   1.113 -                                SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f);
   1.114 +                                if (item->touchscreen_data->pointerFingerID == item->touchscreen_data->slots[j].tracking_id) {
   1.115 +                                    SDL_SendMouseMotion(mouse->focus, SDL_TOUCH_MOUSEID, 0, abs_x, abs_y);
   1.116 +                                }
   1.117 +
   1.118 +                                SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, norm_pressure);
   1.119                                  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
   1.120                                  break;
   1.121                              default:
   1.122 @@ -453,6 +503,16 @@
   1.123      item->touchscreen_data->max_y = abs_info.maximum;
   1.124      item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
   1.125  
   1.126 +    ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
   1.127 +    if (ret < 0) {
   1.128 +        SDL_free(item->touchscreen_data->name);
   1.129 +        SDL_free(item->touchscreen_data);
   1.130 +        return SDL_SetError("Failed to get evdev touchscreen limits");
   1.131 +    }
   1.132 +    item->touchscreen_data->min_pressure = abs_info.minimum;
   1.133 +    item->touchscreen_data->max_pressure = abs_info.maximum;
   1.134 +    item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
   1.135 +
   1.136      ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   1.137      if (ret < 0) {
   1.138          SDL_free(item->touchscreen_data->name);
   1.139 @@ -474,6 +534,8 @@
   1.140          item->touchscreen_data->slots[i].tracking_id = -1;
   1.141      }
   1.142  
   1.143 +    item->touchscreen_data->pointerFingerID = -1;
   1.144 +
   1.145      ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
   1.146          SDL_TOUCH_DEVICE_DIRECT,
   1.147          item->touchscreen_data->name);
   1.148 @@ -594,6 +656,24 @@
   1.149          }
   1.150      }
   1.151  
   1.152 +    *mt_req_code = ABS_MT_PRESSURE;
   1.153 +    ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
   1.154 +    if (ret < 0) {
   1.155 +        SDL_free(mt_req_code);
   1.156 +        return;
   1.157 +    }
   1.158 +    for(i = 0; i < item->touchscreen_data->max_slots; i++) {
   1.159 +        if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
   1.160 +            item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
   1.161 +            item->touchscreen_data->slots[i].pressure = mt_req_values[i];
   1.162 +            if (item->touchscreen_data->slots[i].delta ==
   1.163 +                EVDEV_TOUCH_SLOTDELTA_NONE) {
   1.164 +                item->touchscreen_data->slots[i].delta =
   1.165 +                    EVDEV_TOUCH_SLOTDELTA_MOVE;
   1.166 +            }
   1.167 +        }
   1.168 +    }
   1.169 +
   1.170      ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
   1.171      if (ret < 0) {
   1.172          SDL_free(mt_req_code);