src/video/x11/SDL_x11xinput2.c
author Ryan C. Gordon
Mon, 17 Sep 2012 19:25:42 -0400
changeset 6433 8bbb5ff8788f
parent 6432 de49d33b829e
child 6877 7287d385e6b3
permissions -rw-r--r--
Don't call XInput2 multitouch APIs if they aren't supported.

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