src/video/x11/SDL_x11xinput2.c
author Sam Lantinga
Mon, 23 Dec 2013 17:37:22 -0800
changeset 8071 1ac2db4abe11
parent 8019 5d85b2186aae
child 8093 b43765095a6f
permissions -rw-r--r--
Added a relative mouse mode that uses mouse warping instead of raw input.
To enable this, set the environment variable SDL_MOUSE_RELATIVE_MODE_WARP to "1"

When mouse relative mode is disabled, put the cursor back where the application expects it to be, instead of where it was when relative mode was enabled.
dimitris@6316
     1
/*
dimitris@6316
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 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
*/
dimitris@6316
    21
#include "SDL_config.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@8019
    63
static SDL_bool
icculus@8019
    64
xinput2_version_okay(Display *display, const int major, const int minor)
icculus@8019
    65
{
icculus@8019
    66
    int outmajor = major;
icculus@8019
    67
    int outminor = minor;
icculus@8019
    68
    if (X11_XIQueryVersion(display, &outmajor, &outminor) != Success) {
icculus@8019
    69
        return SDL_FALSE;
icculus@8019
    70
    }
icculus@8019
    71
icculus@8019
    72
    return ( ((outmajor * 1000) + outminor) >= ((major * 1000) + minor) );
icculus@8019
    73
}
dimitris@6316
    74
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
dimitris@6316
    75
slouken@7191
    76
void
slouken@6877
    77
X11_InitXinput2(_THIS)
slouken@6877
    78
{
dimitris@6316
    79
#if SDL_VIDEO_DRIVER_X11_XINPUT2
dimitris@6316
    80
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
dimitris@6316
    81
dimitris@6316
    82
    XIEventMask eventmask;
dimitris@6316
    83
    unsigned char mask[3] = { 0,0,0 };
dimitris@6316
    84
    int event, err;
dimitris@6316
    85
    /*
dimitris@6316
    86
    * Initialize XInput 2
dimitris@6316
    87
    * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
slouken@7191
    88
    * to inform Xserver what version of Xinput we support.The server will store the version we support.
slouken@7191
    89
    * "As XI2 progresses it becomes important that you use this call as the server may treat the client
dimitris@6316
    90
    * differently depending on the supported version".
dimitris@6316
    91
    *
icculus@7827
    92
    * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
dimitris@6316
    93
    */
slouken@6877
    94
    if (!SDL_X11_HAVE_XINPUT2 ||
icculus@7827
    95
        !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
icculus@8019
    96
        return; /* X server does not have XInput at all */
dimitris@6316
    97
    }
dimitris@6316
    98
icculus@8019
    99
    if (!xinput2_version_okay(data->display, 2, 0)) {
icculus@8019
   100
        return; /* X server does not support the version we want */
dimitris@6316
   101
    }
icculus@8019
   102
dimitris@6316
   103
    xinput2_initialized = 1;
icculus@8019
   104
dimitris@6316
   105
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
icculus@8019
   106
    if (xinput2_version_okay(data->display, 2, 2)) {  /* Multitouch needs XInput 2.2 */
dimitris@6316
   107
        xinput2_multitouch_supported = 1;
dimitris@6316
   108
    }
dimitris@6316
   109
#endif
dimitris@6316
   110
gabomdq@7678
   111
    /* Enable  Raw motion events for this display */
dimitris@6316
   112
    eventmask.deviceid = XIAllMasterDevices;
dimitris@6316
   113
    eventmask.mask_len = sizeof(mask);
dimitris@6316
   114
    eventmask.mask = mask;
dimitris@6316
   115
dimitris@6316
   116
    XISetMask(mask, XI_RawMotion);
slouken@7191
   117
icculus@7827
   118
    if (X11_XISelectEvents(data->display,DefaultRootWindow(data->display),&eventmask,1) != Success) {
slouken@7191
   119
        return;
dimitris@6316
   120
    }
dimitris@6316
   121
#endif
dimitris@6316
   122
}
dimitris@6316
   123
slouken@7191
   124
int
slouken@6877
   125
X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
slouken@6877
   126
{
dimitris@6316
   127
#if SDL_VIDEO_DRIVER_X11_XINPUT2
dimitris@6316
   128
    if(cookie->extension != xinput2_opcode) {
dimitris@6316
   129
        return 0;
dimitris@6316
   130
    }
dimitris@6316
   131
    switch(cookie->evtype) {
dimitris@6316
   132
        case XI_RawMotion: {
dimitris@6316
   133
            const XIRawEvent *rawev = (const XIRawEvent*)cookie->data;
dimitris@6316
   134
            SDL_Mouse *mouse = SDL_GetMouse();
dimitris@6316
   135
            double relative_cords[2];
dimitris@6316
   136
slouken@8071
   137
            if (!mouse->relative_mode || mouse->relative_mode_warp) {
dimitris@6316
   138
                return 0;
dimitris@6316
   139
            }
dimitris@6316
   140
dimitris@6316
   141
            parse_valuators(rawev->raw_values,rawev->valuators.mask,
dimitris@6316
   142
                            rawev->valuators.mask_len,relative_cords,2);
slouken@6950
   143
            SDL_SendMouseMotion(mouse->focus,mouse->mouseID,1,(int)relative_cords[0],(int)relative_cords[1]);
dimitris@6316
   144
            return 1;
dimitris@6316
   145
            }
dimitris@6316
   146
            break;
dimitris@6316
   147
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
dimitris@6316
   148
        case XI_TouchBegin: {
dimitris@6316
   149
            const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
slouken@6951
   150
            SDL_SendTouch(xev->sourceid,xev->detail,
slouken@6951
   151
                      SDL_TRUE, xev->event_x, xev->event_y, 1.0);
dimitris@6316
   152
            return 1;
dimitris@6316
   153
            }
dimitris@6316
   154
            break;
dimitris@6316
   155
        case XI_TouchEnd: {
dimitris@6316
   156
            const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
slouken@6951
   157
            SDL_SendTouch(xev->sourceid,xev->detail,
slouken@6951
   158
                      SDL_FALSE, xev->event_x, xev->event_y, 1.0);
dimitris@6316
   159
            return 1;
dimitris@6316
   160
            }
dimitris@6316
   161
            break;
dimitris@6316
   162
        case XI_TouchUpdate: {
dimitris@6316
   163
            const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
dimitris@6316
   164
            SDL_SendTouchMotion(xev->sourceid,xev->detail,
slouken@6951
   165
                                xev->event_x, xev->event_y, 1.0);
dimitris@6316
   166
            return 1;
dimitris@6316
   167
            }
dimitris@6316
   168
            break;
dimitris@6316
   169
#endif
dimitris@6316
   170
    }
dimitris@6316
   171
#endif
dimitris@6316
   172
    return 0;
dimitris@6316
   173
}
dimitris@6316
   174
slouken@7191
   175
void
slouken@6877
   176
X11_InitXinput2Multitouch(_THIS)
slouken@6877
   177
{
dimitris@6316
   178
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
dimitris@6316
   179
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
dimitris@6316
   180
    XIDeviceInfo *info;
dimitris@6316
   181
    int ndevices,i,j;
icculus@7836
   182
    info = X11_XIQueryDevice(data->display, XIAllMasterDevices, &ndevices);
dimitris@6316
   183
dimitris@6316
   184
    for (i = 0; i < ndevices; i++) {
dimitris@6316
   185
        XIDeviceInfo *dev = &info[i];
dimitris@6316
   186
        for (j = 0; j < dev->num_classes; j++) {
dimitris@6316
   187
            SDL_TouchID touchId;
dimitris@6316
   188
            XIAnyClassInfo *class = dev->classes[j];
dimitris@6316
   189
            XITouchClassInfo *t = (XITouchClassInfo*)class;
dimitris@6316
   190
gabomdq@7678
   191
            /* Only touch devices */
dimitris@6316
   192
            if (class->type != XITouchClass)
dimitris@6316
   193
                continue;
dimitris@6316
   194
dimitris@6316
   195
            touchId = t->sourceid;
dimitris@6316
   196
            if (!SDL_GetTouch(touchId)) {
slouken@6951
   197
                SDL_AddTouch(touchId, dev->name);
dimitris@6316
   198
            }
dimitris@6316
   199
        }
dimitris@6316
   200
    }
icculus@7836
   201
    X11_XIFreeDeviceInfo(info);
dimitris@6316
   202
#endif
dimitris@6316
   203
}
dimitris@6316
   204
slouken@7191
   205
void
slouken@6877
   206
X11_Xinput2SelectTouch(_THIS, SDL_Window *window)
slouken@6877
   207
{
dimitris@6316
   208
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
gabomdq@7679
   209
    SDL_VideoData *data = NULL;
gabomdq@7679
   210
    XIEventMask eventmask;
gabomdq@7679
   211
    unsigned char mask[3] = { 0,0,0 };
gabomdq@7679
   212
    SDL_WindowData *window_data = NULL;
gabomdq@7679
   213
    
icculus@6433
   214
    if (!X11_Xinput2IsMultitouchSupported()) {
icculus@6433
   215
        return;
icculus@6433
   216
    }
icculus@6433
   217
gabomdq@7679
   218
    data = (SDL_VideoData *) _this->driverdata;
gabomdq@7679
   219
    window_data = (SDL_WindowData*)window->driverdata;
dimitris@6316
   220
dimitris@6316
   221
    eventmask.deviceid = XIAllMasterDevices;
dimitris@6316
   222
    eventmask.mask_len = sizeof(mask);
dimitris@6316
   223
    eventmask.mask = mask;
dimitris@6316
   224
dimitris@6316
   225
    XISetMask(mask, XI_TouchBegin);
dimitris@6316
   226
    XISetMask(mask, XI_TouchUpdate);
dimitris@6316
   227
    XISetMask(mask, XI_TouchEnd);
slouken@7191
   228
icculus@7827
   229
    X11_XISelectEvents(data->display,window_data->xwindow,&eventmask,1);
dimitris@6316
   230
#endif
dimitris@6316
   231
}
dimitris@6316
   232
dimitris@6316
   233
slouken@7191
   234
int
slouken@6877
   235
X11_Xinput2IsInitialized()
slouken@6877
   236
{
icculus@6375
   237
#if SDL_VIDEO_DRIVER_X11_XINPUT2
dimitris@6316
   238
    return xinput2_initialized;
icculus@6375
   239
#else
icculus@6375
   240
    return 0;
icculus@6375
   241
#endif
dimitris@6316
   242
}
dimitris@6316
   243
dimitris@6316
   244
int
slouken@6877
   245
X11_Xinput2IsMultitouchSupported()
slouken@6877
   246
{
icculus@6375
   247
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
dimitris@6316
   248
    return xinput2_initialized && xinput2_multitouch_supported;
icculus@6375
   249
#else
icculus@6375
   250
    return 0;
icculus@6375
   251
#endif
dimitris@6316
   252
}
dimitris@6316
   253
dimitris@6316
   254
#endif /* SDL_VIDEO_DRIVER_X11 */
dimitris@6316
   255
dimitris@6316
   256
/* vi: set ts=4 sw=4 expandtab: */