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: */