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 {
369  Display *display = GetDisplay();
370  const int num_screens = SDL_GetNumVideoDisplays();
371  int i;
372 
373  /* !!! FIXME: should we XSync() here first? */
374 
375  for (i = 0; i < num_screens; i++) {
377  if (data != NULL) {
378  Window root, child;
379  int rootx, rooty, winx, winy;
380  unsigned int mask;
381  if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
382  XWindowAttributes root_attrs;
383  Uint32 retval = 0;
384  retval |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
385  retval |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
386  retval |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
387  /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
388  * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
389  *
390  * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
391  X11_XGetWindowAttributes(display, root, &root_attrs);
392  *x = root_attrs.x + rootx;
393  *y = root_attrs.y + rooty;
394  return retval;
395  }
396  }
397  }
398 
399  SDL_assert(0 && "The pointer wasn't on any X11 screen?!");
400 
401  return 0;
402 }
403 
404 
405 void
407 {
408  SDL_Mouse *mouse = SDL_GetMouse();
409 
410  mouse->CreateCursor = X11_CreateCursor;
411  mouse->CreateSystemCursor = X11_CreateSystemCursor;
412  mouse->ShowCursor = X11_ShowCursor;
413  mouse->FreeCursor = X11_FreeCursor;
414  mouse->WarpMouse = X11_WarpMouse;
415  mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
416  mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
417  mouse->CaptureMouse = X11_CaptureMouse;
418  mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
419 
420  SDL_SetDefaultCursor(X11_CreateDefaultCursor());
421 }
422 
423 void
425 {
426  X11_DestroyEmptyCursor();
427 }
428 
429 #endif /* SDL_VIDEO_DRIVER_X11 */
430 
431 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:109
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:66
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:155
GLeglImageOES image
Definition: SDL_opengl.h:2141
#define SDL_BUTTON_RMASK
Definition: SDL_mouse.h:287
int(* SetRelativeMouseMode)(SDL_bool enabled)
Definition: SDL_mouse_c.h:67
struct wl_display * display
SDL_Window * window
GLbyte green
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)
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
void * SDL_GetDisplayDriverData(int displayIndex)
Definition: SDL_video.c:647
#define SDL_GetNumVideoDisplays
SDL_bool
Definition: SDL_stdinc.h:126
int(* CaptureMouse)(SDL_Window *window)
Definition: SDL_mouse_c.h:70
SDL_bool retval
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)
GLbyte GLbyte blue
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:139
void SDL_free(void *mem)
SDL_SystemCursor
Cursor types for SDL_CreateSystemCursor.
Definition: SDL_mouse.h:46
#define SDL_zero(x)
Definition: SDL_stdinc.h:355
#define SDL_BUTTON_LMASK
Definition: SDL_mouse.h:285
SDL_Window * windows
Definition: SDL_sysvideo.h:281
SDL_Cursor * cursor
Definition: testwm2.c:40
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
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
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:42
#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_PixelFormat * format
Definition: SDL_surface.h:72
#define SDL_SetError
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
The type used to identify a window.
Definition: SDL_sysvideo.h:71
const GLubyte GLuint red
Definition: SDL_glfuncs.h:78
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:573
void * driverdata
Definition: SDL_sysvideo.h:106
int X11_Xinput2IsInitialized(void)
#define SDL_BUTTON_MMASK
Definition: SDL_mouse.h:286
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