SDL  2.0
SDL_x11mouse.c
Go to the documentation of this file.
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 <X11/cursorfont.h>
26 #include "SDL_assert.h"
27 #include "SDL_x11video.h"
28 #include "SDL_x11mouse.h"
29 #include "SDL_x11xinput2.h"
30 #include "../../events/SDL_mouse_c.h"
31 
32 
33 /* FIXME: Find a better place to put this... */
34 static Cursor x11_empty_cursor = None;
35 
36 static Display *
37 GetDisplay(void)
38 {
39  return ((SDL_VideoData *)SDL_GetVideoDevice()->driverdata)->display;
40 }
41 
42 static Cursor
43 X11_CreateEmptyCursor()
44 {
45  if (x11_empty_cursor == None) {
46  Display *display = GetDisplay();
47  char data[1];
48  XColor color;
49  Pixmap pixmap;
50 
51  SDL_zero(data);
52  color.red = color.green = color.blue = 0;
53  pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
54  data, 1, 1);
55  if (pixmap) {
56  x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
57  &color, &color, 0, 0);
58  X11_XFreePixmap(display, pixmap);
59  }
60  }
61  return x11_empty_cursor;
62 }
63 
64 static void
65 X11_DestroyEmptyCursor(void)
66 {
67  if (x11_empty_cursor != None) {
68  X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
69  x11_empty_cursor = None;
70  }
71 }
72 
73 static SDL_Cursor *
74 X11_CreateDefaultCursor()
75 {
77 
78  cursor = SDL_calloc(1, sizeof(*cursor));
79  if (cursor) {
80  /* None is used to indicate the default cursor */
81  cursor->driverdata = (void*)None;
82  } else {
84  }
85 
86  return cursor;
87 }
88 
89 #if SDL_VIDEO_DRIVER_X11_XCURSOR
90 static Cursor
91 X11_CreateXCursorCursor(SDL_Surface * surface, int hot_x, int hot_y)
92 {
93  Display *display = GetDisplay();
94  Cursor cursor = None;
95  XcursorImage *image;
96 
97  image = X11_XcursorImageCreate(surface->w, surface->h);
98  if (!image) {
100  return None;
101  }
102  image->xhot = hot_x;
103  image->yhot = hot_y;
104  image->delay = 0;
105 
107  SDL_assert(surface->pitch == surface->w * 4);
108  SDL_memcpy(image->pixels, surface->pixels, surface->h * surface->pitch);
109 
110  cursor = X11_XcursorImageLoadCursor(display, image);
111 
112  X11_XcursorImageDestroy(image);
113 
114  return cursor;
115 }
116 #endif /* SDL_VIDEO_DRIVER_X11_XCURSOR */
117 
118 static Cursor
119 X11_CreatePixmapCursor(SDL_Surface * surface, int hot_x, int hot_y)
120 {
121  Display *display = GetDisplay();
122  XColor fg, bg;
123  Cursor cursor = None;
124  Uint32 *ptr;
125  Uint8 *data_bits, *mask_bits;
126  Pixmap data_pixmap, mask_pixmap;
127  int x, y;
128  unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
129  unsigned int width_bytes = ((surface->w + 7) & ~7) / 8;
130 
131  data_bits = SDL_calloc(1, surface->h * width_bytes);
132  if (!data_bits) {
133  SDL_OutOfMemory();
134  return None;
135  }
136 
137  mask_bits = SDL_calloc(1, surface->h * width_bytes);
138  if (!mask_bits) {
139  SDL_free(data_bits);
140  SDL_OutOfMemory();
141  return None;
142  }
143 
144  /* Code below assumes ARGB pixel format */
146 
147  rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0;
148  for (y = 0; y < surface->h; ++y) {
149  ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
150  for (x = 0; x < surface->w; ++x) {
151  int alpha = (*ptr >> 24) & 0xff;
152  int red = (*ptr >> 16) & 0xff;
153  int green = (*ptr >> 8) & 0xff;
154  int blue = (*ptr >> 0) & 0xff;
155  if (alpha > 25) {
156  mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
157 
158  if ((red + green + blue) > 0x40) {
159  fgBits++;
160  rfg += red;
161  gfg += green;
162  bfg += blue;
163  data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
164  } else {
165  bgBits++;
166  rbg += red;
167  gbg += green;
168  bbg += blue;
169  }
170  }
171  ++ptr;
172  }
173  }
174 
175  if (fgBits) {
176  fg.red = rfg * 257 / fgBits;
177  fg.green = gfg * 257 / fgBits;
178  fg.blue = bfg * 257 / fgBits;
179  }
180  else fg.red = fg.green = fg.blue = 0;
181 
182  if (bgBits) {
183  bg.red = rbg * 257 / bgBits;
184  bg.green = gbg * 257 / bgBits;
185  bg.blue = bbg * 257 / bgBits;
186  }
187  else bg.red = bg.green = bg.blue = 0;
188 
189  data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
190  (char*)data_bits,
191  surface->w, surface->h);
192  mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
193  (char*)mask_bits,
194  surface->w, surface->h);
195  cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
196  &fg, &bg, hot_x, hot_y);
197  X11_XFreePixmap(display, data_pixmap);
198  X11_XFreePixmap(display, mask_pixmap);
199 
200  return cursor;
201 }
202 
203 static SDL_Cursor *
204 X11_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
205 {
207 
208  cursor = SDL_calloc(1, sizeof(*cursor));
209  if (cursor) {
210  Cursor x11_cursor = None;
211 
212 #if SDL_VIDEO_DRIVER_X11_XCURSOR
213  if (SDL_X11_HAVE_XCURSOR) {
214  x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
215  }
216 #endif
217  if (x11_cursor == None) {
218  x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
219  }
220  cursor->driverdata = (void*)x11_cursor;
221  } else {
222  SDL_OutOfMemory();
223  }
224 
225  return cursor;
226 }
227 
228 static SDL_Cursor *
229 X11_CreateSystemCursor(SDL_SystemCursor id)
230 {
232  unsigned int shape;
233 
234  switch(id)
235  {
236  default:
237  SDL_assert(0);
238  return NULL;
239  /* X Font Cursors reference: */
240  /* http://tronche.com/gui/x/xlib/appendix/b/ */
241  case SDL_SYSTEM_CURSOR_ARROW: shape = XC_left_ptr; break;
242  case SDL_SYSTEM_CURSOR_IBEAM: shape = XC_xterm; break;
243  case SDL_SYSTEM_CURSOR_WAIT: shape = XC_watch; break;
244  case SDL_SYSTEM_CURSOR_CROSSHAIR: shape = XC_tcross; break;
245  case SDL_SYSTEM_CURSOR_WAITARROW: shape = XC_watch; break;
246  case SDL_SYSTEM_CURSOR_SIZENWSE: shape = XC_fleur; break;
247  case SDL_SYSTEM_CURSOR_SIZENESW: shape = XC_fleur; break;
248  case SDL_SYSTEM_CURSOR_SIZEWE: shape = XC_sb_h_double_arrow; break;
249  case SDL_SYSTEM_CURSOR_SIZENS: shape = XC_sb_v_double_arrow; break;
250  case SDL_SYSTEM_CURSOR_SIZEALL: shape = XC_fleur; break;
251  case SDL_SYSTEM_CURSOR_NO: shape = XC_pirate; break;
252  case SDL_SYSTEM_CURSOR_HAND: shape = XC_hand2; break;
253  }
254 
255  cursor = SDL_calloc(1, sizeof(*cursor));
256  if (cursor) {
257  Cursor x11_cursor;
258 
259  x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
260 
261  cursor->driverdata = (void*)x11_cursor;
262  } else {
263  SDL_OutOfMemory();
264  }
265 
266  return cursor;
267 }
268 
269 static void
270 X11_FreeCursor(SDL_Cursor * cursor)
271 {
272  Cursor x11_cursor = (Cursor)cursor->driverdata;
273 
274  if (x11_cursor != None) {
275  X11_XFreeCursor(GetDisplay(), x11_cursor);
276  }
277  SDL_free(cursor);
278 }
279 
280 static int
281 X11_ShowCursor(SDL_Cursor * cursor)
282 {
283  Cursor x11_cursor = 0;
284 
285  if (cursor) {
286  x11_cursor = (Cursor)cursor->driverdata;
287  } else {
288  x11_cursor = X11_CreateEmptyCursor();
289  }
290 
291  /* FIXME: Is there a better way than this? */
292  {
294  Display *display = GetDisplay();
297 
298  for (window = video->windows; window; window = window->next) {
299  data = (SDL_WindowData *)window->driverdata;
300  if (x11_cursor != None) {
301  X11_XDefineCursor(display, data->xwindow, x11_cursor);
302  } else {
303  X11_XUndefineCursor(display, data->xwindow);
304  }
305  }
306  X11_XFlush(display);
307  }
308  return 0;
309 }
310 
311 static void
312 WarpMouseInternal(Window xwindow, const int x, const int y)
313 {
315  Display *display = videodata->display;
316  X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, x, y);
317  X11_XSync(display, False);
318  videodata->global_mouse_changed = SDL_TRUE;
319 }
320 
321 static void
322 X11_WarpMouse(SDL_Window * window, int x, int y)
323 {
324  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
325  WarpMouseInternal(data->xwindow, x, y);
326 }
327 
328 static int
329 X11_WarpMouseGlobal(int x, int y)
330 {
331  WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
332  return 0;
333 }
334 
335 static int
336 X11_SetRelativeMouseMode(SDL_bool enabled)
337 {
338 #if SDL_VIDEO_DRIVER_X11_XINPUT2
340  return 0;
341 #else
342  SDL_Unsupported();
343 #endif
344  return -1;
345 }
346 
347 static int
348 X11_CaptureMouse(SDL_Window *window)
349 {
350  Display *display = GetDisplay();
351 
352  if (window) {
353  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
354  const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
355  const int rc = X11_XGrabPointer(display, data->xwindow, False,
356  mask, GrabModeAsync, GrabModeAsync,
357  None, None, CurrentTime);
358  if (rc != GrabSuccess) {
359  return SDL_SetError("X server refused mouse capture");
360  }
361  } else {
362  X11_XUngrabPointer(display, CurrentTime);
363  }
364 
365  X11_XSync(display, False);
366 
367  return 0;
368 }
369 
370 static Uint32
371 X11_GetGlobalMouseState(int *x, int *y)
372 {
374  Display *display = GetDisplay();
375  const int num_screens = SDL_GetNumVideoDisplays();
376  int i;
377 
378  /* !!! FIXME: should we XSync() here first? */
379 
380 #if !SDL_VIDEO_DRIVER_X11_XINPUT2
381  videodata->global_mouse_changed = SDL_TRUE;
382 #endif
383 
384  /* check if we have this cached since XInput last saw the mouse move. */
385  /* !!! FIXME: can we just calculate this from XInput's events? */
386  if (videodata->global_mouse_changed) {
387  for (i = 0; i < num_screens; i++) {
389  if (data != NULL) {
390  Window root, child;
391  int rootx, rooty, winx, winy;
392  unsigned int mask;
393  if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
394  XWindowAttributes root_attrs;
395  Uint32 buttons = 0;
396  buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
397  buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
398  buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
399  /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
400  * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
401  *
402  * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
403  X11_XGetWindowAttributes(display, root, &root_attrs);
404  videodata->global_mouse_position.x = root_attrs.x + rootx;
405  videodata->global_mouse_position.y = root_attrs.y + rooty;
406  videodata->global_mouse_buttons = buttons;
407  videodata->global_mouse_changed = SDL_FALSE;
408  break;
409  }
410  }
411  }
412  }
413 
414  SDL_assert(!videodata->global_mouse_changed); /* The pointer wasn't on any X11 screen?! */
415 
416  *x = videodata->global_mouse_position.x;
417  *y = videodata->global_mouse_position.y;
418  return videodata->global_mouse_buttons;
419 }
420 
421 
422 void
424 {
425  SDL_Mouse *mouse = SDL_GetMouse();
426 
427  mouse->CreateCursor = X11_CreateCursor;
428  mouse->CreateSystemCursor = X11_CreateSystemCursor;
429  mouse->ShowCursor = X11_ShowCursor;
430  mouse->FreeCursor = X11_FreeCursor;
431  mouse->WarpMouse = X11_WarpMouse;
432  mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
433  mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
434  mouse->CaptureMouse = X11_CaptureMouse;
435  mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
436 
437  SDL_SetDefaultCursor(X11_CreateDefaultCursor());
438 }
439 
440 void
442 {
443  X11_DestroyEmptyCursor();
444 }
445 
446 #endif /* SDL_VIDEO_DRIVER_X11 */
447 
448 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:114
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:112
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
GLeglImageOES image
Definition: SDL_opengl.h:2148
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:289
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
int(* SetRelativeMouseMode)(SDL_bool enabled)
Definition: SDL_mouse_c.h:67
struct wl_display * display
EGLSurface surface
Definition: eglext.h:248
A collection of pixels used in software blitting.
Definition: SDL_surface.h:69
GLbyte green
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
Uint32(* GetGlobalMouseState)(int *x, int *y)
Definition: SDL_mouse_c.h:73
void X11_InitMouse(_THIS)
uint32_t Uint32
Definition: SDL_stdinc.h:181
SDL_bool global_mouse_changed
Definition: SDL_x11video.h:133
GLfloat GLfloat GLfloat alpha
void * SDL_GetDisplayDriverData(int displayIndex)
Definition: SDL_video.c:660
SDL_Point global_mouse_position
Definition: SDL_x11video.h:134
const GLubyte GLuint red
Definition: SDL_glfuncs.h:79
#define SDL_GetNumVideoDisplays
int x
Definition: SDL_rect.h:50
int(* CaptureMouse)(SDL_Window *window)
Definition: SDL_mouse_c.h:70
SDL_Cursor *(* CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y)
Definition: SDL_mouse_c.h:46
#define SDL_memcpy
int y
Definition: SDL_rect.h:51
void * pixels
Definition: SDL_surface.h:75
EGLConfig struct EGLClientPixmapHI * pixmap
Definition: eglext.h:899
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
GLenum GLint GLuint mask
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor().
Definition: SDL_mouse.h:46
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:287
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
SDL_Window * windows
Definition: SDL_sysvideo.h:313
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:101
void X11_QuitMouse(_THIS)
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
GLenum GLenum GLsizei const GLuint GLboolean enabled
SDL_Cursor * cursor
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
SDL_PixelFormat * format
Definition: SDL_surface.h:72
#define SDL_SetError
#define SDL_calloc
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
Uint32 global_mouse_buttons
Definition: SDL_x11video.h:135
The type used to identify a window.
Definition: SDL_sysvideo.h:73
GLbyte GLbyte blue
GLuint color
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:586
void * driverdata
Definition: SDL_sysvideo.h:111
int X11_Xinput2IsInitialized(void)
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:288
SDL_Cursor *(* CreateSystemCursor)(SDL_SystemCursor id)
Definition: SDL_mouse_c.h:49
void * driverdata
Definition: SDL_mouse_c.h:33
#define SDL_Unsupported()
Definition: SDL_error.h:53
int uint32_t uint32_t uint32_t uint32_t uint32_t int drmModeModeInfoPtr mode int uint32_t uint32_t uint32_t uint32_t int32_t hot_x
Definition: SDL_kmsdrmsym.h:55