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