sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_x11mouse.c (13712B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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_x11video.h"
     27 #include "SDL_x11mouse.h"
     28 #include "SDL_x11xinput2.h"
     29 #include "../../events/SDL_mouse_c.h"
     30 
     31 
     32 /* FIXME: Find a better place to put this... */
     33 static Cursor x11_empty_cursor = None;
     34 
     35 static Display *
     36 GetDisplay(void)
     37 {
     38     return ((SDL_VideoData *)SDL_GetVideoDevice()->driverdata)->display;
     39 }
     40 
     41 static Cursor
     42 X11_CreateEmptyCursor()
     43 {
     44     if (x11_empty_cursor == None) {
     45         Display *display = GetDisplay();
     46         char data[1];
     47         XColor color;
     48         Pixmap pixmap;
     49 
     50         SDL_zeroa(data);
     51         color.red = color.green = color.blue = 0;
     52         pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
     53                                        data, 1, 1);
     54         if (pixmap) {
     55             x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
     56                                                    &color, &color, 0, 0);
     57             X11_XFreePixmap(display, pixmap);
     58         }
     59     }
     60     return x11_empty_cursor;
     61 }
     62 
     63 static void
     64 X11_DestroyEmptyCursor(void)
     65 {
     66     if (x11_empty_cursor != None) {
     67         X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
     68         x11_empty_cursor = None;
     69     }
     70 }
     71 
     72 static SDL_Cursor *
     73 X11_CreateDefaultCursor()
     74 {
     75     SDL_Cursor *cursor;
     76 
     77     cursor = SDL_calloc(1, sizeof(*cursor));
     78     if (cursor) {
     79         /* None is used to indicate the default cursor */
     80         cursor->driverdata = (void*)None;
     81     } else {
     82         SDL_OutOfMemory();
     83     }
     84 
     85     return cursor;
     86 }
     87 
     88 #if SDL_VIDEO_DRIVER_X11_XCURSOR
     89 static Cursor
     90 X11_CreateXCursorCursor(SDL_Surface * surface, int hot_x, int hot_y)
     91 {
     92     Display *display = GetDisplay();
     93     Cursor cursor = None;
     94     XcursorImage *image;
     95 
     96     image = X11_XcursorImageCreate(surface->w, surface->h);
     97     if (!image) {
     98         SDL_OutOfMemory();
     99         return None;
    100     }
    101     image->xhot = hot_x;
    102     image->yhot = hot_y;
    103     image->delay = 0;
    104 
    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);
    108 
    109     cursor = X11_XcursorImageLoadCursor(display, image);
    110 
    111     X11_XcursorImageDestroy(image);
    112 
    113     return cursor;
    114 }
    115 #endif /* SDL_VIDEO_DRIVER_X11_XCURSOR */
    116 
    117 static Cursor
    118 X11_CreatePixmapCursor(SDL_Surface * surface, int hot_x, int hot_y)
    119 {
    120     Display *display = GetDisplay();
    121     XColor fg, bg;
    122     Cursor cursor = None;
    123     Uint32 *ptr;
    124     Uint8 *data_bits, *mask_bits;
    125     Pixmap data_pixmap, mask_pixmap;
    126     int x, y;
    127     unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
    128     unsigned int width_bytes = ((surface->w + 7) & ~7) / 8;
    129 
    130     data_bits = SDL_calloc(1, surface->h * width_bytes);
    131     if (!data_bits) {
    132         SDL_OutOfMemory();
    133         return None;
    134     }
    135 
    136     mask_bits = SDL_calloc(1, surface->h * width_bytes);
    137     if (!mask_bits) {
    138         SDL_free(data_bits);
    139         SDL_OutOfMemory();
    140         return None;
    141     }
    142 
    143     /* Code below assumes ARGB pixel format */
    144     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
    145 
    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;
    154             if (alpha > 25) {
    155                 mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
    156 
    157                 if ((red + green + blue) > 0x40) {
    158                     fgBits++;
    159                     rfg += red;
    160                     gfg += green;
    161                     bfg += blue;
    162                     data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
    163                 } else {
    164                     bgBits++;
    165                     rbg += red;
    166                     gbg += green;
    167                     bbg += blue;
    168                 }
    169             }
    170             ++ptr;
    171         }
    172     }
    173 
    174     if (fgBits) {
    175         fg.red   = rfg * 257 / fgBits;
    176         fg.green = gfg * 257 / fgBits;
    177         fg.blue  = bfg * 257 / fgBits;
    178     }
    179     else fg.red = fg.green = fg.blue = 0;
    180 
    181     if (bgBits) {
    182         bg.red   = rbg * 257 / bgBits;
    183         bg.green = gbg * 257 / bgBits;
    184         bg.blue  = bbg * 257 / bgBits;
    185     }
    186     else bg.red = bg.green = bg.blue = 0;
    187 
    188     data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
    189                                         (char*)data_bits,
    190                                         surface->w, surface->h);
    191     mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
    192                                         (char*)mask_bits,
    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);
    198 
    199     return cursor;
    200 }
    201 
    202 static SDL_Cursor *
    203 X11_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    204 {
    205     SDL_Cursor *cursor;
    206 
    207     cursor = SDL_calloc(1, sizeof(*cursor));
    208     if (cursor) {
    209         Cursor x11_cursor = None;
    210 
    211 #if SDL_VIDEO_DRIVER_X11_XCURSOR
    212         if (SDL_X11_HAVE_XCURSOR) {
    213             x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
    214         }
    215 #endif
    216         if (x11_cursor == None) {
    217             x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
    218         }
    219         cursor->driverdata = (void*)x11_cursor;
    220     } else {
    221         SDL_OutOfMemory();
    222     }
    223 
    224     return cursor;
    225 }
    226 
    227 static SDL_Cursor *
    228 X11_CreateSystemCursor(SDL_SystemCursor id)
    229 {
    230     SDL_Cursor *cursor;
    231     unsigned int shape;
    232 
    233     switch(id)
    234     {
    235     default:
    236         SDL_assert(0);
    237         return NULL;
    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;
    252     }
    253 
    254     cursor = SDL_calloc(1, sizeof(*cursor));
    255     if (cursor) {
    256         Cursor x11_cursor;
    257 
    258         x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
    259 
    260         cursor->driverdata = (void*)x11_cursor;
    261     } else {
    262         SDL_OutOfMemory();
    263     }
    264 
    265     return cursor;
    266 }
    267 
    268 static void
    269 X11_FreeCursor(SDL_Cursor * cursor)
    270 {
    271     Cursor x11_cursor = (Cursor)cursor->driverdata;
    272 
    273     if (x11_cursor != None) {
    274         X11_XFreeCursor(GetDisplay(), x11_cursor);
    275     }
    276     SDL_free(cursor);
    277 }
    278 
    279 static int
    280 X11_ShowCursor(SDL_Cursor * cursor)
    281 {
    282     Cursor x11_cursor = 0;
    283 
    284     if (cursor) {
    285         x11_cursor = (Cursor)cursor->driverdata;
    286     } else {
    287         x11_cursor = X11_CreateEmptyCursor();
    288     }
    289 
    290     /* FIXME: Is there a better way than this? */
    291     {
    292         SDL_VideoDevice *video = SDL_GetVideoDevice();
    293         Display *display = GetDisplay();
    294         SDL_Window *window;
    295         SDL_WindowData *data;
    296 
    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);
    301             } else {
    302                 X11_XUndefineCursor(display, data->xwindow);
    303             }
    304         }
    305         X11_XFlush(display);
    306     }
    307     return 0;
    308 }
    309 
    310 static void
    311 WarpMouseInternal(Window xwindow, const int x, const int y)
    312 {
    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;
    318 }
    319 
    320 static void
    321 X11_WarpMouse(SDL_Window * window, int x, int y)
    322 {
    323     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    324     WarpMouseInternal(data->xwindow, x, y);
    325 }
    326 
    327 static int
    328 X11_WarpMouseGlobal(int x, int y)
    329 {
    330     WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
    331     return 0;
    332 }
    333 
    334 static int
    335 X11_SetRelativeMouseMode(SDL_bool enabled)
    336 {
    337 #if SDL_VIDEO_DRIVER_X11_XINPUT2
    338     if(X11_Xinput2IsInitialized())
    339         return 0;
    340 #else
    341     SDL_Unsupported();
    342 #endif
    343     return -1;
    344 }
    345 
    346 static int
    347 X11_CaptureMouse(SDL_Window *window)
    348 {
    349     Display *display = GetDisplay();
    350 
    351     if (window) {
    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");
    359         }
    360     } else {
    361         X11_XUngrabPointer(display, CurrentTime);
    362     }
    363 
    364     X11_XSync(display, False);
    365 
    366     return 0;
    367 }
    368 
    369 static Uint32
    370 X11_GetGlobalMouseState(int *x, int *y)
    371 {
    372     SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
    373     Display *display = GetDisplay();
    374     const int num_screens = SDL_GetNumVideoDisplays();
    375     int i;
    376 
    377     /* !!! FIXME: should we XSync() here first? */
    378 
    379 #if !SDL_VIDEO_DRIVER_X11_XINPUT2
    380     videodata->global_mouse_changed = SDL_TRUE;
    381 #endif
    382 
    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);
    388             if (data != NULL) {
    389                 Window root, child;
    390                 int rootx, rooty, winx, winy;
    391                 unsigned int mask;
    392                 if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
    393                     XWindowAttributes root_attrs;
    394                     Uint32 buttons = 0;
    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).
    400                      *
    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;
    407                     break;
    408                 }
    409             }
    410         }
    411     }
    412 
    413     SDL_assert(!videodata->global_mouse_changed);  /* The pointer wasn't on any X11 screen?! */
    414 
    415     *x = videodata->global_mouse_position.x;
    416     *y = videodata->global_mouse_position.y;
    417     return videodata->global_mouse_buttons;
    418 }
    419 
    420 
    421 void
    422 X11_InitMouse(_THIS)
    423 {
    424     SDL_Mouse *mouse = SDL_GetMouse();
    425 
    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;
    435 
    436     SDL_SetDefaultCursor(X11_CreateDefaultCursor());
    437 }
    438 
    439 void
    440 X11_QuitMouse(_THIS)
    441 {
    442     X11_DestroyEmptyCursor();
    443 }
    444 
    445 #endif /* SDL_VIDEO_DRIVER_X11 */
    446 
    447 /* vi: set ts=4 sw=4 expandtab: */