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