src/video/x11/SDL_x11xinput2.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 11 Oct 2017 13:31:21 -0700
changeset 11598 6fde74294cc9
parent 10737 3406a0f8b041
child 11602 8dd72342f1f1
permissions -rw-r--r--
Fixed bug 3871 - Touch events are not normalised on X11

Trent Gamblin

The documentation for SDL_TouchFingerEvent says that the x and y coordinates are normalised between 0-1. I've found that to be true on Windows, Android and iOS but on X11 they are in pixel coordinates. This patch fixes the issue. This was the cleanest way I could do it with what was available without changing things around a lot but you may know a better way.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include "SDL_x11video.h"
    26 #include "SDL_x11xinput2.h"
    27 #include "../../events/SDL_mouse_c.h"
    28 #include "../../events/SDL_touch_c.h"
    29 
    30 #define MAX_AXIS 16
    31 
    32 #if SDL_VIDEO_DRIVER_X11_XINPUT2
    33 static int xinput2_initialized = 0;
    34 
    35 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
    36 static int xinput2_multitouch_supported = 0;
    37 #endif
    38 
    39 /* Opcode returned X11_XQueryExtension
    40  * It will be used in event processing
    41  * to know that the event came from
    42  * this extension */
    43 static int xinput2_opcode;
    44 
    45 static void parse_valuators(const double *input_values,unsigned char *mask,int mask_len,
    46                             double *output_values,int output_values_len) {
    47     int i = 0,z = 0;
    48     int top = mask_len * 8;
    49     if (top > MAX_AXIS)
    50         top = MAX_AXIS;
    51 
    52     SDL_memset(output_values,0,output_values_len * sizeof(double));
    53     for (; i < top && z < output_values_len; i++) {
    54         if (XIMaskIsSet(mask, i)) {
    55             const int value = (int) *input_values;
    56             output_values[z] = value;
    57             input_values++;
    58         }
    59         z++;
    60     }
    61 }
    62 
    63 static int
    64 query_xinput2_version(Display *display, int major, int minor)
    65 {
    66     /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
    67     X11_XIQueryVersion(display, &major, &minor);
    68     return ((major * 1000) + minor);
    69 }
    70 
    71 static SDL_bool
    72 xinput2_version_atleast(const int version, const int wantmajor, const int wantminor)
    73 {
    74     return ( version >= ((wantmajor * 1000) + wantminor) );
    75 }
    76 
    77 static void
    78 xinput2_normalize_touch_coordinates(SDL_VideoData *videodata, Window window,
    79     double in_x, double in_y, float *out_x, float *out_y)
    80 {
    81    int i;
    82    for (i = 0; i < videodata->numwindows; i++) {
    83       SDL_WindowData *d = videodata->windowlist[i];
    84       if (d->xwindow == window) {
    85          *out_x = in_x / (d->window->w-1);
    86          *out_y = in_y / (d->window->h-1);
    87          return;
    88       }
    89    }
    90    // couldn't find the window...
    91    *out_x = in_x;
    92    *out_y = in_y;
    93 }
    94 #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
    95 
    96 void
    97 X11_InitXinput2(_THIS)
    98 {
    99 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   100     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   101 
   102     int version = 0;
   103     XIEventMask eventmask;
   104     unsigned char mask[3] = { 0,0,0 };
   105     int event, err;
   106 
   107     /*
   108     * Initialize XInput 2
   109     * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
   110     * to inform Xserver what version of Xinput we support.The server will store the version we support.
   111     * "As XI2 progresses it becomes important that you use this call as the server may treat the client
   112     * differently depending on the supported version".
   113     *
   114     * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
   115     */
   116     if (!SDL_X11_HAVE_XINPUT2 ||
   117         !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
   118         return; /* X server does not have XInput at all */
   119     }
   120 
   121     /* We need at least 2.2 for Multitouch, 2.0 otherwise. */
   122     version = query_xinput2_version(data->display, 2, 2);
   123     if (!xinput2_version_atleast(version, 2, 0)) {
   124         return; /* X server does not support the version we want at all. */
   125     }
   126 
   127     xinput2_initialized = 1;
   128 
   129 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH  /* Multitouch needs XInput 2.2 */
   130     xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
   131 #endif
   132 
   133     /* Enable  Raw motion events for this display */
   134     eventmask.deviceid = XIAllMasterDevices;
   135     eventmask.mask_len = sizeof(mask);
   136     eventmask.mask = mask;
   137 
   138     XISetMask(mask, XI_RawMotion);
   139     XISetMask(mask, XI_RawButtonPress);
   140     XISetMask(mask, XI_RawButtonRelease);
   141 
   142     if (X11_XISelectEvents(data->display,DefaultRootWindow(data->display),&eventmask,1) != Success) {
   143         return;
   144     }
   145 #endif
   146 }
   147 
   148 int
   149 X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
   150 {
   151 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   152     if(cookie->extension != xinput2_opcode) {
   153         return 0;
   154     }
   155     switch(cookie->evtype) {
   156         case XI_RawMotion: {
   157             const XIRawEvent *rawev = (const XIRawEvent*)cookie->data;
   158             SDL_Mouse *mouse = SDL_GetMouse();
   159             double relative_coords[2];
   160             static Time prev_time = 0;
   161             static double prev_rel_coords[2];
   162 
   163             videodata->global_mouse_changed = SDL_TRUE;
   164 
   165             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   166                 return 0;
   167             }
   168 
   169             parse_valuators(rawev->raw_values,rawev->valuators.mask,
   170                             rawev->valuators.mask_len,relative_coords,2);
   171 
   172             if ((rawev->time == prev_time) && (relative_coords[0] == prev_rel_coords[0]) && (relative_coords[1] == prev_rel_coords[1])) {
   173                 return 0;  /* duplicate event, drop it. */
   174             }
   175 
   176             SDL_SendMouseMotion(mouse->focus,mouse->mouseID,1,(int)relative_coords[0],(int)relative_coords[1]);
   177             prev_rel_coords[0] = relative_coords[0];
   178             prev_rel_coords[1] = relative_coords[1];
   179             prev_time = rawev->time;
   180             return 1;
   181             }
   182             break;
   183 
   184         case XI_RawButtonPress:
   185         case XI_RawButtonRelease:
   186             videodata->global_mouse_changed = SDL_TRUE;
   187             break;
   188 
   189 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   190         case XI_TouchBegin: {
   191             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   192             float x, y;
   193             xinput2_normalize_touch_coordinates(videodata, xev->event,
   194                                   xev->event_x, xev->event_y, &x, &y);
   195             SDL_SendTouch(xev->sourceid,xev->detail,
   196                       SDL_TRUE, x, y, 1.0);
   197             return 1;
   198             }
   199             break;
   200         case XI_TouchEnd: {
   201             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   202             float x, y;
   203             xinput2_normalize_touch_coordinates(videodata, xev->event,
   204                                   xev->event_x, xev->event_y, &x, &y);
   205             SDL_SendTouch(xev->sourceid,xev->detail,
   206                       SDL_FALSE, x, y, 1.0);
   207             return 1;
   208             }
   209             break;
   210         case XI_TouchUpdate: {
   211             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   212             float x, y;
   213             xinput2_normalize_touch_coordinates(videodata, xev->event,
   214                                   xev->event_x, xev->event_y, &x, &y);
   215             SDL_SendTouchMotion(xev->sourceid,xev->detail,
   216                                 x, y, 1.0);
   217             return 1;
   218             }
   219             break;
   220 #endif
   221     }
   222 #endif
   223     return 0;
   224 }
   225 
   226 void
   227 X11_InitXinput2Multitouch(_THIS)
   228 {
   229 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   230     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   231     XIDeviceInfo *info;
   232     int ndevices,i,j;
   233     info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
   234 
   235     for (i = 0; i < ndevices; i++) {
   236         XIDeviceInfo *dev = &info[i];
   237         for (j = 0; j < dev->num_classes; j++) {
   238             SDL_TouchID touchId;
   239             XIAnyClassInfo *class = dev->classes[j];
   240             XITouchClassInfo *t = (XITouchClassInfo*)class;
   241 
   242             /* Only touch devices */
   243             if (class->type != XITouchClass)
   244                 continue;
   245 
   246             touchId = t->sourceid;
   247             SDL_AddTouch(touchId, dev->name);
   248         }
   249     }
   250     X11_XIFreeDeviceInfo(info);
   251 #endif
   252 }
   253 
   254 void
   255 X11_Xinput2SelectTouch(_THIS, SDL_Window *window)
   256 {
   257 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   258     SDL_VideoData *data = NULL;
   259     XIEventMask eventmask;
   260     unsigned char mask[3] = { 0,0,0 };
   261     SDL_WindowData *window_data = NULL;
   262     
   263     if (!X11_Xinput2IsMultitouchSupported()) {
   264         return;
   265     }
   266 
   267     data = (SDL_VideoData *) _this->driverdata;
   268     window_data = (SDL_WindowData*)window->driverdata;
   269 
   270     eventmask.deviceid = XIAllMasterDevices;
   271     eventmask.mask_len = sizeof(mask);
   272     eventmask.mask = mask;
   273 
   274     XISetMask(mask, XI_TouchBegin);
   275     XISetMask(mask, XI_TouchUpdate);
   276     XISetMask(mask, XI_TouchEnd);
   277 
   278     X11_XISelectEvents(data->display,window_data->xwindow,&eventmask,1);
   279 #endif
   280 }
   281 
   282 
   283 int
   284 X11_Xinput2IsInitialized()
   285 {
   286 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   287     return xinput2_initialized;
   288 #else
   289     return 0;
   290 #endif
   291 }
   292 
   293 int
   294 X11_Xinput2IsMultitouchSupported()
   295 {
   296 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   297     return xinput2_initialized && xinput2_multitouch_supported;
   298 #else
   299     return 0;
   300 #endif
   301 }
   302 
   303 #endif /* SDL_VIDEO_DRIVER_X11 */
   304 
   305 /* vi: set ts=4 sw=4 expandtab: */