sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_x11xinput2.c (11181B)


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