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