src/video/x11/SDL_x11xinput2.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Jul 2018 19:42:08 -0400
changeset 12069 317db3d3712c
parent 12029 dfebed374d73
child 12404 eb60e952b13f
permissions -rw-r--r--
Backed out changeset dfebed374d73.

This change isn't correct. See comments in Bugzilla #4183.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 
    77 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
    78 static void
    79 xinput2_normalize_touch_coordinates(SDL_VideoData *videodata, Window window,
    80     double in_x, double in_y, float *out_x, float *out_y)
    81 {
    82     int i;
    83     for (i = 0; i < videodata->numwindows; i++) {
    84         SDL_WindowData *d = videodata->windowlist[i];
    85         if (d->xwindow == window) {
    86             if (d->window->w == 1) {
    87                 *out_x = 0.5f;
    88             } else {
    89                 *out_x = in_x / (d->window->w - 1);
    90             }
    91             if (d->window->h == 1) {
    92                 *out_y = 0.5f;
    93             } else {
    94                 *out_y = in_y / (d->window->h - 1);
    95             }
    96             return;
    97         }
    98     }
    99     // couldn't find the window...
   100     *out_x = in_x;
   101     *out_y = in_y;
   102 }
   103 #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
   104 
   105 #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
   106 
   107 void
   108 X11_InitXinput2(_THIS)
   109 {
   110 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   111     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   112 
   113     int version = 0;
   114     XIEventMask eventmask;
   115     unsigned char mask[3] = { 0,0,0 };
   116     int event, err;
   117 
   118     /*
   119     * Initialize XInput 2
   120     * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
   121     * to inform Xserver what version of Xinput we support.The server will store the version we support.
   122     * "As XI2 progresses it becomes important that you use this call as the server may treat the client
   123     * differently depending on the supported version".
   124     *
   125     * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
   126     */
   127     if (!SDL_X11_HAVE_XINPUT2 ||
   128         !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
   129         return; /* X server does not have XInput at all */
   130     }
   131 
   132     /* We need at least 2.2 for Multitouch, 2.0 otherwise. */
   133     version = query_xinput2_version(data->display, 2, 2);
   134     if (!xinput2_version_atleast(version, 2, 0)) {
   135         return; /* X server does not support the version we want at all. */
   136     }
   137 
   138     xinput2_initialized = 1;
   139 
   140 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH  /* Multitouch needs XInput 2.2 */
   141     xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
   142 #endif
   143 
   144     /* Enable  Raw motion events for this display */
   145     eventmask.deviceid = XIAllMasterDevices;
   146     eventmask.mask_len = sizeof(mask);
   147     eventmask.mask = mask;
   148 
   149     XISetMask(mask, XI_RawMotion);
   150     XISetMask(mask, XI_RawButtonPress);
   151     XISetMask(mask, XI_RawButtonRelease);
   152 
   153     if (X11_XISelectEvents(data->display,DefaultRootWindow(data->display),&eventmask,1) != Success) {
   154         return;
   155     }
   156 #endif
   157 }
   158 
   159 int
   160 X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
   161 {
   162 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   163     if(cookie->extension != xinput2_opcode) {
   164         return 0;
   165     }
   166     switch(cookie->evtype) {
   167         case XI_RawMotion: {
   168             const XIRawEvent *rawev = (const XIRawEvent*)cookie->data;
   169             SDL_Mouse *mouse = SDL_GetMouse();
   170             double relative_coords[2];
   171             static Time prev_time = 0;
   172             static double prev_rel_coords[2];
   173 
   174             videodata->global_mouse_changed = SDL_TRUE;
   175 
   176             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   177                 return 0;
   178             }
   179 
   180             parse_valuators(rawev->raw_values,rawev->valuators.mask,
   181                             rawev->valuators.mask_len,relative_coords,2);
   182 
   183             if ((rawev->time == prev_time) && (relative_coords[0] == prev_rel_coords[0]) && (relative_coords[1] == prev_rel_coords[1])) {
   184                 return 0;  /* duplicate event, drop it. */
   185             }
   186 
   187             SDL_SendMouseMotion(mouse->focus,mouse->mouseID,1,(int)relative_coords[0],(int)relative_coords[1]);
   188             prev_rel_coords[0] = relative_coords[0];
   189             prev_rel_coords[1] = relative_coords[1];
   190             prev_time = rawev->time;
   191             return 1;
   192             }
   193             break;
   194 
   195         case XI_RawButtonPress:
   196         case XI_RawButtonRelease:
   197             videodata->global_mouse_changed = SDL_TRUE;
   198             break;
   199 
   200 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   201         case XI_TouchBegin: {
   202             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   203             float x, y;
   204             xinput2_normalize_touch_coordinates(videodata, xev->event,
   205                                   xev->event_x, xev->event_y, &x, &y);
   206             SDL_SendTouch(xev->sourceid,xev->detail, SDL_TRUE, x, y, 1.0);
   207             return 1;
   208             }
   209             break;
   210         case XI_TouchEnd: {
   211             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   212             float x, y;
   213             xinput2_normalize_touch_coordinates(videodata, xev->event,
   214                                   xev->event_x, xev->event_y, &x, &y);
   215             SDL_SendTouch(xev->sourceid,xev->detail, SDL_FALSE, x, y, 1.0);
   216             return 1;
   217             }
   218             break;
   219         case XI_TouchUpdate: {
   220             const XIDeviceEvent *xev = (const XIDeviceEvent *) cookie->data;
   221             float x, y;
   222             xinput2_normalize_touch_coordinates(videodata, xev->event,
   223                                   xev->event_x, xev->event_y, &x, &y);
   224             SDL_SendTouchMotion(xev->sourceid,xev->detail, x, y, 1.0);
   225             return 1;
   226             }
   227             break;
   228 #endif
   229     }
   230 #endif
   231     return 0;
   232 }
   233 
   234 void
   235 X11_InitXinput2Multitouch(_THIS)
   236 {
   237 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   238     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   239     XIDeviceInfo *info;
   240     int ndevices,i,j;
   241     info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
   242 
   243     for (i = 0; i < ndevices; i++) {
   244         XIDeviceInfo *dev = &info[i];
   245         for (j = 0; j < dev->num_classes; j++) {
   246             SDL_TouchID touchId;
   247             XIAnyClassInfo *class = dev->classes[j];
   248             XITouchClassInfo *t = (XITouchClassInfo*)class;
   249 
   250             /* Only touch devices */
   251             if (class->type != XITouchClass)
   252                 continue;
   253 
   254             touchId = t->sourceid;
   255             SDL_AddTouch(touchId, dev->name);
   256         }
   257     }
   258     X11_XIFreeDeviceInfo(info);
   259 #endif
   260 }
   261 
   262 void
   263 X11_Xinput2SelectTouch(_THIS, SDL_Window *window)
   264 {
   265 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   266     SDL_VideoData *data = NULL;
   267     XIEventMask eventmask;
   268     unsigned char mask[3] = { 0,0,0 };
   269     SDL_WindowData *window_data = NULL;
   270     
   271     if (!X11_Xinput2IsMultitouchSupported()) {
   272         return;
   273     }
   274 
   275     data = (SDL_VideoData *) _this->driverdata;
   276     window_data = (SDL_WindowData*)window->driverdata;
   277 
   278     eventmask.deviceid = XIAllMasterDevices;
   279     eventmask.mask_len = sizeof(mask);
   280     eventmask.mask = mask;
   281 
   282     XISetMask(mask, XI_TouchBegin);
   283     XISetMask(mask, XI_TouchUpdate);
   284     XISetMask(mask, XI_TouchEnd);
   285 
   286     X11_XISelectEvents(data->display,window_data->xwindow,&eventmask,1);
   287 #endif
   288 }
   289 
   290 
   291 int
   292 X11_Xinput2IsInitialized()
   293 {
   294 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   295     return xinput2_initialized;
   296 #else
   297     return 0;
   298 #endif
   299 }
   300 
   301 int
   302 X11_Xinput2IsMultitouchSupported()
   303 {
   304 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   305     return xinput2_initialized && xinput2_multitouch_supported;
   306 #else
   307     return 0;
   308 #endif
   309 }
   310 
   311 #endif /* SDL_VIDEO_DRIVER_X11 */
   312 
   313 /* vi: set ts=4 sw=4 expandtab: */