Browse Source

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

Manuel Alfayate Corchete 4 years ago
parent
commit
b17c49509b

+ 228 - 178
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;
-    }
+    } else {
 
-    /* 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);
-        }
-    }
+	display = SDL_GetDisplayForWindow(window);
 
-    /* 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;
-    }
+	if (display) {
 
-    /* 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 (cursor) {
+		/* Dump the cursor to the display DRM cursor BO so it becomes visible
+		   on that display. */
+                ret = KMSDRM_DumpCursorToBO(display, cursor);
 
-    if (ret) {
-        ret = SDL_SetError("Failed to set DRM cursor.");
-        goto cleanup;
+	    } else {
+		/* Hide the cursor on that display. */
+                ret = KMSDRM_RemoveCursorFromBO(display);
+	    }
+	}
     }
 
-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)
-{
-    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)
+KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display)
 {
-    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;
 
-        /* Correct the Y coordinate, because DRM mouse coordinates start on screen top. */
-        screen_y = dispdata->mode.vdisplay - window->h + mouse->y;
+        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;
+
         ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, screen_y);
 
         if (ret) {

+ 3 - 2
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_ */

+ 175 - 138
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) {
+
+    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;
+	/* 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) {
-    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+/* 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) {
 
-    drmModeRes *resources = NULL;
+    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+    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,25 +603,29 @@ 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;
 
-    /***********************************/
-    /* Block for Vulkan compatibility. */
-    /***********************************/
+    /*****************************************/
+    /* Part 2: setup the SDL_Display itself. */
+    /*****************************************/
 
-    /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
-       Will reopen this in CreateWindow, but only if requested a non-VK window. */
-    close (viddata->drm_fd);
-    viddata->drm_fd = -1;
+    /* 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 (resources)
-        KMSDRM_drmModeFreeResources(resources);
     if (ret) {
         /* Error (complete) cleanup */
         if (dispdata->connector) {
@@ -641,12 +636,93 @@ cleanup:
             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. */
+    /***********************************/
+
+    /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
+       Will reopen this in CreateWindow, but only if requested a non-VK window. */
+    close (viddata->drm_fd);
+    viddata->drm_fd = -1;
+
+cleanup:
+    if (resources)
+        KMSDRM_drmModeFreeResources(resources);
+    if (ret) {
         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

+ 1 - 0
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 */