src/video/x11/SDL_x11xinput2.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 23 May 2019 11:32:36 -0700
changeset 12753 ba1a66b57385
parent 12503 806492103856
permissions -rw-r--r--
Return an error if both mouse relative mode and mouse warping are unavailable, instead of asserting.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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             SDL_TouchDeviceType touchType;
   248             XIAnyClassInfo *class = dev->classes[j];
   249             XITouchClassInfo *t = (XITouchClassInfo*)class;
   250 
   251             /* Only touch devices */
   252             if (class->type != XITouchClass)
   253                 continue;
   254 
   255             if (t->mode == XIDependentTouch) {
   256                 touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
   257             } else { /* XIDirectTouch */
   258                 touchType = SDL_TOUCH_DEVICE_DIRECT;
   259             }
   260 
   261             touchId = t->sourceid;
   262             SDL_AddTouch(touchId, touchType, dev->name);
   263         }
   264     }
   265     X11_XIFreeDeviceInfo(info);
   266 #endif
   267 }
   268 
   269 void
   270 X11_Xinput2SelectTouch(_THIS, SDL_Window *window)
   271 {
   272 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   273     SDL_VideoData *data = NULL;
   274     XIEventMask eventmask;
   275     unsigned char mask[3] = { 0,0,0 };
   276     SDL_WindowData *window_data = NULL;
   277     
   278     if (!X11_Xinput2IsMultitouchSupported()) {
   279         return;
   280     }
   281 
   282     data = (SDL_VideoData *) _this->driverdata;
   283     window_data = (SDL_WindowData*)window->driverdata;
   284 
   285     eventmask.deviceid = XIAllMasterDevices;
   286     eventmask.mask_len = sizeof(mask);
   287     eventmask.mask = mask;
   288 
   289     XISetMask(mask, XI_TouchBegin);
   290     XISetMask(mask, XI_TouchUpdate);
   291     XISetMask(mask, XI_TouchEnd);
   292 
   293     X11_XISelectEvents(data->display,window_data->xwindow,&eventmask,1);
   294 #endif
   295 }
   296 
   297 
   298 int
   299 X11_Xinput2IsInitialized()
   300 {
   301 #if SDL_VIDEO_DRIVER_X11_XINPUT2
   302     return xinput2_initialized;
   303 #else
   304     return 0;
   305 #endif
   306 }
   307 
   308 int
   309 X11_Xinput2IsMultitouchSupported()
   310 {
   311 #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
   312     return xinput2_initialized && xinput2_multitouch_supported;
   313 #else
   314     return 0;
   315 #endif
   316 }
   317 
   318 #endif /* SDL_VIDEO_DRIVER_X11 */
   319 
   320 /* vi: set ts=4 sw=4 expandtab: */