From b17c49509b78beb19c44a736972abba211c971a3 Mon Sep 17 00:00:00 2001 From: Manuel Alfayate Corchete Date: Fri, 29 Jan 2021 18:08:04 +0100 Subject: [PATCH] [KMS/DRM] Patch for bug #5513. KMSDRM backend can now manage and use several displays. --- src/video/kmsdrm/SDL_kmsdrmmouse.c | 412 ++++++++++++++++------------- src/video/kmsdrm/SDL_kmsdrmmouse.h | 5 +- src/video/kmsdrm/SDL_kmsdrmvideo.c | 315 ++++++++++++---------- src/video/kmsdrm/SDL_kmsdrmvideo.h | 1 + 4 files changed, 411 insertions(+), 322 deletions(-) diff --git a/src/video/kmsdrm/SDL_kmsdrmmouse.c b/src/video/kmsdrm/SDL_kmsdrmmouse.c index a2a7c2eaf..e66953ad9 100644 --- a/src/video/kmsdrm/SDL_kmsdrmmouse.c +++ b/src/video/kmsdrm/SDL_kmsdrmmouse.c @@ -83,6 +83,182 @@ void legacy_alpha_premultiply_ARGB8888 (uint32_t *pixel) { (*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0); } +/* Given a display's driverdata, destroy the cursor BO for it. + To be called from KMSDRM_DestroyWindow(), as that's where we + destroy the driverdata for the window's display. */ +void +KMSDRM_DestroyCursorBO (_THIS, SDL_VideoDisplay *display) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata; + + /* Destroy the curso GBM BO. */ + if (dispdata->cursor_bo) { + KMSDRM_gbm_bo_destroy(dispdata->cursor_bo); + dispdata->cursor_bo = NULL; + } +} + +/* Given a display's driverdata, create the cursor BO for it. + To be called from KMSDRM_CreateWindow(), as that's where we + build a window and assign a display to it. */ +void +KMSDRM_CreateCursorBO (SDL_VideoDisplay *display) { + + SDL_VideoDevice *dev = SDL_GetVideoDevice(); + SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata); + SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata; + + if (!KMSDRM_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_drmGetCap(viddata->drm_fd, + DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) || + KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, + &dispdata->cursor_h)) + { + SDL_SetError("Could not get the recommended GBM cursor size"); + return; + } + + if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) { + SDL_SetError("Could not get an usable GBM cursor size"); + return; + } + + dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev, + dispdata->cursor_w, dispdata->cursor_h, + GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR); + + if (!dispdata->cursor_bo) { + SDL_SetError("Could not create GBM cursor BO"); + return; + } +} + +/* Remove a cursor buffer from a display's DRM cursor BO. */ +int +KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display) +{ + int ret = 0; + SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata; + SDL_VideoDevice *video_device = SDL_GetVideoDevice(); + SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata); + + ret = KMSDRM_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; +} + +/* Dump a cursor buffer to a display's DRM cursor BO. */ +int +KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor) +{ + SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata; + KMSDRM_CursorData *curdata = (KMSDRM_CursorData *) cursor->driverdata; + SDL_VideoDevice *video_device = SDL_GetVideoDevice(); + SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata); + + uint32_t bo_handle; + size_t bo_stride; + size_t bufsize; + uint32_t *ready_buffer = NULL; + uint32_t pixel; + + int i,j; + int ret; + + if (!curdata || !dispdata->cursor_bo) { + return SDL_SetError("Cursor or display not initialized properly."); + } + + /* Prepare a buffer we can dump to our GBM BO (different + size, alpha premultiplication...) */ + bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo); + bufsize = bo_stride * dispdata->cursor_h; + + ready_buffer = (uint32_t*)SDL_calloc(1, bufsize); + + if (!ready_buffer) { + ret = SDL_OutOfMemory(); + goto cleanup; + } + + /* 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_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_gbm_bo_get_handle(dispdata->cursor_bo).u32; + if (curdata->hot_x == 0 && curdata->hot_y == 0) { + ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, + bo_handle, dispdata->cursor_w, dispdata->cursor_h); + } else { + ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id, + bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y); + } + + if (ret) { + ret = SDL_SetError("Failed to set DRM cursor."); + goto cleanup; + } + + if (ret) { + ret = SDL_SetError("Failed to reset cursor position."); + goto cleanup; + } + +cleanup: + + if (ready_buffer) { + SDL_free(ready_buffer); + } + return ret; +} + +/* This is only for freeing the SDL_cursor.*/ +static void +KMSDRM_FreeCursor(SDL_Cursor * cursor) +{ + KMSDRM_CursorData *curdata; + + /* Even if the cursor is not ours, free it. */ + if (cursor) { + curdata = (KMSDRM_CursorData *) cursor->driverdata; + /* 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); + } +} + /* 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. */ @@ -176,17 +352,11 @@ KMSDRM_InitCursor() SDL_Mouse *mouse = NULL; mouse = SDL_GetMouse(); - if (!mouse) { - return; - } - if (!(mouse->cur_cursor)) { - return; - } - - if (!(mouse->cursor_shown)) { + if (!mouse || !mouse->cur_cursor || !mouse->cursor_shown) { return; } + /* Re-dump cursor buffer to the GBM BO of the focused window display. */ KMSDRM_ShowCursor(mouse->cur_cursor); } @@ -194,124 +364,60 @@ KMSDRM_InitCursor() static int KMSDRM_ShowCursor(SDL_Cursor * cursor) { - SDL_VideoDevice *video_device = SDL_GetVideoDevice(); - SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata); - SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); + SDL_VideoDisplay *display; + SDL_Window *window; SDL_Mouse *mouse; - KMSDRM_CursorData *curdata; - uint32_t bo_handle; + int num_displays, i; + int ret = 0; - size_t bo_stride; - size_t bufsize; - uint32_t *ready_buffer = NULL; - uint32_t pixel; - - int i,j; - int ret; + /* Get the mouse focused window, if any. */ mouse = SDL_GetMouse(); if (!mouse) { return SDL_SetError("No mouse."); } - /*********************************************************/ - /* 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_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; - } + window = mouse->focus; - /*****************************************************/ - /* If cursor != NULL, DO show cursor on it's window. */ - /*****************************************************/ - curdata = (KMSDRM_CursorData *) cursor->driverdata; + if (!window || !cursor) { - if (!curdata || !dispdata->cursor_bo) { - return SDL_SetError("Cursor not initialized properly."); - } + /* If no window is focused by mouse or cursor is NULL, + since we have no window (no mouse->focus) and hence + we have no display, we simply hide mouse on all displays. + This happens on video quit, where we get here after + the mouse focus has been unset, yet SDL wants to + restore the system default cursor (makes no sense here). */ - /* Prepare a buffer we can dump to our GBM BO (different - size, alpha premultiplication...) */ - bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo); - bufsize = bo_stride * dispdata->cursor_h; + num_displays = SDL_GetNumVideoDisplays(); - ready_buffer = (uint32_t*)SDL_calloc(1, bufsize); + /* Iterate on the displays hidding the cursor. */ + for (i = 0; i < num_displays; i++) { + display = SDL_GetDisplay(i); + ret = KMSDRM_RemoveCursorFromBO(display); + } - if (!ready_buffer) { - ret = SDL_OutOfMemory(); - goto cleanup; - } - - /* 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_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_gbm_bo_get_handle(dispdata->cursor_bo).u32; - if (curdata->hot_x == 0 && curdata->hot_y == 0) { - ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, - bo_handle, dispdata->cursor_w, dispdata->cursor_h); } else { - ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id, - bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y); + + display = SDL_GetDisplayForWindow(window); + + if (display) { + + if (cursor) { + /* Dump the cursor to the display DRM cursor BO so it becomes visible + on that display. */ + ret = KMSDRM_DumpCursorToBO(display, cursor); + + } else { + /* Hide the cursor on that display. */ + ret = KMSDRM_RemoveCursorFromBO(display); + } + } } - if (ret) { - ret = SDL_SetError("Failed to set DRM cursor."); - goto cleanup; - } - -cleanup: - - if (ready_buffer) { - SDL_free(ready_buffer); - } return ret; } -/* This is only for freeing the SDL_cursor.*/ -static void -KMSDRM_FreeCursor(SDL_Cursor * cursor) -{ - KMSDRM_CursorData *curdata; - - /* Even if the cursor is not ours, free it. */ - if (cursor) { - curdata = (KMSDRM_CursorData *) cursor->driverdata; - /* 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); - } -} - /* Warp the mouse to (x,y) */ static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y) @@ -325,9 +431,12 @@ static int KMSDRM_WarpMouseGlobal(int x, int y) { SDL_Mouse *mouse = SDL_GetMouse(); - SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); - if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) { + if (mouse && mouse->cur_cursor && mouse->focus) { + + SDL_Window *window = mouse->focus; + SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + /* Update internal mouse position. */ SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y); @@ -354,28 +463,11 @@ KMSDRM_WarpMouseGlobal(int x, int y) return 0; } -/* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */ void -KMSDRM_DeinitMouse(_THIS) +KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display) { - 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_gbm_bo_destroy(dispdata->cursor_bo); - dispdata->cursor_bo = NULL; - } -} - -/* Create cursor BO. */ -void -KMSDRM_InitMouse(_THIS) -{ - 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(); + SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata; mouse->CreateCursor = KMSDRM_CreateCursor; mouse->ShowCursor = KMSDRM_ShowCursor; @@ -384,61 +476,17 @@ KMSDRM_InitMouse(_THIS) mouse->WarpMouse = KMSDRM_WarpMouse; mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal; - /************************************************/ - /* Create the cursor GBM BO, if we haven't yet. */ - /************************************************/ - if (!dispdata->cursor_bo) { - - if (!KMSDRM_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_drmGetCap(viddata->drm_fd, - DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) || - KMSDRM_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_gbm_bo_create(viddata->gbm_dev, - dispdata->cursor_w, dispdata->cursor_h, - GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR); - - 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 expects to set the default cursor of the display when we init the mouse, but since we have moved the KMSDRM_InitMouse() call to KMSDRM_CreateWindow(), we end up calling KMSDRM_InitMouse() every time we create a window, so we have to prevent this from being done every time a new window is created. If we don't, new default cursors would stack up on mouse->cursors and SDL would have to hide and delete them at quit, not to mention the memory leak... */ + if(dispdata->set_default_cursor_pending) { SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor()); dispdata->set_default_cursor_pending = SDL_FALSE; } - - return; - -cleanup: - if (dispdata->cursor_bo) { - KMSDRM_gbm_bo_destroy(dispdata->cursor_bo); - dispdata->cursor_bo = NULL; - } } void @@ -452,23 +500,25 @@ static void KMSDRM_MoveCursor(SDL_Cursor * cursor) { SDL_Mouse *mouse = SDL_GetMouse(); - SDL_Window *window; - SDL_DisplayData *dispdata; - int drm_fd, ret, screen_y; /* 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 && mouse->focus) { + if (mouse && mouse->cur_cursor && mouse->focus) { - window = mouse->focus; - dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + SDL_Window *window = mouse->focus; + SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; + + if (!dispdata->cursor_bo) { + SDL_SetError("Cursor not initialized properly."); + return; + } + + drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(dispdata->cursor_bo)); /* Correct the Y coordinate, because DRM mouse coordinates start on screen top. */ screen_y = dispdata->mode.vdisplay - window->h + mouse->y; - drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(dispdata->cursor_bo)); - ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, screen_y); if (ret) { diff --git a/src/video/kmsdrm/SDL_kmsdrmmouse.h b/src/video/kmsdrm/SDL_kmsdrmmouse.h index 9a6bad0bd..36625acdb 100644 --- a/src/video/kmsdrm/SDL_kmsdrmmouse.h +++ b/src/video/kmsdrm/SDL_kmsdrmmouse.h @@ -43,10 +43,11 @@ typedef struct _KMSDRM_CursorData } KMSDRM_CursorData; -extern void KMSDRM_InitMouse(_THIS); -extern void KMSDRM_DeinitMouse(_THIS); +extern void KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display); extern void KMSDRM_QuitMouse(_THIS); +extern void KMSDRM_CreateCursorBO(SDL_VideoDisplay *display); +extern void KMSDRM_DestroyCursorBO(_THIS, SDL_VideoDisplay *display); extern void KMSDRM_InitCursor(); #endif /* SDL_KMSDRM_mouse_h_ */ diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c index 8af915218..79e2a7748 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -457,83 +457,64 @@ uint32_t width, uint32_t height, uint32_t refresh_rate){ /* _this is a SDL_VideoDevice * */ /*****************************************************************************/ -/* Deinitializes the dispdata members needed for KMSDRM operation that are - inoffeensive for VK compatibility. */ -void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) { - /* Free connector */ - if (dispdata && dispdata->connector) { - KMSDRM_drmModeFreeConnector(dispdata->connector); - dispdata->connector = NULL; - } +/* Deinitializes the driverdata of the SDL Displays in the SDL display list. */ +void KMSDRM_DeinitDisplays (_THIS) { - /* Free CRTC */ - if (dispdata && dispdata->crtc) { - KMSDRM_drmModeFreeCrtc(dispdata->crtc); - dispdata->crtc = NULL; + SDL_DisplayData *dispdata; + int num_displays, i; + + num_displays = SDL_GetNumVideoDisplays(); + + /* Iterate on the SDL Display list. */ + for (i = 0; i < num_displays; i++) { + + /* Get the driverdata for this display */ + dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(i); + + /* Free connector */ + if (dispdata && dispdata->connector) { + KMSDRM_drmModeFreeConnector(dispdata->connector); + dispdata->connector = NULL; + } + + /* Free CRTC */ + if (dispdata && dispdata->crtc) { + KMSDRM_drmModeFreeCrtc(dispdata->crtc); + dispdata->crtc = NULL; + } } } -/* 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_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { +/* Gets a DRM connector, builds an SDL_Display with it, and adds it to the + list of SDL Displays. */ +void KMSDRM_AddDisplay (_THIS, drmModeConnector *connector, drmModeRes *resources) { + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); - - drmModeRes *resources = NULL; + SDL_DisplayData *dispdata = NULL; + SDL_VideoDisplay display = {0}; drmModeEncoder *encoder = NULL; - drmModeConnector *connector = NULL; drmModeCrtc *crtc = NULL; - + int i, j; int ret = 0; - unsigned i,j; + /* Reserve memory for the new display's driverdata. */ + dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData)); + if (!dispdata) { + ret = SDL_OutOfMemory(); + } + + /* Initialize some of the members of the new display's driverdata + to sane values. */ dispdata->gbm_init = SDL_FALSE; dispdata->modeset_pending = SDL_FALSE; dispdata->cursor_bo = NULL; - /* Open /dev/dri/cardNN (/dev/drmN if on OpenBSD) */ - SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), KMSDRM_DRI_CARDPATHFMT, viddata->devindex); - - 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", viddata->devpath); - goto cleanup; - } - - SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); - - /* Get all of the available connectors / devices / crtcs */ - resources = KMSDRM_drmModeGetResources(viddata->drm_fd); - if (!resources) { - ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd); - goto cleanup; - } - - /* Iterate on the available connectors to find a connected connector. */ - for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, - resources->connectors[i]); - - if (!conn) { - continue; - } - - if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) { - connector = conn; - break; - } - - KMSDRM_drmModeFreeConnector(conn); - } - - if (!connector) { - ret = SDL_SetError("No currently active connector found."); - goto cleanup; - } + /* Since we create and show the default cursor on KMSDRM_InitMouse() and + we call KMSDRM_InitMouse() everytime we create a new window, we have + to be sure to create and show the default cursor only the first time. + If we don't, new default cursors would stack up on mouse->cursors and SDL + would have to hide and delete them at quit, not to mention the memory leak... */ + dispdata->set_default_cursor_pending = SDL_TRUE; /* Try to find the connector's current encoder */ for (i = 0; i < resources->count_encoders; i++) { @@ -558,7 +539,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { resources->encoders[i]); if (!encoder) { - continue; + continue; } for (j = 0; j < connector->count_encoders; j++) { @@ -568,7 +549,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { } if (j != connector->count_encoders) { - break; + break; } KMSDRM_drmModeFreeEncoder(encoder); @@ -577,7 +558,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { } if (!encoder) { - ret = SDL_SetError("No connected encoder found."); + ret = SDL_SetError("No connected encoder found for connector."); goto cleanup; } @@ -597,11 +578,21 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { } if (!crtc) { - ret = SDL_SetError("No CRTC found."); + ret = SDL_SetError("No CRTC found for connector."); goto cleanup; } - /* Figure out the default mode to be set. */ + /*********************************************/ + /* Create an SDL Display for this connector. */ + /*********************************************/ + + /*********************************************/ + /* Part 1: setup the SDL_Display driverdata. */ + /*********************************************/ + + /* Get the mode currently setup for this display, + which is the mode currently setup on the CRTC + we found for the active connector. */ dispdata->mode = crtc->mode; /* Save the original mode for restoration on quit. */ @@ -612,11 +603,108 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { goto cleanup; } - /* Store the connector and crtc for future use. These are all we keep - from this function, and these are just structs, inoffensive to VK. */ + /* Store the connector and crtc for this display. */ dispdata->connector = connector; dispdata->crtc = crtc; + /*****************************************/ + /* Part 2: setup the SDL_Display itself. */ + /*****************************************/ + + /* Setup the display. + 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 to the list of SDL displays. */ + SDL_AddVideoDisplay(&display, SDL_FALSE); + +cleanup: + if (encoder) + KMSDRM_drmModeFreeEncoder(encoder); + if (ret) { + /* Error (complete) cleanup */ + if (dispdata->connector) { + KMSDRM_drmModeFreeConnector(dispdata->connector); + dispdata->connector = NULL; + } + if (dispdata->crtc) { + KMSDRM_drmModeFreeCrtc(dispdata->crtc); + dispdata->crtc = NULL; + } + if (dispdata) { + SDL_free(dispdata); + } + } +} + +/* Initializes the list of SDL displays: we build a new display for each + connecter connector we find. + 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_InitDisplays (_THIS) { + + SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); + drmModeRes *resources = NULL; + + int ret = 0; + int i; + + /* Open /dev/dri/cardNN (/dev/drmN if on OpenBSD) */ + SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), KMSDRM_DRI_CARDPATHFMT, viddata->devindex); + + 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", viddata->devpath); + goto cleanup; + } + + SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); + + /* Get all of the available connectors / devices / crtcs */ + resources = KMSDRM_drmModeGetResources(viddata->drm_fd); + if (!resources) { + ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd); + goto cleanup; + } + + /* Iterate on the available connectors. For every connected connector, + we create an SDL_Display and add it to the list of SDL Displays. */ + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector = KMSDRM_drmModeGetConnector(viddata->drm_fd, + resources->connectors[i]); + + if (!connector) { + continue; + } + + if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes) { + /* If it's a connected connector with available videomodes, try to add + an SDL Display representing it. KMSDRM_AddDisplay() is purposely void, + so if it fails (no encoder for connector, no valid video mode for + connector etc...) we can keep looking for connected connectors. */ + KMSDRM_AddDisplay (_this, connector, resources); + } + else { + /* If it's not, free it now. */ + KMSDRM_drmModeFreeConnector(connector); + } + } + + /* Have we added any SDL displays? */ + if (!SDL_GetNumVideoDisplays()) { + ret = SDL_SetError("No connected displays found."); + goto cleanup; + } + /***********************************/ /* Block for Vulkan compatibility. */ /***********************************/ @@ -627,26 +715,14 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) { viddata->drm_fd = -1; cleanup: - if (encoder) - KMSDRM_drmModeFreeEncoder(encoder); if (resources) KMSDRM_drmModeFreeResources(resources); if (ret) { - /* Error (complete) cleanup */ - if (dispdata->connector) { - KMSDRM_drmModeFreeConnector(dispdata->connector); - dispdata->connector = NULL; - } - if (dispdata->crtc) { - KMSDRM_drmModeFreeCrtc(dispdata->crtc); - dispdata->crtc = NULL; - } if (viddata->drm_fd >= 0) { close(viddata->drm_fd); viddata->drm_fd = -1; } } - return ret; } @@ -838,68 +914,26 @@ KMSDRM_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_GBMInit(), which is called on window creation, and only when we know it's not a VK window. */ - if (KMSDRM_DisplayDataInit(_this, dispdata)) { + if (KMSDRM_InitDisplays(_this)) { 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(); #elif defined(SDL_INPUT_WSCONS) SDL_WSCONS_Init(); #endif - /* Since we create and show the default cursor on KMSDRM_InitMouse() and - we call KMSDRM_InitMouse() everytime we create a new window, we have - to be sure to create and show the default cursor only the first time. - If we don't, new default cursors would stack up on mouse->cursors and SDL - would have to hide and delete them at quit, not to mention the memory leak... */ - dispdata->set_default_cursor_pending = SDL_TRUE; - 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; } @@ -909,9 +943,8 @@ void KMSDRM_VideoQuit(_THIS) { SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); - SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); - KMSDRM_DisplayDataDeinit(_this, dispdata); + KMSDRM_DeinitDisplays(_this); #ifdef SDL_INPUT_LINUXEV SDL_EVDEV_Quit(); @@ -1010,8 +1043,8 @@ KMSDRM_DestroyWindow(_THIS, SDL_Window *window) if ( !is_vulkan && dispdata->gbm_init ) { - /* Destroy cursor GBM plane. */ - KMSDRM_DeinitMouse(_this); + /* Destroy the window display's cursor GBM BO. */ + KMSDRM_DestroyCursorBO(_this, SDL_GetDisplayForWindow(window)); /* Destroy GBM surface and buffers. */ KMSDRM_DestroySurfaces(_this, window); @@ -1126,15 +1159,20 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window) goto cleanup; } } - - /* Can't init mouse stuff sooner because cursor plane is not ready, - so we do it here. */ - KMSDRM_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. */ + /* Create the cursor BO for the display of this window, + now that we know this is not a VK window. */ + KMSDRM_CreateCursorBO(display); + + /* Init mouse (=create and set the default cursor), + now that we know this is not a VK window. */ + KMSDRM_InitMouse(_this, display); + + /* When we destroy a window, we remove the cursor buffer from + the cursor plane and destroy the cursor GBM BO, but SDL expects + that we keep showing the visible cursors bewteen window + destruction/creation cycles. So we must manually re-show the + visible cursors, if necessary, when we create a window. */ KMSDRM_InitCursor(); } @@ -1256,7 +1294,6 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) { as pending so it's done on SwapWindow. */ KMSDRM_CreateSurfaces(_this, window); dispdata->modeset_pending = SDL_TRUE; - } int diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h index 29cfc1833..9b5a573b9 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.h +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h @@ -138,6 +138,7 @@ void KMSDRM_RaiseWindow(_THIS, SDL_Window * window); void KMSDRM_MaximizeWindow(_THIS, SDL_Window * window); void KMSDRM_MinimizeWindow(_THIS, SDL_Window * window); void KMSDRM_RestoreWindow(_THIS, SDL_Window * window); +void KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed); void KMSDRM_DestroyWindow(_THIS, SDL_Window * window); /* Window manager function */