src/video/x11/SDL_x11xinput2.c
author Sylvain Becker <sylvain.becker@gmail.com>
Wed, 10 Jul 2019 10:06:28 +0200
changeset 12936 fe7caa031d3e
parent 12503 806492103856
child 12979 bbbb30026158
permissions -rw-r--r--
x11: prevent a synthetic mouse event when using a touchscreen

With multitouch, register to receive XI_Motion (which desctivates MotionNotify),
so that we can distinguish real mouse motions from synthetic one.

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