[KMS/DRM] Rewrite KMSDRM_LEGACY backend to accomodate Vulkan compatibility. Fix several bugs on that backend.
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c
index 85a6999..ac1f420 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.c
@@ -44,7 +44,32 @@
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
-/* Evaluate if a given cursor size is supported or not. Notably, current Intel gfx only support 64x64 and up. */
+/* Converts a pixel from straight-alpha [AA, RR, GG, BB], which the SDL cursor surface has,
+ to premultiplied-alpha [AA. AA*RR, AA*GG, AA*BB].
+ These multiplications have to be done with floats instead of uint32_t's,
+ and the resulting values have to be converted to be relative to the 0-255 interval,
+ where 255 is 1.00 and anything between 0 and 255 is 0.xx. */
+void legacy_alpha_premultiply_ARGB8888 (uint32_t *pixel) {
+
+ uint32_t A, R, G, B;
+
+ /* Component bytes extraction. */
+ A = (*pixel >> (3 << 3)) & 0xFF;
+ R = (*pixel >> (2 << 3)) & 0xFF;
+ G = (*pixel >> (1 << 3)) & 0xFF;
+ B = (*pixel >> (0 << 3)) & 0xFF;
+
+ /* Alpha pre-multiplication of each component. */
+ R = (float)A * ((float)R /255);
+ G = (float)A * ((float)G /255);
+ B = (float)A * ((float)B /255);
+
+ /* ARGB8888 pixel recomposition. */
+ (*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0);
+}
+
+/* Evaluate if a given cursor size is supported or not.
+ Notably, current Intel gfx only support 64x64 and up. */
static SDL_bool
KMSDRM_LEGACY_IsCursorSizeSupported (int w, int h, uint32_t bo_format) {
@@ -54,7 +79,7 @@
int ret;
uint32_t bo_handle;
- struct gbm_bo *bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, w, h, bo_format,
+ struct gbm_bo *bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm_dev, w, h, bo_format,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!bo) {
@@ -63,7 +88,7 @@
}
bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(bo).u32;
- ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, w, h);
+ ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, bo_handle, w, h);
if (ret) {
goto cleanup;
@@ -80,328 +105,236 @@
return SDL_FALSE;
}
-/* Create a cursor from a surface */
+/* This simply gets the cursor soft-buffer ready.
+ We don't copy it to a GBO BO until ShowCursor() because the cusor GBM BO (living
+ in dispata) is destroyed and recreated when we recreate windows, etc. */
static SDL_Cursor *
KMSDRM_LEGACY_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
{
- SDL_VideoDevice *dev = SDL_GetVideoDevice();
- SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
- SDL_PixelFormat *pixlfmt = surface->format;
KMSDRM_LEGACY_CursorData *curdata;
- SDL_Cursor *cursor;
- SDL_bool cursor_supported = SDL_FALSE;
- int i, ret, usable_cursor_w, usable_cursor_h;
- uint32_t bo_format, bo_stride;
- char *buffer = NULL;
- size_t bufsize;
+ SDL_Cursor *cursor, *ret;
- switch(pixlfmt->format) {
- case SDL_PIXELFORMAT_RGB332:
- bo_format = GBM_FORMAT_RGB332;
- break;
- case SDL_PIXELFORMAT_ARGB4444:
- bo_format = GBM_FORMAT_ARGB4444;
- break;
- case SDL_PIXELFORMAT_RGBA4444:
- bo_format = GBM_FORMAT_RGBA4444;
- break;
- case SDL_PIXELFORMAT_ABGR4444:
- bo_format = GBM_FORMAT_ABGR4444;
- break;
- case SDL_PIXELFORMAT_BGRA4444:
- bo_format = GBM_FORMAT_BGRA4444;
- break;
- case SDL_PIXELFORMAT_ARGB1555:
- bo_format = GBM_FORMAT_ARGB1555;
- break;
- case SDL_PIXELFORMAT_RGBA5551:
- bo_format = GBM_FORMAT_RGBA5551;
- break;
- case SDL_PIXELFORMAT_ABGR1555:
- bo_format = GBM_FORMAT_ABGR1555;
- break;
- case SDL_PIXELFORMAT_BGRA5551:
- bo_format = GBM_FORMAT_BGRA5551;
- break;
- case SDL_PIXELFORMAT_RGB565:
- bo_format = GBM_FORMAT_RGB565;
- break;
- case SDL_PIXELFORMAT_BGR565:
- bo_format = GBM_FORMAT_BGR565;
- break;
- case SDL_PIXELFORMAT_RGB888:
- case SDL_PIXELFORMAT_RGB24:
- bo_format = GBM_FORMAT_RGB888;
- break;
- case SDL_PIXELFORMAT_BGR888:
- case SDL_PIXELFORMAT_BGR24:
- bo_format = GBM_FORMAT_BGR888;
- break;
- case SDL_PIXELFORMAT_RGBX8888:
- bo_format = GBM_FORMAT_RGBX8888;
- break;
- case SDL_PIXELFORMAT_BGRX8888:
- bo_format = GBM_FORMAT_BGRX8888;
- break;
- case SDL_PIXELFORMAT_ARGB8888:
- bo_format = GBM_FORMAT_ARGB8888;
- break;
- case SDL_PIXELFORMAT_RGBA8888:
- bo_format = GBM_FORMAT_RGBA8888;
- break;
- case SDL_PIXELFORMAT_ABGR8888:
- bo_format = GBM_FORMAT_ABGR8888;
- break;
- case SDL_PIXELFORMAT_BGRA8888:
- bo_format = GBM_FORMAT_BGRA8888;
- break;
- case SDL_PIXELFORMAT_ARGB2101010:
- bo_format = GBM_FORMAT_ARGB2101010;
- break;
- default:
- SDL_SetError("Unsupported pixel format for cursor");
- return NULL;
- }
+ curdata = NULL;
+ ret = NULL;
- if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
- SDL_SetError("Unsupported pixel format for cursor");
- return NULL;
- }
+ /* All code below assumes ARGB8888 format for the cursor surface,
+ like other backends do. Also, the GBM BO pixels have to be
+ alpha-premultiplied, but the SDL surface we receive has
+ straight-alpha pixels, so we always have to convert. */
+ SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
+ SDL_assert(surface->pitch == surface->w * 4);
cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
SDL_OutOfMemory();
- return NULL;
+ goto cleanup;
}
curdata = (KMSDRM_LEGACY_CursorData *) SDL_calloc(1, sizeof(*curdata));
if (!curdata) {
SDL_OutOfMemory();
- SDL_free(cursor);
- return NULL;
- }
-
- /* We have to know beforehand if a cursor with the same size as the surface is supported.
- * If it's not, we have to find an usable cursor size and use an intermediate and clean buffer.
- * If we can't find a cursor size supported by the hardware, we won't go on trying to
- * call SDL_SetCursor() later. */
-
- usable_cursor_w = surface->w;
- usable_cursor_h = surface->h;
-
- while (usable_cursor_w <= MAX_CURSOR_W && usable_cursor_h <= MAX_CURSOR_H) {
- if (KMSDRM_LEGACY_IsCursorSizeSupported(usable_cursor_w, usable_cursor_h, bo_format)) {
- cursor_supported = SDL_TRUE;
- break;
- }
- usable_cursor_w += usable_cursor_w;
- usable_cursor_h += usable_cursor_h;
- }
-
- if (!cursor_supported) {
- SDL_SetError("Could not find a cursor size supported by the kernel driver");
goto cleanup;
}
+ /* hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. */
curdata->hot_x = hot_x;
curdata->hot_y = hot_y;
- curdata->w = usable_cursor_w;
- curdata->h = usable_cursor_h;
+ curdata->w = surface->w;
+ curdata->h = surface->h;
+ curdata->buffer = NULL;
- curdata->bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
- GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+ /* Configure the cursor buffer info.
+ This buffer has the original size of the cursor surface we are given. */
+ curdata->buffer_pitch = surface->pitch;
+ curdata->buffer_size = surface->pitch * surface->h;
+ curdata->buffer = (uint32_t*)SDL_malloc(curdata->buffer_size);
- if (!curdata->bo) {
- SDL_SetError("Could not create GBM cursor BO");
+ if (!curdata->buffer) {
+ SDL_OutOfMemory();
goto cleanup;
}
- bo_stride = KMSDRM_LEGACY_gbm_bo_get_stride(curdata->bo);
- bufsize = bo_stride * curdata->h;
-
- if (surface->pitch != bo_stride) {
- /* pitch doesn't match stride, must be copied to temp buffer */
- buffer = SDL_malloc(bufsize);
- if (!buffer) {
- SDL_OutOfMemory();
+ if (SDL_MUSTLOCK(surface)) {
+ if (SDL_LockSurface(surface) < 0) {
+ /* Could not lock surface */
goto cleanup;
}
+ }
- if (SDL_MUSTLOCK(surface)) {
- if (SDL_LockSurface(surface) < 0) {
- /* Could not lock surface */
- goto cleanup;
- }
- }
+ /* Copy the surface pixels to the cursor buffer, for future use in ShowCursor() */
+ SDL_memcpy(curdata->buffer, surface->pixels, curdata->buffer_size);
- /* Clean the whole temporary buffer */
- SDL_memset(buffer, 0x00, bo_stride * curdata->h);
-
- /* Copy to temporary buffer */
- for (i = 0; i < surface->h; i++) {
- SDL_memcpy(buffer + (i * bo_stride),
- ((char *)surface->pixels) + (i * surface->pitch),
- surface->w * pixlfmt->BytesPerPixel);
- }
-
- if (SDL_MUSTLOCK(surface)) {
- SDL_UnlockSurface(surface);
- }
-
- if (KMSDRM_LEGACY_gbm_bo_write(curdata->bo, buffer, bufsize)) {
- SDL_SetError("Could not write to GBM cursor BO");
- goto cleanup;
- }
-
- /* Free temporary buffer */
- SDL_free(buffer);
- buffer = NULL;
- } else {
- /* surface matches BO format */
- if (SDL_MUSTLOCK(surface)) {
- if (SDL_LockSurface(surface) < 0) {
- /* Could not lock surface */
- goto cleanup;
- }
- }
-
- ret = KMSDRM_LEGACY_gbm_bo_write(curdata->bo, surface->pixels, bufsize);
-
- if (SDL_MUSTLOCK(surface)) {
- SDL_UnlockSurface(surface);
- }
-
- if (ret) {
- SDL_SetError("Could not write to GBM cursor BO");
- goto cleanup;
- }
+ if (SDL_MUSTLOCK(surface)) {
+ SDL_UnlockSurface(surface);
}
cursor->driverdata = curdata;
- return cursor;
+ ret = cursor;
cleanup:
- if (buffer) {
- SDL_free(buffer);
+ if (ret == NULL) {
+ if (curdata) {
+ if (curdata->buffer) {
+ SDL_free(curdata->buffer);
+ }
+ SDL_free(curdata);
+ }
+ if (cursor) {
+ SDL_free(cursor);
+ }
}
- if (cursor) {
- SDL_free(cursor);
- }
- if (curdata) {
- if (curdata->bo) {
- KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo);
- }
- SDL_free(curdata);
- }
- return NULL;
+
+ return ret;
}
-/* Show the specified cursor, or hide if cursor is NULL */
+/* When we create a window, we have to test if we have to show the cursor,
+ and explicily do so if necessary.
+ This is because when we destroy a window, we take the cursor away from the
+ cursor plane, and destroy the cusror GBM BO. So we have to re-show it,
+ so to say. */
+void
+KMSDRM_LEGACY_InitCursor()
+{
+ SDL_Mouse *mouse = NULL;
+ mouse = SDL_GetMouse();
+
+ if (!mouse) {
+ return;
+ }
+ if (!(mouse->cur_cursor)) {
+ return;
+ }
+
+ if (!(mouse->cursor_shown)) {
+ return;
+ }
+
+ KMSDRM_LEGACY_ShowCursor(mouse->cur_cursor);
+}
+
+/* Show the specified cursor, or hide if cursor is NULL. */
static int
KMSDRM_LEGACY_ShowCursor(SDL_Cursor * cursor)
{
- SDL_VideoDevice *dev = SDL_GetVideoDevice();
- SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
+ SDL_VideoDevice *video_device = SDL_GetVideoDevice();
+ SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_Mouse *mouse;
KMSDRM_LEGACY_CursorData *curdata;
- SDL_VideoDisplay *display = NULL;
- SDL_DisplayData *dispdata = NULL;
- int ret;
+
uint32_t bo_handle;
+ size_t bo_stride;
+ size_t bufsize;
+ uint32_t *ready_buffer = NULL;
+ uint32_t pixel;
+
+ int i,j;
+ int ret;
+
mouse = SDL_GetMouse();
if (!mouse) {
return SDL_SetError("No mouse.");
}
- if (mouse->focus) {
- display = SDL_GetDisplayForWindow(mouse->focus);
- if (display) {
- dispdata = (SDL_DisplayData*) display->driverdata;
- }
- }
-
- if (!cursor) {
- /* Hide current cursor */
- if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
- curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
-
- if (curdata->crtc_id != 0) {
- ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, curdata->crtc_id, 0, 0, 0);
- if (ret) {
- SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
- return ret;
- }
- /* Mark previous cursor as not-displayed */
- curdata->crtc_id = 0;
-
- return 0;
- }
- }
- /* otherwise if possible, hide global cursor */
- if (dispdata && dispdata->crtc_id != 0) {
- ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, 0, 0, 0);
- if (ret) {
- SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
- return ret;
- }
- return 0;
- }
-
- return SDL_SetError("Couldn't find cursor to hide.");
- }
- /* If cursor != NULL, show new cursor on display */
- if (!display) {
- return SDL_SetError("Could not get display for mouse.");
- }
- if (!dispdata) {
- return SDL_SetError("Could not get display driverdata.");
- }
-
- curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata;
- if (!curdata || !curdata->bo) {
- return SDL_SetError("Cursor not initialized properly.");
- }
-
- bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(curdata->bo).u32;
- if (curdata->hot_x == 0 && curdata->hot_y == 0) {
- ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle,
- curdata->w, curdata->h);
- } else {
- ret = KMSDRM_LEGACY_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc_id, bo_handle,
- curdata->w, curdata->h, curdata->hot_x, curdata->hot_y);
- }
- if (ret) {
- SDL_SetError("drmModeSetCursor failed.");
+ /*********************************************************/
+ /* Hide cursor if it's NULL or it has no focus(=winwow). */
+ /*********************************************************/
+ if (!cursor || !mouse->focus) {
+ /* Hide the drm cursor with no more considerations because
+ SDL_VideoQuit() takes us here after disabling the mouse
+ so there is no mouse->cur_cursor by now. */
+ ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd,
+ dispdata->crtc->crtc_id, 0, 0, 0);
+ if (ret) {
+ ret = SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
+ }
return ret;
}
- curdata->crtc_id = dispdata->crtc_id;
+ /************************************************/
+ /* If cursor != NULL, DO show cursor on display */
+ /************************************************/
+ if (!dispdata) {
+ return SDL_SetError("Could not get display driverdata.");
+ }
+
+ curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata;
- return 0;
+ if (!curdata || !dispdata->cursor_bo) {
+ return SDL_SetError("Cursor not initialized properly.");
+ }
+
+ /* Prepare a buffer we can dump to our GBM BO (different
+ size, alpha premultiplication...) */
+ bo_stride = KMSDRM_LEGACY_gbm_bo_get_stride(dispdata->cursor_bo);
+ bufsize = bo_stride * curdata->h;
+
+ ready_buffer = (uint32_t*)SDL_malloc(bufsize);
+ if (!ready_buffer) {
+ ret = SDL_OutOfMemory();
+ goto cleanup;
+ }
+
+ /* Clean the whole buffer we are preparing. */
+ SDL_memset(ready_buffer, 0x00, bo_stride * curdata->h);
+
+ /* Copy from the cursor buffer to a buffer that we can dump to the GBM BO,
+ pre-multiplying by alpha each pixel as we go. */
+ for (i = 0; i < curdata->h; i++) {
+ for (j = 0; j < curdata->w; j++) {
+ pixel = ((uint32_t*)curdata->buffer)[i * curdata->w + j];
+ legacy_alpha_premultiply_ARGB8888 (&pixel);
+ SDL_memcpy(ready_buffer + (i * dispdata->cursor_w) + j, &pixel, 4);
+ }
+ }
+
+ /* Dump the cursor buffer to our GBM BO. */
+ if (KMSDRM_LEGACY_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) {
+ ret = SDL_SetError("Could not write to GBM cursor BO");
+ goto cleanup;
+ }
+
+ /* Put the GBM BO buffer on screen using the DRM interface. */
+ bo_handle = KMSDRM_LEGACY_gbm_bo_get_handle(dispdata->cursor_bo).u32;
+ if (curdata->hot_x == 0 && curdata->hot_y == 0) {
+ ret = KMSDRM_LEGACY_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
+ bo_handle, curdata->w, curdata->h);
+ } else {
+ ret = KMSDRM_LEGACY_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
+ bo_handle, curdata->w, curdata->h, curdata->hot_x, curdata->hot_y);
+ }
+
+ if (ret) {
+ ret = SDL_SetError("Failed to set DRM cursor.");
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (ready_buffer) {
+ SDL_free(ready_buffer);
+ }
+ return ret;
}
-/* Free a window manager cursor */
+/* We have destroyed the cursor by now, in KMSDRM_DestroyCursor.
+ This is only for freeing the SDL_cursor.*/
static void
KMSDRM_LEGACY_FreeCursor(SDL_Cursor * cursor)
{
KMSDRM_LEGACY_CursorData *curdata;
- int drm_fd;
+ /* Even if the cursor is not ours, free it. */
if (cursor) {
curdata = (KMSDRM_LEGACY_CursorData *) cursor->driverdata;
-
- if (curdata) {
- if (curdata->bo) {
- if (curdata->crtc_id != 0) {
- drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
- /* Hide the cursor if previously shown on a CRTC */
- KMSDRM_LEGACY_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0);
- curdata->crtc_id = 0;
- }
- KMSDRM_LEGACY_gbm_bo_destroy(curdata->bo);
- curdata->bo = NULL;
- }
+ /* Free cursor buffer */
+ if (curdata->buffer) {
+ SDL_free(curdata->buffer);
+ curdata->buffer = NULL;
+ }
+ /* Free cursor itself */
+ if (cursor->driverdata) {
SDL_free(cursor->driverdata);
}
SDL_free(cursor);
@@ -420,44 +353,57 @@
static int
KMSDRM_LEGACY_WarpMouseGlobal(int x, int y)
{
- KMSDRM_LEGACY_CursorData *curdata;
SDL_Mouse *mouse = SDL_GetMouse();
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
/* Update internal mouse position. */
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
/* And now update the cursor graphic position on screen. */
- curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
- if (curdata->bo) {
+ if (dispdata->cursor_bo) {
+ int ret, drm_fd;
+ drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(
+ KMSDRM_LEGACY_gbm_bo_get_device(dispdata->cursor_bo));
+ ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, x, y);
- if (curdata->crtc_id != 0) {
- int ret, drm_fd;
- drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
- ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y);
+ if (ret) {
+ SDL_SetError("drmModeMoveCursor() failed.");
+ }
- if (ret) {
- SDL_SetError("drmModeMoveCursor() failed.");
- }
+ return ret;
- return ret;
- } else {
- return SDL_SetError("Cursor is not currently shown.");
- }
} else {
return SDL_SetError("Cursor not initialized properly.");
}
} else {
return SDL_SetError("No mouse or current cursor.");
}
+
+ return 0;
}
+/* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */
+void
+KMSDRM_LEGACY_DeinitMouse(_THIS)
+{
+ SDL_VideoDevice *video_device = SDL_GetVideoDevice();
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
+
+ /* Destroy the curso GBM BO. */
+ if (video_device && dispdata->cursor_bo) {
+ KMSDRM_LEGACY_gbm_bo_destroy(dispdata->cursor_bo);
+ dispdata->cursor_bo = NULL;
+ }
+}
+
+/* Create cursor BO. */
void
KMSDRM_LEGACY_InitMouse(_THIS)
{
- /* FIXME: Using UDEV it should be possible to scan all mice
- * but there's no point in doing so as there's no multimice support...yet!
- */
+ SDL_VideoDevice *dev = SDL_GetVideoDevice();
+ SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = KMSDRM_LEGACY_CreateCursor;
@@ -467,7 +413,53 @@
mouse->WarpMouse = KMSDRM_LEGACY_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_LEGACY_WarpMouseGlobal;
+ /************************************************/
+ /* Create the cursor GBM BO, if we haven't yet. */
+ /************************************************/
+ if (!dispdata->cursor_bo) {
+
+ if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm_dev,
+ GBM_FORMAT_ARGB8888,
+ GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
+ {
+ SDL_SetError("Unsupported pixel format for cursor");
+ return;
+ }
+
+ if (KMSDRM_LEGACY_drmGetCap(viddata->drm_fd,
+ DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) ||
+ KMSDRM_LEGACY_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT,
+ &dispdata->cursor_h))
+ {
+ SDL_SetError("Could not get the recommended GBM cursor size");
+ goto cleanup;
+ }
+
+ if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) {
+ SDL_SetError("Could not get an usable GBM cursor size");
+ goto cleanup;
+ }
+
+ dispdata->cursor_bo = KMSDRM_LEGACY_gbm_bo_create(viddata->gbm_dev,
+ dispdata->cursor_w, dispdata->cursor_h,
+ GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+
+ if (!dispdata->cursor_bo) {
+ SDL_SetError("Could not create GBM cursor BO");
+ goto cleanup;
+ }
+ }
+
+ /* SDL expects to set the default cursor on screen when we init the mouse. */
SDL_SetDefaultCursor(KMSDRM_LEGACY_CreateDefaultCursor());
+
+ return;
+
+cleanup:
+ if (dispdata->cursor_bo) {
+ KMSDRM_LEGACY_gbm_bo_destroy(dispdata->cursor_bo);
+ dispdata->cursor_bo = NULL;
+ }
}
void
@@ -481,15 +473,14 @@
KMSDRM_LEGACY_MoveCursor(SDL_Cursor * cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
- KMSDRM_LEGACY_CursorData *curdata;
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
int drm_fd, ret;
/* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
That's why we move the cursor graphic ONLY. */
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
- curdata = (KMSDRM_LEGACY_CursorData *) mouse->cur_cursor->driverdata;
- drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(curdata->bo));
- ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
+ drm_fd = KMSDRM_LEGACY_gbm_device_get_fd(KMSDRM_LEGACY_gbm_bo_get_device(dispdata->cursor_bo));
+ ret = KMSDRM_LEGACY_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, mouse->y);
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h
index 46e1122..e2bc968 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_mouse.h
@@ -31,15 +31,24 @@
typedef struct _KMSDRM_LEGACY_CursorData
{
- struct gbm_bo *bo;
- uint32_t crtc_id;
int hot_x, hot_y;
int w, h;
+
+ /* The buffer where we store the mouse bitmap ready to be used.
+ We get it ready and filled in CreateCursor(), and copy it
+ to a GBM BO in ShowCursor().*/
+ uint32_t *buffer;
+ size_t buffer_size;
+ size_t buffer_pitch;
+
} KMSDRM_LEGACY_CursorData;
extern void KMSDRM_LEGACY_InitMouse(_THIS);
+extern void KMSDRM_LEGACY_DeinitMouse(_THIS);
extern void KMSDRM_LEGACY_QuitMouse(_THIS);
+extern void KMSDRM_LEGACY_InitCursor();
+
#endif /* SDL_KMSDRM_LEGACY_mouse_h_ */
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c
index f522a4b..bc3d58a 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.c
@@ -35,10 +35,36 @@
/* EGL implementation of SDL OpenGL support */
+void
+KMSDRM_LEGACY_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor)
+{
+ /* if SDL was _also_ built with the Raspberry Pi driver (so we're
+ definitely a Pi device), default to GLES2. */
+#if SDL_VIDEO_DRIVER_RPI
+ *mask = SDL_GL_CONTEXT_PROFILE_ES;
+ *major = 2;
+ *minor = 0;
+#endif
+}
+
int
KMSDRM_LEGACY_GLES_LoadLibrary(_THIS, const char *path) {
- NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm;
+ /* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(),
+ where we do the same library load we would normally do here.
+ because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(),
+ so gbm dev isn't yet created when this is called, AND we can't alter the
+ call order in SDL_CreateWindow(). */
+#if 0
+ NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA);
+#endif
+ return 0;
+}
+
+void
+KMSDRM_LEGACY_GLES_UnloadLibrary(_THIS) {
+ /* As with KMSDRM_GLES_LoadLibrary(), we define our own "dummy" unloading function
+ so we manually unload the library whenever we want. */
}
SDL_EGL_CreateContext_impl(KMSDRM_LEGACY)
@@ -81,16 +107,17 @@
}
/* Release the previous front buffer */
- if (windata->curr_bo) {
- KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
- /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Released GBM surface %p", (void *)windata->curr_bo); */
- windata->curr_bo = NULL;
+ if (windata->bo) {
+ KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->bo);
+ windata->bo = NULL;
}
- windata->curr_bo = windata->next_bo;
+ windata->bo = windata->next_bo;
- /* Make the current back buffer the next front buffer */
- if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface))) {
+ /* Mark a buffer to becume the next front buffer.
+ This won't happen until pagelip completes. */
+ if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display,
+ windata->egl_surface))) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed.");
return 0;
}
@@ -100,46 +127,60 @@
if (!windata->next_bo) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer");
return 0;
- /* } else {
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Locked GBM surface %p", (void *)windata->next_bo); */
}
+ /* Get the fb_info for the next front buffer. */
fb_info = KMSDRM_LEGACY_FBFromBO(_this, windata->next_bo);
if (!fb_info) {
return 0;
}
- if (!windata->curr_bo) {
- /* On the first swap, immediately present the new front buffer. Before
- drmModePageFlip can be used the CRTC has to be configured to use
- the current connector and mode with drmModeSetCrtc */
- ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id, 0,
- 0, &dispdata->conn->connector_id, 1, &dispdata->mode);
+ if (!windata->bo) {
+ /***************************************************************************/
+ /* This is fundamental. */
+ /* We can't display an fb smaller than the resolution currently configured */
+ /* on the CRTC, because the CRTC would be scanning out of bounds. */
+ /* So instead of using drmModeSetCrtc() to tell CRTC to scan the fb */
+ /* directly, we use a plane (overlay or primary, doesn't mind if we */
+ /* activated the UNVERSAL PLANES cap) to scale the buffer to the */
+ /* resolution currently configured on the CRTC. */
+ /* */
+ /* We can't do this sooner, on CreateWindow(), because we don't have a */
+ /* framebuffer there yet, and DRM doesn't like 0 or -1 as the fb_id. */
+ /***************************************************************************/
+ ret = KMSDRM_LEGACY_drmModeSetPlane(viddata->drm_fd, dispdata->plane_id,
+ dispdata->crtc->crtc_id, fb_info->fb_id, 0,
+ windata->output_x, 0,
+ windata->output_w, windata->output_h,
+ 0, 0,
+ windata->src_w << 16, windata->src_h << 16);
if (ret) {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not configure CRTC");
- }
- } else {
- /* On subsequent swaps, queue the new front buffer to be flipped during
- the next vertical blank */
- ret = KMSDRM_LEGACY_drmModePageFlip(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
- /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip)",
- viddata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */
-
- if (_this->egl_data->egl_swapinterval == 1) {
- if (ret == 0) {
- windata->waiting_for_flip = SDL_TRUE;
- } else {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
- }
+ SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not set PLANE");
}
- /* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios.
- Run your SDL2 program with "SDL_KMSDRM_LEGACY_DOUBLE_BUFFER=1 <program_name>" to enable this. */
- if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
- KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1);
- }
+ return 0;
+ }
+
+ /* Issue pageflip on the next front buffer.
+ The pageflip will be done during the next vblank. */
+ ret = KMSDRM_LEGACY_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
+ fb_info->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
+
+ if (_this->egl_data->egl_swapinterval == 1) {
+ if (ret == 0) {
+ windata->waiting_for_flip = SDL_TRUE;
+ } else {
+ SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
+ }
+ }
+
+ /* If we are in double-buffer mode, wait immediately for vsync
+ (as if we only had two buffers),
+ Run your SDL2 program with "SDL_KMSDRM_LEGACY_DOUBLE_BUFFER=1 <program_name>"
+ to enable this. */
+ if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
+ KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1);
}
return 0;
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h
index 0769f49..e1314b7 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_opengles.h
@@ -31,10 +31,10 @@
/* OpenGLES functions */
#define KMSDRM_LEGACY_GLES_GetAttribute SDL_EGL_GetAttribute
#define KMSDRM_LEGACY_GLES_GetProcAddress SDL_EGL_GetProcAddress
-#define KMSDRM_LEGACY_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define KMSDRM_LEGACY_GLES_DeleteContext SDL_EGL_DeleteContext
#define KMSDRM_LEGACY_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
+extern void KMSDRM_LEGACY_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor);
extern int KMSDRM_LEGACY_GLES_SetSwapInterval(_THIS, int interval);
extern int KMSDRM_LEGACY_GLES_LoadLibrary(_THIS, const char *path);
extern SDL_GLContext KMSDRM_LEGACY_GLES_CreateContext(_THIS, SDL_Window * window);
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h
index 1b4ecab..487dc19 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_sym.h
@@ -40,6 +40,7 @@
SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeCrtc,(drmModeCrtcPtr ptr))
SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeConnector,(drmModeConnectorPtr ptr))
SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeEncoder,(drmModeEncoderPtr ptr))
+SDL_KMSDRM_LEGACY_SYM(int,drmGetCap,(int fd, uint64_t capability, uint64_t *value))
SDL_KMSDRM_LEGACY_SYM(drmModeResPtr,drmModeGetResources,(int fd))
SDL_KMSDRM_LEGACY_SYM(int,drmModeAddFB,(int fd, uint32_t width, uint32_t height, uint8_t depth,
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
@@ -62,6 +63,24 @@
SDL_KMSDRM_LEGACY_SYM(int,drmModePageFlip,(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data))
+/* Planes stuff. */
+SDL_KMSDRM_LEGACY_SYM(int,drmSetClientCap,(int fd, uint64_t capability, uint64_t value))
+SDL_KMSDRM_LEGACY_SYM(drmModePlaneResPtr,drmModeGetPlaneResources,(int fd))
+SDL_KMSDRM_LEGACY_SYM(drmModePlanePtr,drmModeGetPlane,(int fd, uint32_t plane_id))
+SDL_KMSDRM_LEGACY_SYM(drmModeObjectPropertiesPtr,drmModeObjectGetProperties,(int fd,uint32_t object_id,uint32_t object_type))
+SDL_KMSDRM_LEGACY_SYM(drmModePropertyPtr,drmModeGetProperty,(int fd, uint32_t propertyId))
+
+SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeProperty,(drmModePropertyPtr ptr))
+SDL_KMSDRM_LEGACY_SYM(void,drmModeFreeObjectProperties,(drmModeObjectPropertiesPtr ptr))
+SDL_KMSDRM_LEGACY_SYM(void,drmModeFreePlane,(drmModePlanePtr ptr))
+SDL_KMSDRM_LEGACY_SYM(void,drmModeFreePlaneResources,(drmModePlaneResPtr ptr))
+SDL_KMSDRM_LEGACY_SYM(int,drmModeSetPlane,(int fd, uint32_t plane_id, uint32_t crtc_id,
+ uint32_t fb_id, uint32_t flags,
+ int32_t crtc_x, int32_t crtc_y,
+ uint32_t crtc_w, uint32_t crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h))
+/* Planes stuff ends. */
SDL_KMSDRM_LEGACY_MODULE(GBM)
SDL_KMSDRM_LEGACY_SYM(int,gbm_device_get_fd,(struct gbm_device *gbm))
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c
index db33278..445d8cb 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.c
@@ -61,6 +61,106 @@
#define KMSDRM_LEGACY_DRI_CARDPATHFMT "/dev/dri/card%d"
#endif
+#if 0
+
+void print_plane_info(_THIS, drmModePlanePtr plane)
+{
+ char *plane_type;
+ drmModeRes *resources;
+ uint32_t type = 0;
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+ int i;
+
+ drmModeObjectPropertiesPtr props = KMSDRM_LEGACY_drmModeObjectGetProperties(viddata->drm_fd,
+ plane->plane_id, DRM_MODE_OBJECT_PLANE);
+
+ /* Search the plane props for the plane type. */
+ for (i = 0; i < props->count_props; i++) {
+ drmModePropertyPtr p = KMSDRM_LEGACY_drmModeGetProperty(viddata->drm_fd, props->props[i]);
+ if ((strcmp(p->name, "type") == 0)) {
+ type = props->prop_values[i];
+ }
+
+ KMSDRM_LEGACY_drmModeFreeProperty(p);
+ }
+
+ switch (type) {
+ case DRM_PLANE_TYPE_OVERLAY:
+ plane_type = "overlay";
+ break;
+
+ case DRM_PLANE_TYPE_PRIMARY:
+ plane_type = "primary";
+ break;
+
+ case DRM_PLANE_TYPE_CURSOR:
+ plane_type = "cursor";
+ break;
+ }
+
+
+ /* Remember that to present a plane on screen, it has to be
+ connected to a CRTC so the CRTC scans it,
+ scales it, etc... and presents it on screen. */
+
+ /* Now we look for the CRTCs supported by the plane. */
+ resources = KMSDRM_LEGACY_drmModeGetResources(viddata->drm_fd);
+ if (!resources)
+ return;
+
+ printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ", plane->plane_id, plane_type, plane->crtc_id);
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (plane->possible_crtcs & (1 << i)) {
+ uint32_t crtc_id = resources->crtcs[i];
+ printf ("%d", crtc_id);
+ break;
+ }
+ }
+
+ printf ("\n\n");
+}
+
+void get_planes_info(_THIS)
+{
+ drmModePlaneResPtr plane_resources;
+ uint32_t i;
+
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+ SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
+
+ plane_resources = KMSDRM_LEGACY_drmModeGetPlaneResources(viddata->drm_fd);
+ if (!plane_resources) {
+ printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
+ return;
+ }
+
+ printf("--Number of planes found: %d-- \n", plane_resources->count_planes);
+ printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc->crtc_id);
+
+ /* Iterate on all the available planes. */
+ for (i = 0; (i < plane_resources->count_planes); i++) {
+
+ uint32_t plane_id = plane_resources->planes[i];
+
+ drmModePlanePtr plane = KMSDRM_LEGACY_drmModeGetPlane(viddata->drm_fd, plane_id);
+ if (!plane) {
+ printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
+ continue;
+ }
+
+ /* Print plane info. */
+ print_plane_info(_this, plane);
+ KMSDRM_LEGACY_drmModeFreePlane(plane);
+ }
+
+ KMSDRM_LEGACY_drmModeFreePlaneResources(plane_resources);
+}
+
+#endif
+
+
+
+
static int
check_modestting(int devindex)
{
@@ -216,6 +316,7 @@
device->SetWindowIcon = KMSDRM_LEGACY_SetWindowIcon;
device->SetWindowPosition = KMSDRM_LEGACY_SetWindowPosition;
device->SetWindowSize = KMSDRM_LEGACY_SetWindowSize;
+ device->SetWindowFullscreen = KMSDRM_LEGACY_SetWindowFullscreen;
device->ShowWindow = KMSDRM_LEGACY_ShowWindow;
device->HideWindow = KMSDRM_LEGACY_HideWindow;
device->RaiseWindow = KMSDRM_LEGACY_RaiseWindow;
@@ -364,113 +465,64 @@
/* SDL Video and Display initialization/handling functions */
/* _this is a SDL_VideoDevice * */
/*****************************************************************************/
-static void
-KMSDRM_LEGACY_DestroySurfaces(_THIS, SDL_Window * window)
-{
- SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
- KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1);
-
- if (windata->curr_bo) {
- KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
- windata->curr_bo = NULL;
+/* Deinitializes the dispdata members needed for KMSDRM operation that are
+ inoffeensive for VK compatibility. */
+void KMSDRM_LEGACY_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
+ /* Free connector */
+ if (dispdata && dispdata->connector) {
+ KMSDRM_LEGACY_drmModeFreeConnector(dispdata->connector);
+ dispdata->connector = NULL;
}
- if (windata->next_bo) {
- KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->next_bo);
- windata->next_bo = NULL;
- }
-
- SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- if (windata->egl_surface != EGL_NO_SURFACE) {
- SDL_EGL_DestroySurface(_this, windata->egl_surface);
- windata->egl_surface = EGL_NO_SURFACE;
- }
-
- if (windata->gs) {
- KMSDRM_LEGACY_gbm_surface_destroy(windata->gs);
- windata->gs = NULL;
+ /* Free CRTC */
+ if (dispdata && dispdata->crtc) {
+ KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->crtc);
+ dispdata->crtc = NULL;
}
}
-int
-KMSDRM_LEGACY_CreateSurfaces(_THIS, SDL_Window * window)
-{
+/* Initializes the dispdata members needed for KMSDRM operation that are
+ inoffeensive for VK compatibility, except we must leave the drm_fd
+ closed when we get to the end of this function.
+ This is to be called early, in VideoInit(), because it gets us
+ the videomode information, which SDL needs immediately after VideoInit(). */
+int KMSDRM_LEGACY_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
- SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
- SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
- Uint32 width = dispdata->mode.hdisplay;
- Uint32 height = dispdata->mode.vdisplay;
- Uint32 surface_fmt = GBM_FORMAT_XRGB8888;
- Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
- EGLContext egl_context;
- if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) {
- SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
- }
-
- SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
- egl_context = (EGLContext)SDL_GL_GetCurrentContext();
-
- KMSDRM_LEGACY_DestroySurfaces(_this, window);
-
- windata->gs = KMSDRM_LEGACY_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags);
-
- if (!windata->gs) {
- return SDL_SetError("Could not create GBM surface");
- }
-
- windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
-
- if (windata->egl_surface == EGL_NO_SURFACE) {
- return SDL_SetError("Could not create EGL window surface");
- }
-
- SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
-
- windata->egl_surface_dirty = 0;
-
- return 0;
-}
-
-int
-KMSDRM_LEGACY_VideoInit(_THIS)
-{
- int i, j, ret = 0;
- SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
- SDL_DisplayData *dispdata = NULL;
drmModeRes *resources = NULL;
+ drmModePlaneRes *plane_resources = NULL;
drmModeEncoder *encoder = NULL;
- char devname[32];
- SDL_VideoDisplay display = {0};
+ drmModeConnector *connector = NULL;
+ drmModeCrtc *crtc = NULL;
- dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
+ uint32_t crtc_index = 0;
- if (!dispdata) {
- return SDL_OutOfMemory();
- }
+ int ret = 0;
+ unsigned i,j;
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_LEGACY_VideoInit()");
+ dispdata->modeset_pending = SDL_FALSE;
+ dispdata->gbm_init = SDL_FALSE;
+
+ dispdata->cursor_bo = NULL;
+
+ dispdata->plane_id = 0;
/* Open /dev/dri/cardNN (/dev/drmN if on OpenBSD) */
- SDL_snprintf(devname, sizeof(devname), KMSDRM_LEGACY_DRI_CARDPATHFMT, viddata->devindex);
+ SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), KMSDRM_LEGACY_DRI_CARDPATHFMT, viddata->devindex);
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
- viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", viddata->devpath);
+ viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
if (viddata->drm_fd < 0) {
- ret = SDL_SetError("Could not open %s", devname);
+ ret = SDL_SetError("Could not open %s", viddata->devpath);
goto cleanup;
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
- viddata->gbm = KMSDRM_LEGACY_gbm_create_device(viddata->drm_fd);
- if (!viddata->gbm) {
- ret = SDL_SetError("Couldn't create gbm device.");
- goto cleanup;
- }
+ /* Activate universal planes. */
+ KMSDRM_LEGACY_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
/* Get all of the available connectors / devices / crtcs */
resources = KMSDRM_LEGACY_drmModeGetResources(viddata->drm_fd);
@@ -479,24 +531,24 @@
goto cleanup;
}
+ /* Iterate on the available connectors to find a connected connector. */
for (i = 0; i < resources->count_connectors; i++) {
- drmModeConnector *conn = KMSDRM_LEGACY_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
+ drmModeConnector *conn = KMSDRM_LEGACY_drmModeGetConnector(viddata->drm_fd,
+ resources->connectors[i]);
if (!conn) {
continue;
}
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
- conn->connector_id, conn->count_modes);
- dispdata->conn = conn;
+ connector = conn;
break;
}
KMSDRM_LEGACY_drmModeFreeConnector(conn);
}
- if (!dispdata->conn) {
+ if (!connector) {
ret = SDL_SetError("No currently active connector found.");
goto cleanup;
}
@@ -509,8 +561,7 @@
continue;
}
- if (encoder->encoder_id == dispdata->conn->encoder_id) {
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
+ if (encoder->encoder_id == connector->encoder_id) {
break;
}
@@ -521,19 +572,20 @@
if (!encoder) {
/* No encoder was connected, find the first supported one */
for (i = 0; i < resources->count_encoders; i++) {
- encoder = KMSDRM_LEGACY_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
+ encoder = KMSDRM_LEGACY_drmModeGetEncoder(viddata->drm_fd,
+ resources->encoders[i]);
if (!encoder) {
continue;
}
- for (j = 0; j < dispdata->conn->count_encoders; j++) {
- if (dispdata->conn->encoders[j] == encoder->encoder_id) {
+ for (j = 0; j < connector->count_encoders; j++) {
+ if (connector->encoders[j] == encoder->encoder_id) {
break;
}
}
- if (j != dispdata->conn->count_encoders) {
+ if (j != connector->count_encoders) {
break;
}
@@ -547,156 +599,431 @@
goto cleanup;
}
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
-
/* Try to find a CRTC connected to this encoder */
- dispdata->saved_crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
+ crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
- if (!dispdata->saved_crtc) {
- /* No CRTC was connected, find the first CRTC that can be connected */
+ /* If no CRTC was connected to the encoder, find the first CRTC
+ that is supported by the encoder, and use that. */
+ if (!crtc) {
for (i = 0; i < resources->count_crtcs; i++) {
if (encoder->possible_crtcs & (1 << i)) {
encoder->crtc_id = resources->crtcs[i];
- dispdata->saved_crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
+ crtc_index = i;
+ crtc = KMSDRM_LEGACY_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
break;
}
}
}
- if (!dispdata->saved_crtc) {
+ if (!crtc) {
ret = SDL_SetError("No CRTC found.");
goto cleanup;
}
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Saved crtc_id %u, fb_id %u, (%u,%u), %ux%u",
- dispdata->saved_crtc->crtc_id, dispdata->saved_crtc->buffer_id, dispdata->saved_crtc->x,
- dispdata->saved_crtc->y, dispdata->saved_crtc->width, dispdata->saved_crtc->height);
+ /* Figure out the default mode to be set. */
+ dispdata->mode = crtc->mode;
- dispdata->crtc_id = encoder->crtc_id;
+ /* Save the original mode for restoration on quit. */
+ dispdata->original_mode = dispdata->mode;
- /* Figure out the default mode to be set. If the current CRTC's mode isn't
- valid, select the first mode supported by the connector
-
- FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
- dispdata->mode = dispdata->saved_crtc->mode;
-
- if (dispdata->saved_crtc->mode_valid == 0) {
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
- "Current mode is invalid, selecting connector's mode #0.");
- dispdata->mode = dispdata->conn->modes[0];
+ /* Find the connector's preferred mode, to be used in case the current mode
+ is not valid, or if restoring the current mode fails. */
+ for (i = 0; i < connector->count_modes; i++) {
+ if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
+ dispdata->preferred_mode = connector->modes[i];
+ }
}
- /* Setup the single display that's available */
+ /* If the current CRTC's mode isn't valid, select the preferred
+ mode of the connector. */
+ if (crtc->mode_valid == 0) {
+ dispdata->mode = dispdata->preferred_mode;
+ }
- display.desktop_mode.w = dispdata->mode.hdisplay;
- display.desktop_mode.h = dispdata->mode.vdisplay;
- display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
-#if 1
- display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
-#else
- /* FIXME */
- drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->saved_crtc->buffer_id);
- display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
- drmModeFreeFB(fb);
-#endif
- display.current_mode = display.desktop_mode;
- display.driverdata = dispdata;
- SDL_AddVideoDisplay(&display, SDL_FALSE);
+ if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) {
+ ret = SDL_SetError("Couldn't get a valid connector videomode.");
+ goto cleanup;
+ }
-#ifdef SDL_INPUT_LINUXEV
- SDL_EVDEV_Init();
-#endif
+ /*******************************************************/
+ /* Look for a plane that can be connected to our CRTC. */
+ /*******************************************************/
+ plane_resources = KMSDRM_LEGACY_drmModeGetPlaneResources(viddata->drm_fd);
- KMSDRM_LEGACY_InitMouse(_this);
+ for (int i = 0; (i < plane_resources->count_planes); i++) {
- return ret;
+ drmModePlane *plane = KMSDRM_LEGACY_drmModeGetPlane(viddata->drm_fd,
+ plane_resources->planes[i]);
+
+ /* 1 - Does this plane support our CRTC?
+ 2 - Is this plane unused or used by our CRTC? Both possibilities are good.
+ We don't mind if it's primary or overlay. */
+ if ((plane->possible_crtcs & (1 << crtc_index)) &&
+ (plane->crtc_id == crtc->crtc_id || plane->crtc_id == 0 ))
+ {
+ dispdata->plane_id = plane->plane_id;
+ break;
+ }
+
+ KMSDRM_LEGACY_drmModeFreePlane(plane);
+ }
+
+ KMSDRM_LEGACY_drmModeFreePlaneResources(plane_resources);
+
+ if (!dispdata->plane_id) {
+ ret = SDL_SetError("Could not locate a primary plane compatible with active CRTC.");
+ goto cleanup;
+ }
+
+ /* Store the connector and crtc for future use. These and the plane_id is
+ all we keep from this function, and these are just structs, inoffensive to VK. */
+ dispdata->connector = connector;
+ dispdata->crtc = crtc;
+
+ /***********************************/
+ /* Block for Vulkan compatibility. */
+ /***********************************/
+
+ /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
+ Will reopen this in CreateWindow, but only if requested a non-VK window. */
+ close (viddata->drm_fd);
+ viddata->drm_fd = -1;
cleanup:
if (encoder)
KMSDRM_LEGACY_drmModeFreeEncoder(encoder);
if (resources)
KMSDRM_LEGACY_drmModeFreeResources(resources);
-
- if (ret != 0) {
+ if (ret) {
/* Error (complete) cleanup */
- if (dispdata->conn) {
- KMSDRM_LEGACY_drmModeFreeConnector(dispdata->conn);
- dispdata->conn = NULL;
+ if (dispdata->connector) {
+ KMSDRM_LEGACY_drmModeFreeConnector(dispdata->connector);
+ dispdata->connector = NULL;
}
- if (dispdata->saved_crtc) {
- KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->saved_crtc);
- dispdata->saved_crtc = NULL;
- }
- if (viddata->gbm) {
- KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm);
- viddata->gbm = NULL;
+ if (dispdata->crtc) {
+ KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->crtc);
+ dispdata->crtc = NULL;
}
if (viddata->drm_fd >= 0) {
close(viddata->drm_fd);
viddata->drm_fd = -1;
}
- SDL_free(dispdata);
}
+
return ret;
}
+/*UNUSED function. Keep for documentation purposes. */
+SDL_bool KMSDRM_IsPlanePrimary (_THIS, drmModePlane *plane) {
+
+ SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+ SDL_bool ret = SDL_FALSE;
+
+ /* Find out if it's a primary plane. */
+ drmModeObjectProperties *plane_props =
+ KMSDRM_LEGACY_drmModeObjectGetProperties(viddata->drm_fd,
+ plane->plane_id, DRM_MODE_OBJECT_ANY);
+
+ for (int j = 0; (j < plane_props->count_props); j++) {
+
+ drmModePropertyRes *prop = KMSDRM_LEGACY_drmModeGetProperty(viddata->drm_fd,
+ plane_props->props[j]);
+
+ if ((strcmp(prop->name, "type") == 0) &&
+ (plane_props->prop_values[j] == DRM_PLANE_TYPE_PRIMARY))
+ {
+ ret = SDL_TRUE;
+ }
+
+ KMSDRM_LEGACY_drmModeFreeProperty(prop);
+
+ }
+
+ KMSDRM_LEGACY_drmModeFreeObjectProperties(plane_props);
+
+ return ret;
+}
+
+/* Init the Vulkan-INCOMPATIBLE stuff:
+ Reopen FD, create gbm dev, create dumb buffer and setup display plane.
+ This is to be called late, in WindowCreate(), and ONLY if this is not
+ a Vulkan window.
+ We are doing this so late to allow Vulkan to work if we build a VK window.
+ These things are incompatible with Vulkan, which accesses the same resources
+ internally so they must be free when trying to build a Vulkan surface.
+*/
+int
+KMSDRM_LEGACY_GBMInit (_THIS, SDL_DisplayData *dispdata)
+{
+ SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+ int ret = 0;
+
+ /* Reopen the FD! */
+ viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
+
+ /* Create the GBM device. */
+ viddata->gbm_dev = KMSDRM_LEGACY_gbm_create_device(viddata->drm_fd);
+ if (!viddata->gbm_dev) {
+ ret = SDL_SetError("Couldn't create gbm device.");
+ }
+
+ dispdata->gbm_init = SDL_TRUE;
+
+ return ret;
+}
+
+/* Deinit the Vulkan-incompatible KMSDRM stuff. */
+void
+KMSDRM_LEGACY_GBMDeinit (_THIS, SDL_DisplayData *dispdata)
+{
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+
+ /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
+ already called when we get here. */
+ if (viddata->gbm_dev) {
+ KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm_dev);
+ viddata->gbm_dev = NULL;
+ }
+
+ /* Finally close DRM FD. May be reopen on next non-vulkan window creation. */
+ if (viddata->drm_fd >= 0) {
+ close(viddata->drm_fd);
+ viddata->drm_fd = -1;
+ }
+
+ dispdata->gbm_init = SDL_FALSE;
+}
+
+void
+KMSDRM_LEGACY_DestroySurfaces(_THIS, SDL_Window *window)
+{
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+ SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ int ret;
+
+ /**********************************************/
+ /* Wait for last issued pageflip to complete. */
+ /**********************************************/
+ KMSDRM_LEGACY_WaitPageFlip(_this, windata, -1);
+
+ /***********************************************************************/
+ /* Restore the original CRTC configuration: configue the crtc with the */
+ /* original video mode and make it point to the original TTY buffer. */
+ /***********************************************************************/
+
+ ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
+ dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
+ &dispdata->original_mode);
+
+ /* If we failed to set the original mode, try to set the connector prefered mode. */
+ if (ret && (dispdata->crtc->mode_valid == 0)) {
+ ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id,
+ dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1,
+ &dispdata->original_mode);
+ }
+
+ if(ret) {
+ SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not restore CRTC");
+ }
+
+ /***************************/
+ /* Destroy the EGL surface */
+ /***************************/
+
+ SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ if (windata->egl_surface != EGL_NO_SURFACE) {
+ SDL_EGL_DestroySurface(_this, windata->egl_surface);
+ windata->egl_surface = EGL_NO_SURFACE;
+ }
+
+ /***************************/
+ /* Destroy the GBM buffers */
+ /***************************/
+
+ if (windata->bo) {
+ KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->bo);
+ windata->bo = NULL;
+ }
+
+ if (windata->next_bo) {
+ KMSDRM_LEGACY_gbm_surface_release_buffer(windata->gs, windata->next_bo);
+ windata->next_bo = NULL;
+ }
+
+ /***************************/
+ /* Destroy the GBM surface */
+ /***************************/
+
+ if (windata->gs) {
+ KMSDRM_LEGACY_gbm_surface_destroy(windata->gs);
+ windata->gs = NULL;
+ }
+}
+
+int
+KMSDRM_LEGACY_CreateSurfaces(_THIS, SDL_Window * window)
+{
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+ SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
+ uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
+ uint32_t width, height;
+
+ EGLContext egl_context;
+
+ int ret = 0;
+
+ /* If the current window already has surfaces, destroy them before creating other.
+ This is mainly for ReconfigureWindow, where we simply call CreateSurfaces()
+ for regenerating a window's surfaces. */
+ if (windata->gs) {
+ KMSDRM_LEGACY_DestroySurfaces(_this, window);
+ }
+
+ if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
+ ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+
+ width = dispdata->mode.hdisplay;
+ height = dispdata->mode.vdisplay;
+ } else {
+ width = window->w;
+ height = window->h;
+ }
+
+ if (!KMSDRM_LEGACY_gbm_device_is_format_supported(viddata->gbm_dev,
+ surface_fmt, surface_flags)) {
+ SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO,
+ "GBM surface format not supported. Trying anyway.");
+ }
+
+ windata->gs = KMSDRM_LEGACY_gbm_surface_create(viddata->gbm_dev,
+ width, height, surface_fmt, surface_flags);
+
+ if (!windata->gs) {
+ return SDL_SetError("Could not create GBM surface");
+ }
+
+ /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
+ but we need an EGL surface NOW, or GL won't be able to render into any surface
+ and we won't see the first frame. */
+ SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
+ windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
+
+ if (windata->egl_surface == EGL_NO_SURFACE) {
+ ret = SDL_SetError("Could not create EGL window surface");
+ goto cleanup;
+ }
+
+ /* Current context passing to EGL is now done here. If something fails,
+ go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */
+ egl_context = (EGLContext)SDL_GL_GetCurrentContext();
+ ret = SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
+
+cleanup:
+
+ if (ret) {
+ /* Error (complete) cleanup. */
+ if (windata->gs) {
+ KMSDRM_LEGACY_gbm_surface_destroy(windata->gs);
+ windata->gs = NULL;
+ }
+ }
+
+ return ret;
+}
+
+int
+KMSDRM_LEGACY_VideoInit(_THIS)
+{
+ int ret = 0;
+
+ SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+ SDL_DisplayData *dispdata = NULL;
+ SDL_VideoDisplay display = {0};
+
+ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
+
+ viddata->video_init = SDL_FALSE;
+
+ dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
+ if (!dispdata) {
+ return SDL_OutOfMemory();
+ }
+
+ /* Get KMSDRM resources info and store what we need. Getting and storing
+ this info isn't a problem for VK compatibility.
+ For VK-incompatible initializations we have KMSDRM_LEGACY_GBMInit(), which is
+ called on window creation, and only when we know it's not a VK window. */
+ if (KMSDRM_LEGACY_DisplayDataInit(_this, dispdata)) {
+ ret = SDL_SetError("error getting KMS/DRM information");
+ goto cleanup;
+ }
+
+ /* Setup the single display that's available.
+ There's no problem with it being still incomplete. */
+ display.driverdata = dispdata;
+ display.desktop_mode.w = dispdata->mode.hdisplay;
+ display.desktop_mode.h = dispdata->mode.vdisplay;
+ display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
+ display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
+ display.current_mode = display.desktop_mode;
+
+ /* Add the display only when it's ready, */
+ SDL_AddVideoDisplay(&display, SDL_FALSE);
+
+#ifdef SDL_INPUT_LINUXEV
+ SDL_EVDEV_Init();
+#endif
+
+ viddata->video_init = SDL_TRUE;
+
+cleanup:
+
+ if (ret) {
+ /* Error (complete) cleanup */
+ if (dispdata->crtc) {
+ SDL_free(dispdata->crtc);
+ }
+ if (dispdata->connector) {
+ SDL_free(dispdata->connector);
+ }
+
+ SDL_free(dispdata);
+ }
+
+ return ret;
+}
+
+/* The driverdata pointers, like dispdata, viddata, windata, etc...
+ are freed by SDL internals, so not our job. */
void
KMSDRM_LEGACY_VideoQuit(_THIS)
{
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_LEGACY_VideoQuit()");
+ KMSDRM_LEGACY_DisplayDataDeinit(_this, dispdata);
- if (_this->gl_config.driver_loaded) {
- SDL_GL_UnloadLibrary();
- }
+#ifdef SDL_INPUT_LINUXEV
+ SDL_EVDEV_Quit();
+#endif
/* Clear out the window list */
SDL_free(viddata->windows);
viddata->windows = NULL;
viddata->max_windows = 0;
viddata->num_windows = 0;
-
- /* Restore saved CRTC settings */
- if (viddata->drm_fd >= 0 && dispdata && dispdata->conn && dispdata->saved_crtc) {
- drmModeConnector *conn = dispdata->conn;
- drmModeCrtc *crtc = dispdata->saved_crtc;
-
- int ret = KMSDRM_LEGACY_drmModeSetCrtc(viddata->drm_fd, crtc->crtc_id, crtc->buffer_id,
- crtc->x, crtc->y, &conn->connector_id, 1, &crtc->mode);
-
- if (ret != 0) {
- SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode");
- }
- }
- if (dispdata && dispdata->conn) {
- KMSDRM_LEGACY_drmModeFreeConnector(dispdata->conn);
- dispdata->conn = NULL;
- }
- if (dispdata && dispdata->saved_crtc) {
- KMSDRM_LEGACY_drmModeFreeCrtc(dispdata->saved_crtc);
- dispdata->saved_crtc = NULL;
- }
- if (viddata->gbm) {
- KMSDRM_LEGACY_gbm_device_destroy(viddata->gbm);
- viddata->gbm = NULL;
- }
- if (viddata->drm_fd >= 0) {
- close(viddata->drm_fd);
- SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
- viddata->drm_fd = -1;
- }
-#ifdef SDL_INPUT_LINUXEV
- SDL_EVDEV_Quit();
-#endif
+ viddata->video_init = SDL_FALSE;
}
void
KMSDRM_LEGACY_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
{
SDL_DisplayData *dispdata = display->driverdata;
- drmModeConnector *conn = dispdata->conn;
+ drmModeConnector *conn = dispdata->connector;
SDL_DisplayMode mode;
int i;
@@ -725,7 +1052,7 @@
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
- drmModeConnector *conn = dispdata->conn;
+ drmModeConnector *conn = dispdata->connector;
int i;
if (!modedata) {
@@ -749,99 +1076,52 @@
return 0;
}
-int
-KMSDRM_LEGACY_CreateWindow(_THIS, SDL_Window * window)
-{
- SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
- SDL_WindowData *windata;
- SDL_VideoDisplay *display;
-
- if (!_this->egl_data) {
- if (SDL_GL_LoadLibrary(NULL) < 0) {
- goto error;
- }
- }
-
- /* Allocate window internal data */
- windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
-
- if (!windata) {
- SDL_OutOfMemory();
- goto error;
- }
-
- /* Windows have one size for now */
- display = SDL_GetDisplayForWindow(window);
- window->w = display->desktop_mode.w;
- window->h = display->desktop_mode.h;
-
- /* Maybe you didn't ask for a fullscreen OpenGL window, but that's what you get */
- window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
-
- /* In case we want low-latency, double-buffer video, we take note here */
- windata->double_buffer = SDL_FALSE;
-
- if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
- windata->double_buffer = SDL_TRUE;
- }
-
- /* Setup driver data for this window */
- windata->viddata = viddata;
- window->driverdata = windata;
-
- if (KMSDRM_LEGACY_CreateSurfaces(_this, window)) {
- goto error;
- }
-
- /* Add window to the internal list of tracked windows. Note, while it may
- seem odd to support multiple fullscreen windows, some apps create an
- extra window as a dummy surface when working with multiple contexts */
- if (viddata->num_windows >= viddata->max_windows) {
- int new_max_windows = viddata->max_windows + 1;
- viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
- new_max_windows * sizeof(SDL_Window *));
- viddata->max_windows = new_max_windows;
-
- if (!viddata->windows) {
- SDL_OutOfMemory();
- goto error;
- }
- }
-
- viddata->windows[viddata->num_windows++] = window;
-
- /* Focus on the newly created window */
- SDL_SetMouseFocus(window);
- SDL_SetKeyboardFocus(window);
-
- return 0;
-
-error:
- KMSDRM_LEGACY_DestroyWindow(_this, window);
-
- return -1;
-}
-
void
-KMSDRM_LEGACY_DestroyWindow(_THIS, SDL_Window * window)
+KMSDRM_LEGACY_DestroyWindow(_THIS, SDL_Window *window)
{
SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
- SDL_VideoData *viddata;
- int i, j;
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ SDL_VideoData *viddata = windata->viddata;
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+ unsigned int i, j;
if (!windata) {
return;
}
- viddata = windata->viddata;
+ if ( !is_vulkan && dispdata->gbm_init ) {
- /* If this is the only window left, hide the cursor. */
- if (viddata->num_windows == 1)
- {
- SDL_ShowCursor(SDL_FALSE);
+ /* Destroy cursor GBM plane. */
+ KMSDRM_LEGACY_DeinitMouse(_this);
+
+ /* Destroy GBM surface and buffers. */
+ KMSDRM_LEGACY_DestroySurfaces(_this, window);
+
+ /* Unload EGL library. */
+ if (_this->egl_data) {
+ SDL_EGL_UnloadLibrary(_this);
+ }
+
+ /* Unload GL library. */
+ if (_this->gl_config.driver_loaded) {
+ SDL_GL_UnloadLibrary();
+ }
+
+ /* Free display plane, and destroy GBM device. */
+ KMSDRM_LEGACY_GBMDeinit(_this, dispdata);
}
- /* Remove from the internal window list */
+ else {
+ /* If we were in Vulkan mode, get out of it. */
+ if (viddata->vulkan_mode) {
+ viddata->vulkan_mode = SDL_FALSE;
+ }
+ }
+
+ /********************************************/
+ /* Remove from the internal SDL window list */
+ /********************************************/
+
for (i = 0; i < viddata->num_windows; i++) {
if (viddata->windows[i] == window) {
viddata->num_windows--;
@@ -854,14 +1134,199 @@
}
}
- KMSDRM_LEGACY_DestroySurfaces(_this, window);
-
+ /*********************************************************************/
+ /* Free the window driverdata. Bye bye, surface and buffer pointers! */
+ /*********************************************************************/
window->driverdata = NULL;
-
SDL_free(windata);
}
int
+KMSDRM_LEGACY_CreateWindow(_THIS, SDL_Window * window)
+{
+ SDL_WindowData *windata = NULL;
+ SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+ SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
+ SDL_DisplayData *dispdata = display->driverdata;
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+ SDL_bool vulkan_mode = viddata->vulkan_mode; /* Do we have any Vulkan windows? */
+ NativeDisplayType egl_display;
+ float ratio;
+ int ret = 0;
+
+ if ( !(dispdata->gbm_init) && !is_vulkan && !vulkan_mode ) {
+
+ /* If this is not a Vulkan Window, then this is a GL window, so at the
+ end of this function, we must have marked the window as being OPENGL
+ and we must have loaded the GL library: both things are needed so the
+ GL_CreateRenderer() and GL_LoadFunctions() calls in SDL_CreateWindow()
+ succeed without having to re-create the window.
+ We must load the EGL library too, which can't be loaded until the GBM
+ device has been created, because SDL_EGL_Library() function uses it. */
+
+ /* Maybe you didn't ask for an OPENGL window, but that's what you will get.
+ See previous comment on why. */
+ window->flags |= SDL_WINDOW_OPENGL;
+
+ /* Reopen FD, create gbm dev, setup display plane, etc,.
+ but only when we come here for the first time,
+ and only if it's not a VK window. */
+ if ((ret = KMSDRM_LEGACY_GBMInit(_this, dispdata))) {
+ goto cleanup;
+ }
+
+ /* Manually load the GL library. KMSDRM_EGL_LoadLibrary() has already
+ been called by SDL_CreateWindow() but we don't do anything there,
+ precisely to be able to load it here.
+ If we let SDL_CreateWindow() load the lib, it will be loaded
+ before we call KMSDRM_GBMInit(), causing GLES programs to fail. */
+ if (!_this->egl_data) {
+ egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
+ if (SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA)) {
+ goto cleanup;
+ }
+
+ if (SDL_GL_LoadLibrary(NULL) < 0) {
+ goto cleanup;
+ }
+ }
+
+ /* Can't init mouse stuff sooner because cursor plane is not ready,
+ so we do it here. */
+ KMSDRM_LEGACY_InitMouse(_this);
+
+ /* Since we take cursor buffer way from the cursor plane and
+ destroy the cursor GBM BO when we destroy a window, we must
+ also manually re-show the cursor on screen, if necessary,
+ when we create a window. */
+ KMSDRM_LEGACY_InitCursor();
+ }
+
+ /* Allocate window internal data */
+ windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
+ if (!windata) {
+ ret = SDL_OutOfMemory();
+ goto cleanup;
+ }
+
+ if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
+ || ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN))
+ {
+ windata->src_w = dispdata->mode.hdisplay;
+ windata->src_h = dispdata->mode.vdisplay;
+ windata->output_w = dispdata->mode.hdisplay;
+ windata->output_h = dispdata->mode.vdisplay;
+ windata->output_x = 0;
+ } else {
+ /* Normal non-fullscreen windows are scaled to the in-use video mode
+ using a PLANE connected to the CRTC, so get input size,
+ output (CRTC) size, and position. */
+ ratio = (float)window->w / (float)window->h;
+ windata->src_w = window->w;
+ windata->src_h = window->h;
+ windata->output_w = dispdata->mode.vdisplay * ratio;
+ windata->output_h = dispdata->mode.vdisplay;
+ windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
+ }
+
+ /* Don't force fullscreen on all windows: it confuses programs that try
+ to set a window fullscreen after creating it as non-fullscreen (sm64ex) */
+ // window->flags |= SDL_WINDOW_FULLSCREEN;
+
+ /* Setup driver data for this window */
+ windata->viddata = viddata;
+ window->driverdata = windata;
+
+ if (!is_vulkan && !vulkan_mode) {
+ /* Create the window surfaces. Needs the window diverdata in place. */
+ if ((ret = KMSDRM_LEGACY_CreateSurfaces(_this, window))) {
+ goto cleanup;
+ }
+ }
+
+ /* Add window to the internal list of tracked windows. Note, while it may
+ seem odd to support multiple fullscreen windows, some apps create an
+ extra window as a dummy surface when working with multiple contexts */
+ if (viddata->num_windows >= viddata->max_windows) {
+ unsigned int new_max_windows = viddata->max_windows + 1;
+ viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
+ new_max_windows * sizeof(SDL_Window *));
+ viddata->max_windows = new_max_windows;
+
+ if (!viddata->windows) {
+ ret = SDL_OutOfMemory();
+ goto cleanup;
+ }
+ }
+
+ viddata->windows[viddata->num_windows++] = window;
+
+ /* If we have just created a Vulkan window, establish that we are in Vulkan mode now. */
+ viddata->vulkan_mode = is_vulkan;
+
+ /* Focus on the newly created window */
+ SDL_SetMouseFocus(window);
+ SDL_SetKeyboardFocus(window);
+
+ /***********************************************************/
+ /* Tell SDL that the mouse has entered the window using an */
+ /* artificial event: we have no windowing system to tell */
+ /* SDL that it has happened. This makes SDL set the */
+ /* SDL_WINDOW_MOUSE_FOCUS on this window, thus fixing */
+ /* Scummvm sticky-on-sides software cursor. */
+ /***********************************************************/
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_ENTER, 0, 0);
+
+cleanup:
+
+ if (ret) {
+ /* Allocated windata will be freed in KMSDRM_DestroyWindow */
+ KMSDRM_LEGACY_DestroyWindow(_this, window);
+ }
+ return ret;
+}
+
+/*****************************************************************************/
+/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
+/* without destroying the window itself. */
+/* To be used by SetWindowSize() and SetWindowFullscreen(). */
+/*****************************************************************************/
+static int
+KMSDRM_LEGACY_ReconfigureWindow( _THIS, SDL_Window * window) {
+ SDL_WindowData *windata = window->driverdata;
+ SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+ SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+ float ratio;
+
+ if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
+ ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+
+ windata->src_w = dispdata->mode.hdisplay;
+ windata->src_h = dispdata->mode.vdisplay;
+ windata->output_w = dispdata->mode.hdisplay;
+ windata->output_h = dispdata->mode.vdisplay;
+ windata->output_x = 0;
+
+ } else {
+ /* Normal non-fullscreen windows are scaled using the CRTC,
+ so get output (CRTC) size and position, for AR correction. */
+ ratio = (float)window->w / (float)window->h;
+ windata->src_w = window->w;
+ windata->src_h = window->h;
+ windata->output_w = dispdata->mode.vdisplay * ratio;
+ windata->output_h = dispdata->mode.vdisplay;
+ windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
+ }
+
+ if (!is_vulkan) {
+ if (KMSDRM_LEGACY_CreateSurfaces(_this, window)) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
KMSDRM_LEGACY_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
{
return -1;
@@ -882,6 +1347,16 @@
void
KMSDRM_LEGACY_SetWindowSize(_THIS, SDL_Window * window)
{
+ if (KMSDRM_LEGACY_ReconfigureWindow(_this, window)) {
+ SDL_SetError("Can't reconfigure window on SetWindowSize.");
+ }
+}
+void
+KMSDRM_LEGACY_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+ if (KMSDRM_LEGACY_ReconfigureWindow(_this, window)) {
+ SDL_SetError("Can't reconfigure window on SetWindowFullscreen.");
+ }
}
void
KMSDRM_LEGACY_ShowWindow(_THIS, SDL_Window * window)
diff --git a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h
index 53fdb1b..53a30ea 100644
--- a/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h
+++ b/src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_video.h
@@ -37,7 +37,12 @@
{
int devindex; /* device index that was passed on creation */
int drm_fd; /* DRM file desc */
- struct gbm_device *gbm;
+ char devpath[32]; /* DRM dev path. */
+
+ struct gbm_device *gbm_dev;
+
+ SDL_bool video_init; /* Has VideoInit succeeded? */
+ SDL_bool vulkan_mode; /* Are we in Vulkan mode? One VK window is enough to be. */
SDL_Window **windows;
int max_windows;
@@ -53,26 +58,53 @@
typedef struct SDL_DisplayData
{
- uint32_t crtc_id;
- drmModeConnector *conn;
+ drmModeConnector *connector;
+ drmModeCrtc *crtc;
drmModeModeInfo mode;
- drmModeCrtc *saved_crtc; /* CRTC to restore on quit */
-} SDL_DisplayData;
+ drmModeModeInfo original_mode;
+ drmModeModeInfo preferred_mode;
+ drmModeCrtc *saved_crtc; /* CRTC to restore on quit */
+
+ uint32_t plane_id; /* ID of the primary plane used by the CRTC */
+
+ SDL_bool gbm_init;
+
+ /* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's driverdata struct,
+ because setting/unsetting up these is done on window creation/destruction,
+ where we may not have an SDL_Cursor at all (so no SDL_Cursor driverdata).
+ There's only one cursor GBM BO because we only support one cursor. */
+ struct gbm_bo *cursor_bo;
+ uint64_t cursor_w, cursor_h;
+
+ SDL_bool modeset_pending;
+
+} SDL_DisplayData;
typedef struct SDL_WindowData
{
SDL_VideoData *viddata;
+ /* SDL internals expect EGL surface to be here, and in KMSDRM the GBM surface is
+ what supports the EGL surface on the driver side, so all these surfaces and buffers
+ are expected to be here, in the struct pointed by SDL_Window driverdata pointer:
+ this one. So don't try to move these to dispdata! */
struct gbm_surface *gs;
- struct gbm_bo *curr_bo;
+ struct gbm_bo *bo;
struct gbm_bo *next_bo;
- struct gbm_bo *crtc_bo;
+
SDL_bool waiting_for_flip;
SDL_bool double_buffer;
int egl_surface_dirty;
EGLSurface egl_surface;
+ /* For scaling and AR correction. */
+ int32_t src_w;
+ int32_t src_h;
+ int32_t output_w;
+ int32_t output_h;
+ int32_t output_x;
+
} SDL_WindowData;
typedef struct KMSDRM_LEGACY_FBInfo
@@ -101,6 +133,7 @@
void KMSDRM_LEGACY_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon);
void KMSDRM_LEGACY_SetWindowPosition(_THIS, SDL_Window * window);
void KMSDRM_LEGACY_SetWindowSize(_THIS, SDL_Window * window);
+void KMSDRM_LEGACY_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen);
void KMSDRM_LEGACY_ShowWindow(_THIS, SDL_Window * window);
void KMSDRM_LEGACY_HideWindow(_THIS, SDL_Window * window);
void KMSDRM_LEGACY_RaiseWindow(_THIS, SDL_Window * window);