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