sdl

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

SDL_kmsdrm_legacy_mouse.c (16061B)


      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 
     22 #include "../../SDL_internal.h"
     23 
     24 #if SDL_VIDEO_DRIVER_KMSDRM
     25 
     26 #include "SDL_kmsdrm_legacy_video.h"
     27 #include "SDL_kmsdrm_legacy_mouse.h"
     28 #include "SDL_kmsdrm_legacy_dyn.h"
     29 
     30 #include "../../events/SDL_mouse_c.h"
     31 #include "../../events/default_cursor.h"
     32 
     33 static SDL_Cursor *KMSDRM_LEGACY_CreateDefaultCursor(void);
     34 static SDL_Cursor *KMSDRM_LEGACY_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
     35 static int KMSDRM_LEGACY_ShowCursor(SDL_Cursor * cursor);
     36 static void KMSDRM_LEGACY_MoveCursor(SDL_Cursor * cursor);
     37 static void KMSDRM_LEGACY_FreeCursor(SDL_Cursor * cursor);
     38 static void KMSDRM_LEGACY_WarpMouse(SDL_Window * window, int x, int y);
     39 static int KMSDRM_LEGACY_WarpMouseGlobal(int x, int y);
     40 
     41 static SDL_Cursor *
     42 KMSDRM_LEGACY_CreateDefaultCursor(void)
     43 {
     44     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
     45 }
     46 
     47 /* Evaluate if a given cursor size is supported or not. Notably, current Intel gfx only support 64x64 and up. */
     48 static SDL_bool
     49 KMSDRM_LEGACY_IsCursorSizeSupported (int w, int h, uint32_t bo_format) {
     50 
     51     SDL_VideoDevice *dev = SDL_GetVideoDevice();
     52     SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
     53     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
     54 
     55     int ret;
     56     uint32_t bo_handle;
     57     struct gbm_bo *bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, w, h, bo_format,
     58                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
     59 
     60     if (!bo) {
     61         SDL_SetError("Could not create GBM cursor BO width size %dx%d for size testing", w, h);
     62         goto cleanup;
     63     }
     64 
     65     bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(bo).u32;
     66     ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, w, h);
     67 
     68     if (ret) {
     69         goto cleanup;
     70     }
     71     else {
     72         KMSDRM_LEGACY_gbm_bo_destroy(bo);
     73         return SDL_TRUE;
     74     }
     75 
     76 cleanup:
     77     if (bo) {
     78         KMSDRM_LEGACY_gbm_bo_destroy(bo);
     79     }
     80     return SDL_FALSE;
     81 }
     82 
     83 /* Create a cursor from a surface */
     84 static SDL_Cursor *
     85 KMSDRM_LEGACY_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
     86 {
     87     SDL_VideoDevice *dev = SDL_GetVideoDevice();
     88     SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
     89     SDL_PixelFormat *pixlfmt = surface->format;
     90     KMSDRM_LEGACY_CursorData *curdata;
     91     SDL_Cursor *cursor;
     92     SDL_bool cursor_supported = SDL_FALSE;
     93     int i, ret, usable_cursor_w, usable_cursor_h;
     94     uint32_t bo_format, bo_stride;
     95     char *buffer = NULL;
     96     size_t bufsize;
     97 
     98     switch(pixlfmt->format) {
     99     case SDL_PIXELFORMAT_RGB332:
    100         bo_format = GBM_FORMAT_RGB332;
    101         break;
    102     case SDL_PIXELFORMAT_ARGB4444:
    103         bo_format = GBM_FORMAT_ARGB4444;
    104         break;
    105     case SDL_PIXELFORMAT_RGBA4444:
    106         bo_format = GBM_FORMAT_RGBA4444;
    107         break;
    108     case SDL_PIXELFORMAT_ABGR4444:
    109         bo_format = GBM_FORMAT_ABGR4444;
    110         break;
    111     case SDL_PIXELFORMAT_BGRA4444:
    112         bo_format = GBM_FORMAT_BGRA4444;
    113         break;
    114     case SDL_PIXELFORMAT_ARGB1555:
    115         bo_format = GBM_FORMAT_ARGB1555;
    116         break;
    117     case SDL_PIXELFORMAT_RGBA5551:
    118         bo_format = GBM_FORMAT_RGBA5551;
    119         break;
    120     case SDL_PIXELFORMAT_ABGR1555:
    121         bo_format = GBM_FORMAT_ABGR1555;
    122         break;
    123     case SDL_PIXELFORMAT_BGRA5551:
    124         bo_format = GBM_FORMAT_BGRA5551;
    125         break;
    126     case SDL_PIXELFORMAT_RGB565:
    127         bo_format = GBM_FORMAT_RGB565;
    128         break;
    129     case SDL_PIXELFORMAT_BGR565:
    130         bo_format = GBM_FORMAT_BGR565;
    131         break;
    132     case SDL_PIXELFORMAT_RGB888:
    133     case SDL_PIXELFORMAT_RGB24:
    134         bo_format = GBM_FORMAT_RGB888;
    135         break;
    136     case SDL_PIXELFORMAT_BGR888:
    137     case SDL_PIXELFORMAT_BGR24:
    138         bo_format = GBM_FORMAT_BGR888;
    139         break;
    140     case SDL_PIXELFORMAT_RGBX8888:
    141         bo_format = GBM_FORMAT_RGBX8888;
    142         break;
    143     case SDL_PIXELFORMAT_BGRX8888:
    144         bo_format = GBM_FORMAT_BGRX8888;
    145         break;
    146     case SDL_PIXELFORMAT_ARGB8888:
    147         bo_format = GBM_FORMAT_ARGB8888;
    148         break;
    149     case SDL_PIXELFORMAT_RGBA8888:
    150         bo_format = GBM_FORMAT_RGBA8888;
    151         break;
    152     case SDL_PIXELFORMAT_ABGR8888:
    153         bo_format = GBM_FORMAT_ABGR8888;
    154         break;
    155     case SDL_PIXELFORMAT_BGRA8888:
    156         bo_format = GBM_FORMAT_BGRA8888;
    157         break;
    158     case SDL_PIXELFORMAT_ARGB2101010:
    159         bo_format = GBM_FORMAT_ARGB2101010;
    160         break;
    161     default:
    162         SDL_SetError("Unsupported pixel format for cursor");
    163         return NULL;
    164     }
    165 
    166     if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
    167         SDL_SetError("Unsupported pixel format for cursor");
    168         return NULL;
    169     }
    170 
    171     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
    172     if (!cursor) {
    173         SDL_OutOfMemory();
    174         return NULL;
    175     }
    176     curdata = (KMSDRM_LEGACY_CursorData *) SDL_calloc(1, sizeof(*curdata));
    177     if (!curdata) {
    178         SDL_OutOfMemory();
    179         SDL_free(cursor);
    180         return NULL;
    181     }
    182 
    183     /* We have to know beforehand if a cursor with the same size as the surface is supported.
    184      * If it's not, we have to find an usable cursor size and use an intermediate and clean buffer.
    185      * If we can't find a cursor size supported by the hardware, we won't go on trying to 
    186      * call SDL_SetCursor() later. */
    187 
    188     usable_cursor_w = surface->w;
    189     usable_cursor_h = surface->h;
    190 
    191     while (usable_cursor_w <= MAX_CURSOR_W && usable_cursor_h <= MAX_CURSOR_H) { 
    192         if (KMSDRM_LEGACY_IsCursorSizeSupported(usable_cursor_w, usable_cursor_h, bo_format)) {
    193             cursor_supported = SDL_TRUE;
    194             break;
    195         }
    196         usable_cursor_w += usable_cursor_w;
    197         usable_cursor_h += usable_cursor_h;
    198     }
    199 
    200     if (!cursor_supported) {
    201         SDL_SetError("Could not find a cursor size supported by the kernel driver");
    202         goto cleanup;
    203     }
    204 
    205     curdata->hot_x = hot_x;
    206     curdata->hot_y = hot_y;
    207     curdata->w = usable_cursor_w;
    208     curdata->h = usable_cursor_h;
    209 
    210     curdata->bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
    211                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
    212 
    213     if (!curdata->bo) {
    214         SDL_SetError("Could not create GBM cursor BO");
    215         goto cleanup;
    216     }
    217 
    218     bo_stride = KMSDRM_LEGACY_gbm_bo_get_stride(curdata->bo);
    219     bufsize = bo_stride * curdata->h;
    220 
    221     if (surface->pitch != bo_stride) {
    222         /* pitch doesn't match stride, must be copied to temp buffer  */
    223         buffer = SDL_malloc(bufsize);
    224         if (!buffer) {
    225             SDL_OutOfMemory();
    226             goto cleanup;
    227         }
    228 
    229         if (SDL_MUSTLOCK(surface)) {
    230             if (SDL_LockSurface(surface) < 0) {
    231                 /* Could not lock surface */
    232                 goto cleanup;
    233             }
    234         }
    235 
    236         /* Clean the whole temporary buffer */
    237         SDL_memset(buffer, 0x00, bo_stride * curdata->h);
    238 
    239         /* Copy to temporary buffer */
    240         for (i = 0; i < surface->h; i++) {
    241             SDL_memcpy(buffer + (i * bo_stride),
    242                        ((char *)surface->pixels) + (i * surface->pitch),
    243                        surface->w * pixlfmt->BytesPerPixel);
    244         }
    245 
    246         if (SDL_MUSTLOCK(surface)) {
    247             SDL_UnlockSurface(surface);
    248         }
    249 
    250         if (KMSDRM_LEGACY_gbm_bo_write(curdata->bo, buffer, bufsize)) {
    251             SDL_SetError("Could not write to GBM cursor BO");
    252             goto cleanup;
    253         }
    254 
    255         /* Free temporary buffer */
    256         SDL_free(buffer);
    257         buffer = NULL;
    258     } else {
    259         /* surface matches BO format */
    260         if (SDL_MUSTLOCK(surface)) {
    261             if (SDL_LockSurface(surface) < 0) {
    262                 /* Could not lock surface */
    263                 goto cleanup;
    264             }
    265         }
    266 
    267         ret = KMSDRM_LEGACY_gbm_bo_write(curdata->bo, surface->pixels, bufsize);
    268 
    269         if (SDL_MUSTLOCK(surface)) {
    270             SDL_UnlockSurface(surface);
    271         }
    272 
    273         if (ret) {
    274             SDL_SetError("Could not write to GBM cursor BO");
    275             goto cleanup;
    276         }
    277     }
    278 
    279     cursor->driverdata = curdata;
    280 
    281     return cursor;
    282 
    283 cleanup:
    284     if (buffer) {
    285         SDL_free(buffer);
    286     }
    287     if (cursor) {
    288         SDL_free(cursor);
    289     }
    290     if (curdata) {
    291         if (curdata->bo) {
    292             KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo);
    293         }
    294         SDL_free(curdata);
    295     }
    296     return NULL;
    297 }
    298 
    299 /* Show the specified cursor, or hide if cursor is NULL */
    300 static int
    301 KMSDRM_LEGACY_ShowCursor(SDL_Cursor * cursor)
    302 {
    303     SDL_VideoDevice *dev = SDL_GetVideoDevice();
    304     SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
    305     SDL_Mouse *mouse;
    306     KMSDRM_LEGACY_CursorData *curdata;
    307     SDL_VideoDisplay *display = NULL;
    308     SDL_DisplayData *dispdata = NULL;
    309     int ret;
    310     uint32_t bo_handle;
    311 
    312     mouse = SDL_GetMouse();
    313     if (!mouse) {
    314         return SDL_SetError("No mouse.");
    315     }
    316 
    317     if (mouse->focus) {
    318         display = SDL_GetDisplayForWindow(mouse->focus);
    319         if (display) {
    320             dispdata = (SDL_DisplayData*) display->driverdata;
    321         }
    322     }
    323 
    324     if (!cursor) {
    325         /* Hide current cursor */
    326         if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
    327             curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
    328 
    329             if (curdata->crtc_id != 0) {
    330                 ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, curdata->crtc_id, 0, 0, 0);
    331                 if (ret) {
    332                     SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
    333                     return ret;
    334                 }
    335                 /* Mark previous cursor as not-displayed */
    336                 curdata->crtc_id = 0;
    337 
    338                 return 0;
    339             }
    340         }
    341         /* otherwise if possible, hide global cursor */
    342         if (dispdata && dispdata->crtc_id != 0) {
    343             ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, 0, 0, 0);
    344             if (ret) {
    345                 SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
    346                 return ret;
    347             }
    348             return 0;
    349         }
    350 
    351         return SDL_SetError("Couldn't find cursor to hide.");
    352     }
    353     /* If cursor != NULL, show new cursor on display */
    354     if (!display) {
    355         return SDL_SetError("Could not get display for mouse.");
    356     }
    357     if (!dispdata) {
    358         return SDL_SetError("Could not get display driverdata.");
    359     }
    360 
    361     curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata;
    362     if (!curdata || !curdata->bo) {
    363         return SDL_SetError("Cursor not initialized properly.");
    364     }
    365 
    366     bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(curdata->bo).u32;
    367     if (curdata->hot_x == 0 && curdata->hot_y == 0) {
    368         ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle,
    369                                       curdata->w, curdata->h);
    370     } else {
    371         ret = KMSDRM_LEGACY_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc_id, bo_handle,
    372                                        curdata->w, curdata->h, curdata->hot_x, curdata->hot_y);
    373     }
    374     if (ret) {
    375         SDL_SetError("drmModeSetCursor failed.");
    376         return ret;
    377     }
    378 
    379     curdata->crtc_id = dispdata->crtc_id;
    380 
    381     return 0;
    382 }
    383 
    384 /* Free a window manager cursor */
    385 static void
    386 KMSDRM_LEGACY_FreeCursor(SDL_Cursor * cursor)
    387 {
    388     KMSDRM_LEGACY_CursorData *curdata;
    389     int drm_fd;
    390 
    391     if (cursor) {
    392         curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata;
    393 
    394         if (curdata) {
    395             if (curdata->bo) {
    396                 if (curdata->crtc_id != 0) {
    397                     drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
    398                     /* Hide the cursor if previously shown on a CRTC */
    399                     KMSDRM_LEGACY_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0);
    400                     curdata->crtc_id = 0;
    401                 }
    402                 KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo);
    403                 curdata->bo = NULL;
    404             }
    405             SDL_free(cursor->driverdata);
    406         }
    407         SDL_free(cursor);
    408     }
    409 }
    410 
    411 /* Warp the mouse to (x,y) */
    412 static void
    413 KMSDRM_LEGACY_WarpMouse(SDL_Window * window, int x, int y)
    414 {
    415     /* Only one global/fullscreen window is supported */
    416     KMSDRM_LEGACY_WarpMouseGlobal(x, y);
    417 }
    418 
    419 /* Warp the mouse to (x,y) */
    420 static int
    421 KMSDRM_LEGACY_WarpMouseGlobal(int x, int y)
    422 {
    423     KMSDRM_LEGACY_CursorData *curdata;
    424     SDL_Mouse *mouse = SDL_GetMouse();
    425 
    426     if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
    427         /* Update internal mouse position. */
    428         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
    429 
    430         /* And now update the cursor graphic position on screen. */
    431         curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
    432         if (curdata->bo) {
    433 
    434             if (curdata->crtc_id != 0) {
    435                 int ret, drm_fd;
    436                 drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
    437                 ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y);
    438 
    439                 if (ret) {
    440                     SDL_SetError("drmModeMoveCursor() failed.");
    441                 }
    442 
    443                 return ret;
    444             } else {
    445                 return SDL_SetError("Cursor is not currently shown.");
    446             }
    447         } else {
    448             return SDL_SetError("Cursor not initialized properly.");
    449         }
    450     } else {
    451         return SDL_SetError("No mouse or current cursor.");
    452     }
    453 }
    454 
    455 void
    456 KMSDRM_LEGACY_InitMouse(_THIS)
    457 {
    458     /* FIXME: Using UDEV it should be possible to scan all mice
    459      * but there's no point in doing so as there's no multimice support...yet!
    460      */
    461     SDL_Mouse *mouse = SDL_GetMouse();
    462 
    463     mouse->CreateCursor = KMSDRM_LEGACY_CreateCursor;
    464     mouse->ShowCursor = KMSDRM_LEGACY_ShowCursor;
    465     mouse->MoveCursor = KMSDRM_LEGACY_MoveCursor;
    466     mouse->FreeCursor = KMSDRM_LEGACY_FreeCursor;
    467     mouse->WarpMouse = KMSDRM_LEGACY_WarpMouse;
    468     mouse->WarpMouseGlobal = KMSDRM_LEGACY_WarpMouseGlobal;
    469 
    470     SDL_SetDefaultCursor(KMSDRM_LEGACY_CreateDefaultCursor());
    471 }
    472 
    473 void
    474 KMSDRM_LEGACY_QuitMouse(_THIS)
    475 {
    476     /* TODO: ? */
    477 }
    478 
    479 /* This is called when a mouse motion event occurs */
    480 static void
    481 KMSDRM_LEGACY_MoveCursor(SDL_Cursor * cursor)
    482 {
    483     SDL_Mouse *mouse = SDL_GetMouse();
    484     KMSDRM_LEGACY_CursorData *curdata;
    485     int drm_fd, ret;
    486 
    487     /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
    488        That's why we move the cursor graphic ONLY. */
    489     if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
    490         curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
    491         drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
    492         ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
    493 
    494         if (ret) {
    495             SDL_SetError("drmModeMoveCursor() failed.");
    496         }
    497     }
    498 }
    499 
    500 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
    501 
    502 /* vi: set ts=4 sw=4 expandtab: */