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

main
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);
}
/* 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,122 +364,58 @@ 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 (!window || !cursor) {
/* 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). */
num_displays = SDL_GetNumVideoDisplays();
/* Iterate on the displays hidding the cursor. */
for (i = 0; i < num_displays; i++) {
display = SDL_GetDisplay(i);
ret = KMSDRM_RemoveCursorFromBO(display);
}
/*****************************************************/
/* If cursor != NULL, DO show cursor on it's window. */
/*****************************************************/
curdata = (KMSDRM_CursorData *) cursor->driverdata;
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_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;
}
display = SDL_GetDisplayForWindow(window);
cleanup:
if (display) {
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;
/* 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);
}
/* Free cursor itself */
if (cursor->driverdata) {
SDL_free(cursor->driverdata);
}
SDL_free(cursor);
}
return ret;
}
/* Warp the mouse to (x,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) {

View File

@ -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_ */

View File

@ -457,9 +457,20 @@ 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) {
/* Deinitializes the driverdata of the SDL Displays in the SDL display list. */
void KMSDRM_DeinitDisplays (_THIS) {
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);
@ -472,68 +483,38 @@ void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
dispdata->crtc = NULL;
}
}
}
/* 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) {
/* 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) {
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++) {
@ -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);
@ -1127,14 +1160,19 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
}
}
/* Can't init mouse stuff sooner because cursor plane is not ready,
so we do it here. */
KMSDRM_InitMouse(_this);
/* Create the cursor BO for the display of this window,
now that we know this is not a VK window. */
KMSDRM_CreateCursorBO(display);
/* 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. */
/* 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

View File

@ -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 */