src/video/kmsdrm/SDL_kmsdrmmouse.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 02 Aug 2017 10:22:48 -0700
changeset 11175 cbc6a8a5b701
child 11342 0b924e560249
permissions -rw-r--r--
Fixed bug 3690 - SDL2 KMS/DRM render context support

Manuel

The attached patch adds support for KMS/DRM context graphics.

It builds with no problem on X86_64 GNU/Linux systems, provided the needed libraries are present, and on ARM GNU/Linux systems that have KMS/DRM support and a GLES2 implementation.
Tested on Raspberry Pi: KMS/DRM is what the Raspberry Pi will use as default in the near future, once the propietary DispmanX API by Broadcom is overtaken by open graphics stack, it's possible to boot current Raspbian system in KMS mode by adding "dtoverlay=vc4-kms-v3d" to config.txt on Raspbian's boot partition.
X86 systems use KMS right away in every current GNU/Linux system.

Simple build instructions:

$./autogen.sh
$./configure --enable-video-kmsdrm
$make
     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_KMSDRM
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_surface.h"
    27 
    28 #include "SDL_kmsdrmvideo.h"
    29 #include "SDL_kmsdrmmouse.h"
    30 #include "SDL_kmsdrmdyn.h"
    31 
    32 #include "../SDL_sysvideo.h"
    33 #include "../../events/SDL_mouse_c.h"
    34 #include "../../events/default_cursor.h"
    35 
    36 
    37 static SDL_Cursor *KMSDRM_CreateDefaultCursor(void);
    38 static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
    39 static int KMSDRM_ShowCursor(SDL_Cursor * cursor);
    40 static void KMSDRM_MoveCursor(SDL_Cursor * cursor);
    41 static void KMSDRM_FreeCursor(SDL_Cursor * cursor);
    42 static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y);
    43 static int KMSDRM_WarpMouseGlobal(int x, int y);
    44 
    45 static SDL_Cursor *
    46 KMSDRM_CreateDefaultCursor(void)
    47 {
    48     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
    49 }
    50 
    51 /* Create a cursor from a surface */
    52 static SDL_Cursor *
    53 KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    54 {
    55     SDL_VideoDevice *dev = SDL_GetVideoDevice();
    56     SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
    57     SDL_PixelFormat *pixlfmt = surface->format;
    58     KMSDRM_CursorData *curdata;
    59     SDL_Cursor *cursor;
    60     int i, ret;
    61     uint32_t bo_format, bo_stride;
    62     char *buffer = NULL;
    63     size_t bufsize;
    64 
    65     switch(pixlfmt->format) {
    66     case SDL_PIXELFORMAT_RGB332:
    67         bo_format = GBM_FORMAT_RGB332;
    68         break;
    69     case SDL_PIXELFORMAT_ARGB4444:
    70         bo_format = GBM_FORMAT_ARGB4444;
    71         break;
    72     case SDL_PIXELFORMAT_RGBA4444:
    73         bo_format = GBM_FORMAT_RGBA4444;
    74         break;
    75     case SDL_PIXELFORMAT_ABGR4444:
    76         bo_format = GBM_FORMAT_ABGR4444;
    77         break;
    78     case SDL_PIXELFORMAT_BGRA4444:
    79         bo_format = GBM_FORMAT_BGRA4444;
    80         break;
    81     case SDL_PIXELFORMAT_ARGB1555:
    82         bo_format = GBM_FORMAT_ARGB1555;
    83         break;
    84     case SDL_PIXELFORMAT_RGBA5551:
    85         bo_format = GBM_FORMAT_RGBA5551;
    86         break;
    87     case SDL_PIXELFORMAT_ABGR1555:
    88         bo_format = GBM_FORMAT_ABGR1555;
    89         break;
    90     case SDL_PIXELFORMAT_BGRA5551:
    91         bo_format = GBM_FORMAT_BGRA5551;
    92         break;
    93     case SDL_PIXELFORMAT_RGB565:
    94         bo_format = GBM_FORMAT_RGB565;
    95         break;
    96     case SDL_PIXELFORMAT_BGR565:
    97         bo_format = GBM_FORMAT_BGR565;
    98         break;
    99     case SDL_PIXELFORMAT_RGB888:
   100     case SDL_PIXELFORMAT_RGB24:
   101         bo_format = GBM_FORMAT_RGB888;
   102         break;
   103     case SDL_PIXELFORMAT_BGR888:
   104     case SDL_PIXELFORMAT_BGR24:
   105         bo_format = GBM_FORMAT_BGR888;
   106         break;
   107     case SDL_PIXELFORMAT_RGBX8888:
   108         bo_format = GBM_FORMAT_RGBX8888;
   109         break;
   110     case SDL_PIXELFORMAT_BGRX8888:
   111         bo_format = GBM_FORMAT_BGRX8888;
   112         break;
   113     case SDL_PIXELFORMAT_ARGB8888:
   114         bo_format = GBM_FORMAT_ARGB8888;
   115         break;
   116     case SDL_PIXELFORMAT_RGBA8888:
   117         bo_format = GBM_FORMAT_RGBA8888;
   118         break;
   119     case SDL_PIXELFORMAT_ABGR8888:
   120         bo_format = GBM_FORMAT_ABGR8888;
   121         break;
   122     case SDL_PIXELFORMAT_BGRA8888:
   123         bo_format = GBM_FORMAT_BGRA8888;
   124         break;
   125     case SDL_PIXELFORMAT_ARGB2101010:
   126         bo_format = GBM_FORMAT_ARGB2101010;
   127         break;
   128     default:
   129         SDL_SetError("Unsupported pixel format for cursor");
   130         return NULL;
   131     }
   132 
   133     if (!KMSDRM_gbm_device_is_format_supported(vdata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
   134         SDL_SetError("Unsupported pixel format for cursor");
   135         return NULL;
   136     }
   137 
   138     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
   139     if (cursor == NULL) {
   140         SDL_OutOfMemory();
   141         return NULL;
   142     }
   143     curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
   144     if (curdata == NULL) {
   145         SDL_OutOfMemory();
   146         SDL_free(cursor);
   147         return NULL;
   148     }
   149 
   150     curdata->hot_x = hot_x;
   151     curdata->hot_y = hot_y;
   152     curdata->w = surface->w;
   153     curdata->h = surface->h;
   154 
   155     curdata->bo = KMSDRM_gbm_bo_create(vdata->gbm, surface->w, surface->h, bo_format,
   156                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
   157     if (curdata->bo == NULL) {
   158         SDL_SetError("Could not create GBM cursor BO");
   159         goto cleanup;
   160     }
   161 
   162     bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
   163     bufsize = bo_stride * surface->h;
   164 
   165     if (surface->pitch != bo_stride) {
   166         /* pitch doesn't match stride, must be copied to temp buffer  */
   167         buffer = SDL_malloc(bufsize);
   168         if (buffer == NULL) {
   169             SDL_OutOfMemory();
   170             goto cleanup;
   171         }
   172 
   173         if (SDL_MUSTLOCK(surface)) {
   174             if (SDL_LockSurface(surface) < 0) {
   175                 /* Could not lock surface */
   176                 goto cleanup;
   177             }
   178         }
   179 
   180         /* Copy to temporary buffer */
   181         for (i = 0; i < surface->h; i++) {
   182             SDL_memcpy(buffer + (i * bo_stride),
   183                        ((char *)surface->pixels) + (i * surface->pitch),
   184                        surface->w * pixlfmt->BytesPerPixel);
   185         }
   186 
   187         if (SDL_MUSTLOCK(surface)) {
   188             SDL_UnlockSurface(surface);
   189         }
   190 
   191         if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
   192             SDL_SetError("Could not write to GBM cursor BO");
   193             goto cleanup;
   194         }
   195 
   196         /* Free temporary buffer */
   197         SDL_free(buffer);
   198         buffer = NULL;
   199     } else {
   200         /* surface matches BO format */
   201         if (SDL_MUSTLOCK(surface)) {
   202             if (SDL_LockSurface(surface) < 0) {
   203                 /* Could not lock surface */
   204                 goto cleanup;
   205             }
   206         }
   207 
   208         ret = KMSDRM_gbm_bo_write(curdata->bo, surface->pixels, bufsize);
   209 
   210         if (SDL_MUSTLOCK(surface)) {
   211             SDL_UnlockSurface(surface);
   212         }
   213 
   214         if (ret) {
   215             SDL_SetError("Could not write to GBM cursor BO");
   216             goto cleanup;
   217         }
   218     }
   219 
   220     cursor->driverdata = curdata;
   221 
   222     return cursor;
   223 
   224 cleanup:
   225     if (buffer != NULL) {
   226         SDL_free(buffer);
   227     }
   228     if (cursor != NULL) {
   229         SDL_free(cursor);
   230     }
   231     if (curdata != NULL) {
   232         if (curdata->bo != NULL) {
   233             KMSDRM_gbm_bo_destroy(curdata->bo);
   234         }
   235         SDL_free(curdata);
   236     }
   237     return NULL;
   238 }
   239 
   240 /* Show the specified cursor, or hide if cursor is NULL */
   241 static int
   242 KMSDRM_ShowCursor(SDL_Cursor * cursor)
   243 {
   244     SDL_VideoDevice *dev = SDL_GetVideoDevice();
   245     SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
   246     SDL_Mouse *mouse;
   247     KMSDRM_CursorData *curdata;
   248     SDL_VideoDisplay *display = NULL;
   249     SDL_DisplayData *ddata = NULL;
   250     int ret;
   251     uint32_t bo_handle;
   252 
   253     mouse = SDL_GetMouse();
   254     if (mouse == NULL) {
   255         return SDL_SetError("No mouse.");
   256     }
   257 
   258     if (mouse->focus != NULL) {
   259         display = SDL_GetDisplayForWindow(mouse->focus);
   260         if (display != NULL) {
   261             ddata = (SDL_DisplayData*) display->driverdata;
   262         }
   263     }
   264 
   265     if (cursor == NULL) {
   266         /* Hide current cursor */
   267         if ( mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   268             curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
   269 
   270             if (curdata->crtc_id != 0) {
   271                 ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, curdata->crtc_id, 0, 0, 0);
   272                 if (ret) {
   273                     SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
   274                     return ret;
   275                 }
   276                 /* Mark previous cursor as not-displayed */
   277                 curdata->crtc_id = 0;
   278 
   279                 return 0;
   280             }
   281         }
   282         /* otherwise if possible, hide global cursor */
   283         if (ddata != NULL && ddata->crtc_id != 0) {
   284             ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, 0, 0, 0);
   285             if (ret) {
   286                 SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
   287                 return ret;
   288             }
   289             return 0;
   290         }
   291 
   292         return SDL_SetError("Couldn't find cursor to hide.");
   293     }
   294     /* If cursor != NULL, show new cursor on display */
   295     if (display == NULL) {
   296         return SDL_SetError("Could not get display for mouse.");
   297     }
   298     if (ddata == NULL) {
   299         return SDL_SetError("Could not get display driverdata.");
   300     }
   301 
   302     curdata = (KMSDRM_CursorData *) cursor->driverdata;
   303     if (curdata == NULL || curdata->bo == NULL) {
   304         return SDL_SetError("Cursor not initialized properly.");
   305     }
   306 
   307     bo_handle = KMSDRM_gbm_bo_get_handle(curdata->bo).u32;
   308     if (curdata->hot_x == 0 && curdata->hot_y == 0) {
   309         ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, bo_handle,
   310                                       curdata->w, curdata->h);
   311     } else {
   312         ret = KMSDRM_drmModeSetCursor2(vdata->drm_fd, ddata->crtc_id, bo_handle,
   313                                        curdata->w, curdata->h,
   314                                        curdata->hot_x, curdata->hot_y);
   315     }
   316     if (ret) {
   317         SDL_SetError("drmModeSetCursor failed.");
   318         return ret;
   319     }
   320 
   321     curdata->crtc_id = ddata->crtc_id;
   322 
   323     return 0;
   324 }
   325 
   326 /* Free a window manager cursor */
   327 static void
   328 KMSDRM_FreeCursor(SDL_Cursor * cursor)
   329 {
   330     KMSDRM_CursorData *curdata;
   331     int drm_fd;
   332 
   333     if (cursor != NULL) {
   334         curdata = (KMSDRM_CursorData *) cursor->driverdata;
   335 
   336         if (curdata != NULL) {
   337             if (curdata->bo != NULL) {
   338                 if (curdata->crtc_id != 0) {
   339                     drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
   340                     /* Hide the cursor if previously shown on a CRTC */
   341                     KMSDRM_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0);
   342                     curdata->crtc_id = 0;
   343                 }
   344                 KMSDRM_gbm_bo_destroy(curdata->bo);
   345                 curdata->bo = NULL;
   346             }
   347             SDL_free(cursor->driverdata);
   348         }
   349         SDL_free(cursor);
   350     }
   351 }
   352 
   353 /* Warp the mouse to (x,y) */
   354 static void
   355 KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
   356 {
   357     /* Only one global/fullscreen window is supported */
   358     KMSDRM_WarpMouseGlobal(x, y);
   359 }
   360 
   361 /* Warp the mouse to (x,y) */
   362 static int
   363 KMSDRM_WarpMouseGlobal(int x, int y)
   364 {
   365     KMSDRM_CursorData *curdata;
   366     SDL_Mouse *mouse = SDL_GetMouse();
   367 
   368     if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   369         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
   370         if (curdata->bo != NULL) {
   371             if (curdata->crtc_id != 0) {
   372                 int ret, drm_fd;
   373                 drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
   374                 ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y);
   375 
   376                 if (ret) {
   377                     SDL_SetError("drmModeMoveCursor() failed.");
   378                 }
   379 
   380                 return ret;
   381             } else {
   382                 return SDL_SetError("Cursor is not currently shown.");
   383             }
   384         } else {
   385             return SDL_SetError("Cursor not initialized properly.");
   386         }
   387     } else {
   388         return SDL_SetError("No mouse or current cursor.");
   389     }
   390 }
   391 
   392 void
   393 KMSDRM_InitMouse(_THIS)
   394 {
   395     /* FIXME: Using UDEV it should be possible to scan all mice
   396      * but there's no point in doing so as there's no multimice support...yet!
   397      */
   398     SDL_Mouse *mouse = SDL_GetMouse();
   399 
   400     mouse->CreateCursor = KMSDRM_CreateCursor;
   401     mouse->ShowCursor = KMSDRM_ShowCursor;
   402     mouse->MoveCursor = KMSDRM_MoveCursor;
   403     mouse->FreeCursor = KMSDRM_FreeCursor;
   404     mouse->WarpMouse = KMSDRM_WarpMouse;
   405     mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
   406 
   407     SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
   408 }
   409 
   410 void
   411 KMSDRM_QuitMouse(_THIS)
   412 {
   413     /* TODO: ? */
   414 }
   415 
   416 /* This is called when a mouse motion event occurs */
   417 static void
   418 KMSDRM_MoveCursor(SDL_Cursor * cursor)
   419 {
   420     SDL_Mouse *mouse = SDL_GetMouse();
   421     KMSDRM_WarpMouse(mouse->focus, mouse->x, mouse->y);
   422 }
   423 
   424 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
   425 
   426 /* vi: set ts=4 sw=4 expandtab: */