SDL  2.0
SDL_x11mouse.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 X11_WarpMouse(SDL_Window * window, int x, int y)
313 {
314  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
315  Display *display = data->videodata->display;
316 
317  X11_XWarpPointer(display, None, data->xwindow, 0, 0, 0, 0, x, y);
318  X11_XSync(display, False);
319 }
320 
321 static int
322 X11_WarpMouseGlobal(int x, int y)
323 {
324  Display *display = GetDisplay();
325 
326  X11_XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, x, y);
327  X11_XSync(display, False);
328  return 0;
329 }
330 
331 static int
332 X11_SetRelativeMouseMode(SDL_bool enabled)
333 {
334 #if SDL_VIDEO_DRIVER_X11_XINPUT2
336  return 0;
337 #else
338  SDL_Unsupported();
339 #endif
340  return -1;
341 }
342 
343 static int
344 X11_CaptureMouse(SDL_Window *window)
345 {
346  Display *display = GetDisplay();
347 
348  if (window) {
349  SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
350  const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
351  const int rc = X11_XGrabPointer(display, data->xwindow, False,
352  mask, GrabModeAsync, GrabModeAsync,
353  None, None, CurrentTime);
354  if (rc != GrabSuccess) {
355  return SDL_SetError("X server refused mouse capture");
356  }
357  } else {
358  X11_XUngrabPointer(display, CurrentTime);
359  }
360 
361  X11_XSync(display, False);
362 
363  return 0;
364 }
365 
366 static Uint32
367 X11_GetGlobalMouseState(int *x, int *y)
368 {
370  Display *display = GetDisplay();
371  const int num_screens = SDL_GetNumVideoDisplays();
372  int i;
373 
374  /* !!! FIXME: should we XSync() here first? */
375 
376 #if !SDL_VIDEO_DRIVER_X11_XINPUT2
377  videodata->global_mouse_changed = SDL_TRUE;
378 #endif
379 
380  /* check if we have this cached since XInput last saw the mouse move. */
381  /* !!! FIXME: can we just calculate this from XInput's events? */
382  if (videodata->global_mouse_changed) {
383  for (i = 0; i < num_screens; i++) {
385  if (data != NULL) {
386  Window root, child;
387  int rootx, rooty, winx, winy;
388  unsigned int mask;
389  if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
390  XWindowAttributes root_attrs;
391  Uint32 buttons = 0;
392  buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
393  buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
394  buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
395  /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
396  * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
397  *
398  * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
399  X11_XGetWindowAttributes(display, root, &root_attrs);
400  videodata->global_mouse_position.x = root_attrs.x + rootx;
401  videodata->global_mouse_position.y = root_attrs.y + rooty;
402  videodata->global_mouse_buttons = buttons;
403  videodata->global_mouse_changed = SDL_FALSE;
404  break;
405  }
406  }
407  }
408  }
409 
410  SDL_assert(!videodata->global_mouse_changed); /* The pointer wasn't on any X11 screen?! */
411 
412  *x = videodata->global_mouse_position.x;
413  *y = videodata->global_mouse_position.y;
414  return videodata->global_mouse_buttons;
415 }
416 
417 
418 void
420 {
421  SDL_Mouse *mouse = SDL_GetMouse();
422 
423  mouse->CreateCursor = X11_CreateCursor;
424  mouse->CreateSystemCursor = X11_CreateSystemCursor;
425  mouse->ShowCursor = X11_ShowCursor;
426  mouse->FreeCursor = X11_FreeCursor;
427  mouse->WarpMouse = X11_WarpMouse;
428  mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
429  mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
430  mouse->CaptureMouse = X11_CaptureMouse;
431  mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
432 
433  SDL_SetDefaultCursor(X11_CreateDefaultCursor());
434 }
435 
436 void
438 {
439  X11_DestroyEmptyCursor();
440 }
441 
442 #endif /* SDL_VIDEO_DRIVER_X11 */
443 
444 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:112
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:66
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
GLeglImageOES image
Definition: SDL_opengl.h:2141
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:289
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
int(* SetRelativeMouseMode)(SDL_bool enabled)
Definition: SDL_mouse_c.h:67
struct wl_display * display
static SDL_Window * window
A collection of pixels used in software blitting.
Definition: SDL_surface.h:69
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
Uint32(* GetGlobalMouseState)(int *x, int *y)
Definition: SDL_mouse_c.h:73
void X11_InitMouse(_THIS)
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:161
SDL_bool global_mouse_changed
Definition: SDL_x11video.h:129
GLfloat GLfloat GLfloat alpha
void * SDL_GetDisplayDriverData(int displayIndex)
Definition: SDL_video.c:645
SDL_Point global_mouse_position
Definition: SDL_x11video.h:130
const GLubyte GLuint red
Definition: SDL_glfuncs.h:78
#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
void * SDL_calloc(size_t nmemb, size_t size)
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
int y
Definition: SDL_rect.h:51
void * pixels
Definition: SDL_surface.h:75
#define _THIS
struct SDL_VideoData * videodata
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
void SDL_free(void *mem)
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor().
Definition: SDL_mouse.h:46
GLbyte GLbyte blue
#define SDL_zero(x)
Definition: SDL_stdinc.h:361
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:287
SDL_Window * windows
Definition: SDL_sysvideo.h:294
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:55
void X11_QuitMouse(_THIS)
GLenum GLint GLuint mask
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:167
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
SDL_PixelFormat * format
Definition: SDL_surface.h:72
#define SDL_SetError
Uint32 global_mouse_buttons
Definition: SDL_x11video.h:131
The type used to identify a window.
Definition: SDL_sysvideo.h:71
GLuint color
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
GLbyte green
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:571
void * driverdata
Definition: SDL_sysvideo.h:109
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