2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_X11
25 #include <X11/cursorfont.h>
26 #include "SDL_x11video.h"
27 #include "SDL_x11mouse.h"
28 #include "SDL_x11xinput2.h"
29 #include "../../events/SDL_mouse_c.h"
32 /* FIXME: Find a better place to put this... */
33 static Cursor x11_empty_cursor = None;
38 return ((SDL_VideoData *)SDL_GetVideoDevice()->driverdata)->display;
42 X11_CreateEmptyCursor()
44 if (x11_empty_cursor == None) {
45 Display *display = GetDisplay();
51 color.red = color.green = color.blue = 0;
52 pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
55 x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
56 &color, &color, 0, 0);
57 X11_XFreePixmap(display, pixmap);
60 return x11_empty_cursor;
64 X11_DestroyEmptyCursor(void)
66 if (x11_empty_cursor != None) {
67 X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
68 x11_empty_cursor = None;
73 X11_CreateDefaultCursor()
77 cursor = SDL_calloc(1, sizeof(*cursor));
79 /* None is used to indicate the default cursor */
80 cursor->driverdata = (void*)None;
88 #if SDL_VIDEO_DRIVER_X11_XCURSOR
90 X11_CreateXCursorCursor(SDL_Surface * surface, int hot_x, int hot_y)
92 Display *display = GetDisplay();
96 image = X11_XcursorImageCreate(surface->w, surface->h);
105 SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
106 SDL_assert(surface->pitch == surface->w * 4);
107 SDL_memcpy(image->pixels, surface->pixels, surface->h * surface->pitch);
109 cursor = X11_XcursorImageLoadCursor(display, image);
111 X11_XcursorImageDestroy(image);
115 #endif /* SDL_VIDEO_DRIVER_X11_XCURSOR */
118 X11_CreatePixmapCursor(SDL_Surface * surface, int hot_x, int hot_y)
120 Display *display = GetDisplay();
122 Cursor cursor = None;
124 Uint8 *data_bits, *mask_bits;
125 Pixmap data_pixmap, mask_pixmap;
127 unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
128 unsigned int width_bytes = ((surface->w + 7) & ~7) / 8;
130 data_bits = SDL_calloc(1, surface->h * width_bytes);
136 mask_bits = SDL_calloc(1, surface->h * width_bytes);
143 /* Code below assumes ARGB pixel format */
144 SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
146 rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0;
147 for (y = 0; y < surface->h; ++y) {
148 ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
149 for (x = 0; x < surface->w; ++x) {
150 int alpha = (*ptr >> 24) & 0xff;
151 int red = (*ptr >> 16) & 0xff;
152 int green = (*ptr >> 8) & 0xff;
153 int blue = (*ptr >> 0) & 0xff;
155 mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
157 if ((red + green + blue) > 0x40) {
162 data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
175 fg.red = rfg * 257 / fgBits;
176 fg.green = gfg * 257 / fgBits;
177 fg.blue = bfg * 257 / fgBits;
179 else fg.red = fg.green = fg.blue = 0;
182 bg.red = rbg * 257 / bgBits;
183 bg.green = gbg * 257 / bgBits;
184 bg.blue = bbg * 257 / bgBits;
186 else bg.red = bg.green = bg.blue = 0;
188 data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
190 surface->w, surface->h);
191 mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
193 surface->w, surface->h);
194 cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
195 &fg, &bg, hot_x, hot_y);
196 X11_XFreePixmap(display, data_pixmap);
197 X11_XFreePixmap(display, mask_pixmap);
203 X11_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
207 cursor = SDL_calloc(1, sizeof(*cursor));
209 Cursor x11_cursor = None;
211 #if SDL_VIDEO_DRIVER_X11_XCURSOR
212 if (SDL_X11_HAVE_XCURSOR) {
213 x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
216 if (x11_cursor == None) {
217 x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
219 cursor->driverdata = (void*)x11_cursor;
228 X11_CreateSystemCursor(SDL_SystemCursor id)
238 /* X Font Cursors reference: */
239 /* http://tronche.com/gui/x/xlib/appendix/b/ */
240 case SDL_SYSTEM_CURSOR_ARROW: shape = XC_left_ptr; break;
241 case SDL_SYSTEM_CURSOR_IBEAM: shape = XC_xterm; break;
242 case SDL_SYSTEM_CURSOR_WAIT: shape = XC_watch; break;
243 case SDL_SYSTEM_CURSOR_CROSSHAIR: shape = XC_tcross; break;
244 case SDL_SYSTEM_CURSOR_WAITARROW: shape = XC_watch; break;
245 case SDL_SYSTEM_CURSOR_SIZENWSE: shape = XC_fleur; break;
246 case SDL_SYSTEM_CURSOR_SIZENESW: shape = XC_fleur; break;
247 case SDL_SYSTEM_CURSOR_SIZEWE: shape = XC_sb_h_double_arrow; break;
248 case SDL_SYSTEM_CURSOR_SIZENS: shape = XC_sb_v_double_arrow; break;
249 case SDL_SYSTEM_CURSOR_SIZEALL: shape = XC_fleur; break;
250 case SDL_SYSTEM_CURSOR_NO: shape = XC_pirate; break;
251 case SDL_SYSTEM_CURSOR_HAND: shape = XC_hand2; break;
254 cursor = SDL_calloc(1, sizeof(*cursor));
258 x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
260 cursor->driverdata = (void*)x11_cursor;
269 X11_FreeCursor(SDL_Cursor * cursor)
271 Cursor x11_cursor = (Cursor)cursor->driverdata;
273 if (x11_cursor != None) {
274 X11_XFreeCursor(GetDisplay(), x11_cursor);
280 X11_ShowCursor(SDL_Cursor * cursor)
282 Cursor x11_cursor = 0;
285 x11_cursor = (Cursor)cursor->driverdata;
287 x11_cursor = X11_CreateEmptyCursor();
290 /* FIXME: Is there a better way than this? */
292 SDL_VideoDevice *video = SDL_GetVideoDevice();
293 Display *display = GetDisplay();
295 SDL_WindowData *data;
297 for (window = video->windows; window; window = window->next) {
298 data = (SDL_WindowData *)window->driverdata;
299 if (x11_cursor != None) {
300 X11_XDefineCursor(display, data->xwindow, x11_cursor);
302 X11_XUndefineCursor(display, data->xwindow);
311 WarpMouseInternal(Window xwindow, const int x, const int y)
313 SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
314 Display *display = videodata->display;
315 X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, x, y);
316 X11_XSync(display, False);
317 videodata->global_mouse_changed = SDL_TRUE;
321 X11_WarpMouse(SDL_Window * window, int x, int y)
323 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
324 WarpMouseInternal(data->xwindow, x, y);
328 X11_WarpMouseGlobal(int x, int y)
330 WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
335 X11_SetRelativeMouseMode(SDL_bool enabled)
337 #if SDL_VIDEO_DRIVER_X11_XINPUT2
338 if(X11_Xinput2IsInitialized())
347 X11_CaptureMouse(SDL_Window *window)
349 Display *display = GetDisplay();
352 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
353 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
354 const int rc = X11_XGrabPointer(display, data->xwindow, False,
355 mask, GrabModeAsync, GrabModeAsync,
356 None, None, CurrentTime);
357 if (rc != GrabSuccess) {
358 return SDL_SetError("X server refused mouse capture");
361 X11_XUngrabPointer(display, CurrentTime);
364 X11_XSync(display, False);
370 X11_GetGlobalMouseState(int *x, int *y)
372 SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
373 Display *display = GetDisplay();
374 const int num_screens = SDL_GetNumVideoDisplays();
377 /* !!! FIXME: should we XSync() here first? */
379 #if !SDL_VIDEO_DRIVER_X11_XINPUT2
380 videodata->global_mouse_changed = SDL_TRUE;
383 /* check if we have this cached since XInput last saw the mouse move. */
384 /* !!! FIXME: can we just calculate this from XInput's events? */
385 if (videodata->global_mouse_changed) {
386 for (i = 0; i < num_screens; i++) {
387 SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
390 int rootx, rooty, winx, winy;
392 if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
393 XWindowAttributes root_attrs;
395 buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
396 buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
397 buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
398 /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
399 * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
401 * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
402 X11_XGetWindowAttributes(display, root, &root_attrs);
403 videodata->global_mouse_position.x = root_attrs.x + rootx;
404 videodata->global_mouse_position.y = root_attrs.y + rooty;
405 videodata->global_mouse_buttons = buttons;
406 videodata->global_mouse_changed = SDL_FALSE;
413 SDL_assert(!videodata->global_mouse_changed); /* The pointer wasn't on any X11 screen?! */
415 *x = videodata->global_mouse_position.x;
416 *y = videodata->global_mouse_position.y;
417 return videodata->global_mouse_buttons;
424 SDL_Mouse *mouse = SDL_GetMouse();
426 mouse->CreateCursor = X11_CreateCursor;
427 mouse->CreateSystemCursor = X11_CreateSystemCursor;
428 mouse->ShowCursor = X11_ShowCursor;
429 mouse->FreeCursor = X11_FreeCursor;
430 mouse->WarpMouse = X11_WarpMouse;
431 mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
432 mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
433 mouse->CaptureMouse = X11_CaptureMouse;
434 mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
436 SDL_SetDefaultCursor(X11_CreateDefaultCursor());
442 X11_DestroyEmptyCursor();
445 #endif /* SDL_VIDEO_DRIVER_X11 */
447 /* vi: set ts=4 sw=4 expandtab: */