src/video/x11/SDL_x11mouse.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 10 Dec 2019 13:09:52 -0800
changeset 13329 732a469df95c
parent 12968 0e3948762c96
permissions -rw-r--r--
Added support for the Razer Raion Fightpad for PS4
     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_zeroa(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 {
    76     SDL_Cursor *cursor;
    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 {
    83         SDL_OutOfMemory();
    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) {
    99         SDL_OutOfMemory();
   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 {
   206     SDL_Cursor *cursor;
   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 {
   231     SDL_Cursor *cursor;
   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     {
   293         SDL_VideoDevice *video = SDL_GetVideoDevice();
   294         Display *display = GetDisplay();
   295         SDL_Window *window;
   296         SDL_WindowData *data;
   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 {
   314     SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
   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
   339     if(X11_Xinput2IsInitialized())
   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 {
   373     SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
   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++) {
   388             SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(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
   423 X11_InitMouse(_THIS)
   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
   441 X11_QuitMouse(_THIS)
   442 {
   443     X11_DestroyEmptyCursor();
   444 }
   445 
   446 #endif /* SDL_VIDEO_DRIVER_X11 */
   447 
   448 /* vi: set ts=4 sw=4 expandtab: */