SDL  2.0
SDL_x11mouse.c
Go to the documentation of this file.
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 <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 
106  SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
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 */
145  SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
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_GetMouse
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:178
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_Point::x
int x
Definition: SDL_rect.h:50
image
GLeglImageOES image
Definition: SDL_opengl.h:2148
SDL_Mouse::WarpMouseGlobal
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
SDL_SYSTEM_CURSOR_HAND
Definition: SDL_mouse.h:59
SDL_Mouse::CaptureMouse
int(* CaptureMouse)(SDL_Window *window)
Definition: SDL_mouse_c.h:70
SDL_VideoDevice::driverdata
void * driverdata
Definition: SDL_sysvideo.h:381
SDL_x11video.h
mask
GLenum GLint GLuint mask
Definition: SDL_opengl_glext.h:657
SDL_x11mouse.h
SDL_Surface
A collection of pixels used in software blitting.
Definition: SDL_surface.h:70
SDL_Cursor
Definition: SDL_mouse_c.h:30
SDL_SYSTEM_CURSOR_WAITARROW
Definition: SDL_mouse.h:52
NULL
#define NULL
Definition: begin_code.h:167
surface
EGLSurface surface
Definition: eglext.h:248
SDL_SYSTEM_CURSOR_CROSSHAIR
Definition: SDL_mouse.h:51
SDL_SYSTEM_CURSOR_SIZENESW
Definition: SDL_mouse.h:54
SDL_BUTTON_RMASK
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:289
SDL_WindowData
Definition: SDL_androidwindow.h:38
red
const GLubyte GLuint red
Definition: SDL_glfuncs.h:79
SDL_Mouse::SetRelativeMouseMode
int(* SetRelativeMouseMode)(SDL_bool enabled)
Definition: SDL_mouse_c.h:67
SDL_VideoData::global_mouse_changed
SDL_bool global_mouse_changed
Definition: SDL_x11video.h:133
SDL_GetDisplayDriverData
void * SDL_GetDisplayDriverData(int displayIndex)
Definition: SDL_video.c:657
SDL_Cursor::driverdata
void * driverdata
Definition: SDL_mouse_c.h:33
SDL_GetNumVideoDisplays
#define SDL_GetNumVideoDisplays
Definition: SDL_dynapi_overrides.h:501
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1973
SDL_Window
The type used to identify a window.
Definition: SDL_sysvideo.h:73
alpha
GLfloat GLfloat GLfloat alpha
Definition: SDL_opengl_glext.h:412
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1573
SDL_Mouse::WarpMouse
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
color
GLuint color
Definition: SDL_opengl_glext.h:1148
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_SYSTEM_CURSOR_SIZEALL
Definition: SDL_mouse.h:57
SDL_VideoData::global_mouse_position
SDL_Point global_mouse_position
Definition: SDL_x11video.h:134
pixmap
EGLConfig struct EGLClientPixmapHI * pixmap
Definition: eglext.h:899
SDL_SystemCursor
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor().
Definition: SDL_mouse.h:46
SDL_assert.h
SDL_BUTTON_LMASK
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:287
SDL_SYSTEM_CURSOR_IBEAM
Definition: SDL_mouse.h:49
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
SDL_Mouse
Definition: SDL_mouse_c.h:43
SDL_Mouse::CreateSystemCursor
SDL_Cursor *(* CreateSystemCursor)(SDL_SystemCursor id)
Definition: SDL_mouse_c.h:49
SDL_VideoData::global_mouse_buttons
Uint32 global_mouse_buttons
Definition: SDL_x11video.h:135
SDL_PIXELFORMAT_ARGB8888
Definition: SDL_pixels.h:248
blue
GLbyte GLbyte blue
Definition: SDL_opengl_glext.h:379
green
GLbyte green
Definition: SDL_opengl_glext.h:379
hot_x
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
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_DisplayData
Definition: SDL_cocoamodes.h:26
SDL_VideoDevice
Definition: SDL_sysvideo.h:148
cursor
SDL_Cursor * cursor
Definition: testwm2.c:40
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_Mouse::ShowCursor
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1573
SDL_SYSTEM_CURSOR_NO
Definition: SDL_mouse.h:58
SDL_SetDefaultCursor
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:167
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_VideoData::display
struct wl_display * display
Definition: SDL_waylandvideo.h:50
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
SDL_Window::next
SDL_Window * next
Definition: SDL_sysvideo.h:114
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_SYSTEM_CURSOR_WAIT
Definition: SDL_mouse.h:50
SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_x11xinput2.h
SDL_Mouse::FreeCursor
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
SDL_Mouse::CreateCursor
SDL_Cursor *(* CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y)
Definition: SDL_mouse_c.h:46
SDL_Mouse::GetGlobalMouseState
Uint32(* GetGlobalMouseState)(int *x, int *y)
Definition: SDL_mouse_c.h:73
X11_InitMouse
void X11_InitMouse(_THIS)
SDL_SYSTEM_CURSOR_SIZEWE
Definition: SDL_mouse.h:55
enabled
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: SDL_opengl_glext.h:2479
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
SDL_GetVideoDevice
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:583
SDL_Unsupported
#define SDL_Unsupported()
Definition: SDL_error.h:53
SDL_SYSTEM_CURSOR_SIZENWSE
Definition: SDL_mouse.h:53
SDL_SYSTEM_CURSOR_ARROW
Definition: SDL_mouse.h:48
X11_QuitMouse
void X11_QuitMouse(_THIS)
SDL_SYSTEM_CURSOR_SIZENS
Definition: SDL_mouse.h:56
X11_Xinput2IsInitialized
int X11_Xinput2IsInitialized(void)
i
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
SDL_BUTTON_MMASK
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:288
SDL_Point::y
int y
Definition: SDL_rect.h:51
SDL_VideoData
Definition: SDL_androidvideo.h:36
SDL_VideoDevice::windows
SDL_Window * windows
Definition: SDL_sysvideo.h:317