[KMS/DRM] Patch for bug #5513. KMSDRM backend can now manage and use several displays.

This commit is contained in:
Manuel Alfayate Corchete 2021-01-29 18:08:04 +01:00
parent a78bce9e38
commit b17c49509b
4 changed files with 411 additions and 322 deletions

View file

@ -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); (*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. /* 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 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. */ in dispata) is destroyed and recreated when we recreate windows, etc. */
@ -176,17 +352,11 @@ KMSDRM_InitCursor()
SDL_Mouse *mouse = NULL; SDL_Mouse *mouse = NULL;
mouse = SDL_GetMouse(); mouse = SDL_GetMouse();
if (!mouse) { if (!mouse || !mouse->cur_cursor || !mouse->cursor_shown) {
return;
}
if (!(mouse->cur_cursor)) {
return;
}
if (!(mouse->cursor_shown)) {
return; return;
} }
/* Re-dump cursor buffer to the GBM BO of the focused window display. */
KMSDRM_ShowCursor(mouse->cur_cursor); KMSDRM_ShowCursor(mouse->cur_cursor);
} }
@ -194,124 +364,60 @@ KMSDRM_InitCursor()
static int static int
KMSDRM_ShowCursor(SDL_Cursor * cursor) KMSDRM_ShowCursor(SDL_Cursor * cursor)
{ {
SDL_VideoDevice *video_device = SDL_GetVideoDevice(); SDL_VideoDisplay *display;
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata); SDL_Window *window;
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_Mouse *mouse; SDL_Mouse *mouse;
KMSDRM_CursorData *curdata;
uint32_t bo_handle; int num_displays, i;
int ret = 0;
size_t bo_stride; /* Get the mouse focused window, if any. */
size_t bufsize;
uint32_t *ready_buffer = NULL;
uint32_t pixel;
int i,j;
int ret;
mouse = SDL_GetMouse(); mouse = SDL_GetMouse();
if (!mouse) { if (!mouse) {
return SDL_SetError("No mouse."); return SDL_SetError("No mouse.");
} }
/*********************************************************/ window = mouse->focus;
/* 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;
}
/*****************************************************/ if (!window || !cursor) {
/* If cursor != NULL, DO show cursor on it's window. */
/*****************************************************/
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (!curdata || !dispdata->cursor_bo) { /* If no window is focused by mouse or cursor is NULL,
return SDL_SetError("Cursor not initialized properly."); 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 num_displays = SDL_GetNumVideoDisplays();
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); /* 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 { } 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; 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) */ /* Warp the mouse to (x,y) */
static void static void
KMSDRM_WarpMouse(SDL_Window * window, int x, int y) KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
@ -325,9 +431,12 @@ static int
KMSDRM_WarpMouseGlobal(int x, int y) KMSDRM_WarpMouseGlobal(int x, int y)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); 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. */ /* Update internal mouse position. */
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y); SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
@ -354,28 +463,11 @@ KMSDRM_WarpMouseGlobal(int x, int y)
return 0; return 0;
} }
/* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */
void 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_Mouse *mouse = SDL_GetMouse();
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
mouse->CreateCursor = KMSDRM_CreateCursor; mouse->CreateCursor = KMSDRM_CreateCursor;
mouse->ShowCursor = KMSDRM_ShowCursor; mouse->ShowCursor = KMSDRM_ShowCursor;
@ -384,61 +476,17 @@ KMSDRM_InitMouse(_THIS)
mouse->WarpMouse = KMSDRM_WarpMouse; mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal; mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
/************************************************/ /* SDL expects to set the default cursor of the display when we init the mouse,
/* 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,
but since we have moved the KMSDRM_InitMouse() call to KMSDRM_CreateWindow(), 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 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. 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 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... */ would have to hide and delete them at quit, not to mention the memory leak... */
if(dispdata->set_default_cursor_pending) { if(dispdata->set_default_cursor_pending) {
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor()); SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
dispdata->set_default_cursor_pending = SDL_FALSE; 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 void
@ -452,23 +500,25 @@ static void
KMSDRM_MoveCursor(SDL_Cursor * cursor) KMSDRM_MoveCursor(SDL_Cursor * cursor)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
SDL_Window *window;
SDL_DisplayData *dispdata;
int drm_fd, ret, screen_y; int drm_fd, ret, screen_y;
/* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity! /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
That's why we move the cursor graphic ONLY. */ 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; SDL_Window *window = mouse->focus;
dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 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. */ /* Correct the Y coordinate, because DRM mouse coordinates start on screen top. */
screen_y = dispdata->mode.vdisplay - window->h + mouse->y; 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); ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, screen_y);
if (ret) { if (ret) {

View file

@ -43,10 +43,11 @@ typedef struct _KMSDRM_CursorData
} KMSDRM_CursorData; } KMSDRM_CursorData;
extern void KMSDRM_InitMouse(_THIS); extern void KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display);
extern void KMSDRM_DeinitMouse(_THIS);
extern void KMSDRM_QuitMouse(_THIS); extern void KMSDRM_QuitMouse(_THIS);
extern void KMSDRM_CreateCursorBO(SDL_VideoDisplay *display);
extern void KMSDRM_DestroyCursorBO(_THIS, SDL_VideoDisplay *display);
extern void KMSDRM_InitCursor(); extern void KMSDRM_InitCursor();
#endif /* SDL_KMSDRM_mouse_h_ */ #endif /* SDL_KMSDRM_mouse_h_ */

View file

@ -457,83 +457,64 @@ uint32_t width, uint32_t height, uint32_t refresh_rate){
/* _this is a SDL_VideoDevice * */ /* _this is a SDL_VideoDevice * */
/*****************************************************************************/ /*****************************************************************************/
/* Deinitializes the dispdata members needed for KMSDRM operation that are /* Deinitializes the driverdata of the SDL Displays in the SDL display list. */
inoffeensive for VK compatibility. */ void KMSDRM_DeinitDisplays (_THIS) {
void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
/* Free connector */
if (dispdata && dispdata->connector) {
KMSDRM_drmModeFreeConnector(dispdata->connector);
dispdata->connector = NULL;
}
/* Free CRTC */ SDL_DisplayData *dispdata;
if (dispdata && dispdata->crtc) { int num_displays, i;
KMSDRM_drmModeFreeCrtc(dispdata->crtc);
dispdata->crtc = NULL; 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 /* Gets a DRM connector, builds an SDL_Display with it, and adds it to the
inoffeensive for VK compatibility, except we must leave the drm_fd list of SDL Displays. */
closed when we get to the end of this function. void KMSDRM_AddDisplay (_THIS, drmModeConnector *connector, drmModeRes *resources) {
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) {
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = NULL;
drmModeRes *resources = NULL; SDL_VideoDisplay display = {0};
drmModeEncoder *encoder = NULL; drmModeEncoder *encoder = NULL;
drmModeConnector *connector = NULL;
drmModeCrtc *crtc = NULL; drmModeCrtc *crtc = NULL;
int i, j;
int ret = 0; 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->gbm_init = SDL_FALSE;
dispdata->modeset_pending = SDL_FALSE; dispdata->modeset_pending = SDL_FALSE;
dispdata->cursor_bo = NULL; dispdata->cursor_bo = NULL;
/* Open /dev/dri/cardNN (/dev/drmN if on OpenBSD) */ /* Since we create and show the default cursor on KMSDRM_InitMouse() and
SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), KMSDRM_DRI_CARDPATHFMT, viddata->devindex); 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.
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", viddata->devpath); If we don't, new default cursors would stack up on mouse->cursors and SDL
viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); would have to hide and delete them at quit, not to mention the memory leak... */
dispdata->set_default_cursor_pending = SDL_TRUE;
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;
}
/* Try to find the connector's current encoder */ /* Try to find the connector's current encoder */
for (i = 0; i < resources->count_encoders; i++) { for (i = 0; i < resources->count_encoders; i++) {
@ -558,7 +539,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
resources->encoders[i]); resources->encoders[i]);
if (!encoder) { if (!encoder) {
continue; continue;
} }
for (j = 0; j < connector->count_encoders; j++) { for (j = 0; j < connector->count_encoders; j++) {
@ -568,7 +549,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
} }
if (j != connector->count_encoders) { if (j != connector->count_encoders) {
break; break;
} }
KMSDRM_drmModeFreeEncoder(encoder); KMSDRM_drmModeFreeEncoder(encoder);
@ -577,7 +558,7 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
} }
if (!encoder) { if (!encoder) {
ret = SDL_SetError("No connected encoder found."); ret = SDL_SetError("No connected encoder found for connector.");
goto cleanup; goto cleanup;
} }
@ -597,11 +578,21 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
} }
if (!crtc) { if (!crtc) {
ret = SDL_SetError("No CRTC found."); ret = SDL_SetError("No CRTC found for connector.");
goto cleanup; 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; dispdata->mode = crtc->mode;
/* Save the original mode for restoration on quit. */ /* Save the original mode for restoration on quit. */
@ -612,11 +603,108 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
goto cleanup; goto cleanup;
} }
/* Store the connector and crtc for future use. These are all we keep /* Store the connector and crtc for this display. */
from this function, and these are just structs, inoffensive to VK. */
dispdata->connector = connector; dispdata->connector = connector;
dispdata->crtc = crtc; 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. */ /* Block for Vulkan compatibility. */
/***********************************/ /***********************************/
@ -627,26 +715,14 @@ int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
viddata->drm_fd = -1; viddata->drm_fd = -1;
cleanup: cleanup:
if (encoder)
KMSDRM_drmModeFreeEncoder(encoder);
if (resources) if (resources)
KMSDRM_drmModeFreeResources(resources); KMSDRM_drmModeFreeResources(resources);
if (ret) { 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) { if (viddata->drm_fd >= 0) {
close(viddata->drm_fd); close(viddata->drm_fd);
viddata->drm_fd = -1; viddata->drm_fd = -1;
} }
} }
return ret; return ret;
} }
@ -838,68 +914,26 @@ KMSDRM_VideoInit(_THIS)
int ret = 0; int ret = 0;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = NULL;
SDL_VideoDisplay display = {0};
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()"); SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
viddata->video_init = SDL_FALSE; 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 /* Get KMSDRM resources info and store what we need. Getting and storing
this info isn't a problem for VK compatibility. this info isn't a problem for VK compatibility.
For VK-incompatible initializations we have KMSDRM_GBMInit(), which is 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. */ 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"); 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 #ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Init(); SDL_EVDEV_Init();
#elif defined(SDL_INPUT_WSCONS) #elif defined(SDL_INPUT_WSCONS)
SDL_WSCONS_Init(); SDL_WSCONS_Init();
#endif #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; 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; return ret;
} }
@ -909,9 +943,8 @@ void
KMSDRM_VideoQuit(_THIS) KMSDRM_VideoQuit(_THIS)
{ {
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); 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 #ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Quit(); SDL_EVDEV_Quit();
@ -1010,8 +1043,8 @@ KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
if ( !is_vulkan && dispdata->gbm_init ) { if ( !is_vulkan && dispdata->gbm_init ) {
/* Destroy cursor GBM plane. */ /* Destroy the window display's cursor GBM BO. */
KMSDRM_DeinitMouse(_this); KMSDRM_DestroyCursorBO(_this, SDL_GetDisplayForWindow(window));
/* Destroy GBM surface and buffers. */ /* Destroy GBM surface and buffers. */
KMSDRM_DestroySurfaces(_this, window); KMSDRM_DestroySurfaces(_this, window);
@ -1126,15 +1159,20 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
goto cleanup; 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 /* Create the cursor BO for the display of this window,
destroy the cursor GBM BO when we destroy a window, we must now that we know this is not a VK window. */
also manually re-show the cursor on screen, if necessary, KMSDRM_CreateCursorBO(display);
when we create a window. */
/* 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(); KMSDRM_InitCursor();
} }
@ -1256,7 +1294,6 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
as pending so it's done on SwapWindow. */ as pending so it's done on SwapWindow. */
KMSDRM_CreateSurfaces(_this, window); KMSDRM_CreateSurfaces(_this, window);
dispdata->modeset_pending = SDL_TRUE; dispdata->modeset_pending = SDL_TRUE;
} }
int int

View file

@ -138,6 +138,7 @@ void KMSDRM_RaiseWindow(_THIS, SDL_Window * window);
void KMSDRM_MaximizeWindow(_THIS, SDL_Window * window); void KMSDRM_MaximizeWindow(_THIS, SDL_Window * window);
void KMSDRM_MinimizeWindow(_THIS, SDL_Window * window); void KMSDRM_MinimizeWindow(_THIS, SDL_Window * window);
void KMSDRM_RestoreWindow(_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); void KMSDRM_DestroyWindow(_THIS, SDL_Window * window);
/* Window manager function */ /* Window manager function */