Fixed bug 4966 - KMSDRM: Add dynamic modeset support
authorSam Lantinga <slouken@libsdl.org>
Sun, 09 Feb 2020 11:44:22 -0800
changeset 13496045f218436fe
parent 13495 4ee6c5c898c8
child 13497 22308b7ba06c
Fixed bug 4966 - KMSDRM: Add dynamic modeset support

Anthony Pesch

* Remove triple buffering support. As far as I can tell, this goes against the libdrm API; the EGL implementations themselves control the buffering. Removing it isn't absolutely necessary as it seemingly works on the Pi at least, but I noticed this while doing my work and explained my reasoning in the commit.

* Replace the crtc_ready logic which allocates an extra bo to perform the initial CRTC configuration (which is required before calling drmModePageFlip) with a call to drmModeSetCrtc after the front and back buffers are allocated, avoiding this allocation.

* Standardized the SDL_*Data variable names and null checks to improve readability. Given that there were duplicate fields in each SDL_*Data structure, having generic names such as "data" at times was very confusing.

* Removed unused fields from the SDL_*Data structures and moves all display related fields out of SDL_VideoData and into SDL_DisplayData. Not required since the code only supports a single display right now, but this was helpful in reading and understanding the code initially.

* Implement KMSDRM_GetDisplayModes / KMSDRM_SetDisplayMode to provide dynamic modeset support.

These changes have been tested on a Raspberry Pi 4 and a Dell XPS laptop with an HD 520.

As an update, I went back over the triple buffer changes and left them in. I didn't entirely get the code originally, I had just seen it calling KMSDRM_gbm_surface_lock_front_buffer twice for a single swap and had removed it because I was paranoid of bugs stemming from it while working on the modeset changes.

I've made a few small changes to the logic that had thrown me off originally and rebased the changes:
* The condition wrapping the call to release buffer was incorrect.
* The first call to KMSDRM_gbm_surface_lock_front_buffer has been removed. I don't understand why it existed.
* Added additional comments describing what was going on in the code (as it does fix the buffer release pattern of the original code before it).
src/video/kmsdrm/SDL_kmsdrmmouse.c
src/video/kmsdrm/SDL_kmsdrmopengles.c
src/video/kmsdrm/SDL_kmsdrmvideo.c
src/video/kmsdrm/SDL_kmsdrmvideo.h
     1.1 --- a/src/video/kmsdrm/SDL_kmsdrmmouse.c	Fri Feb 07 20:20:37 2020 -0800
     1.2 +++ b/src/video/kmsdrm/SDL_kmsdrmmouse.c	Sun Feb 09 11:44:22 2020 -0800
     1.3 @@ -49,19 +49,21 @@
     1.4  KMSDRM_IsCursorSizeSupported (int w, int h, uint32_t bo_format) {
     1.5  
     1.6      SDL_VideoDevice *dev = SDL_GetVideoDevice();
     1.7 -    SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
     1.8 +    SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
     1.9 +    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    1.10 +
    1.11      int ret;
    1.12      uint32_t bo_handle;
    1.13 -    struct gbm_bo *bo = KMSDRM_gbm_bo_create(vdata->gbm, w, h, bo_format,
    1.14 +    struct gbm_bo *bo = KMSDRM_gbm_bo_create(viddata->gbm, w, h, bo_format,
    1.15                                         GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
    1.16  
    1.17 -    if (bo == NULL) {
    1.18 +    if (!bo) {
    1.19          SDL_SetError("Could not create GBM cursor BO width size %dx%d for size testing", w, h);
    1.20          goto cleanup;
    1.21      }
    1.22  
    1.23      bo_handle = KMSDRM_gbm_bo_get_handle(bo).u32;
    1.24 -    ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, vdata->crtc_id, bo_handle, w, h);
    1.25 +    ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, w, h);
    1.26  
    1.27      if (ret) {
    1.28          goto cleanup;
    1.29 @@ -72,7 +74,7 @@
    1.30      }
    1.31  
    1.32  cleanup:
    1.33 -    if (bo != NULL) {
    1.34 +    if (bo) {
    1.35          KMSDRM_gbm_bo_destroy(bo);
    1.36      }
    1.37      return SDL_FALSE;
    1.38 @@ -83,7 +85,7 @@
    1.39  KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
    1.40  {
    1.41      SDL_VideoDevice *dev = SDL_GetVideoDevice();
    1.42 -    SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
    1.43 +    SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
    1.44      SDL_PixelFormat *pixlfmt = surface->format;
    1.45      KMSDRM_CursorData *curdata;
    1.46      SDL_Cursor *cursor;
    1.47 @@ -161,18 +163,18 @@
    1.48          return NULL;
    1.49      }
    1.50  
    1.51 -    if (!KMSDRM_gbm_device_is_format_supported(vdata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
    1.52 +    if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
    1.53          SDL_SetError("Unsupported pixel format for cursor");
    1.54          return NULL;
    1.55      }
    1.56  
    1.57      cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
    1.58 -    if (cursor == NULL) {
    1.59 +    if (!cursor) {
    1.60          SDL_OutOfMemory();
    1.61          return NULL;
    1.62      }
    1.63      curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
    1.64 -    if (curdata == NULL) {
    1.65 +    if (!curdata) {
    1.66          SDL_OutOfMemory();
    1.67          SDL_free(cursor);
    1.68          return NULL;
    1.69 @@ -205,10 +207,10 @@
    1.70      curdata->w = usable_cursor_w;
    1.71      curdata->h = usable_cursor_h;
    1.72  
    1.73 -    curdata->bo = KMSDRM_gbm_bo_create(vdata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
    1.74 +    curdata->bo = KMSDRM_gbm_bo_create(viddata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
    1.75                                         GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
    1.76  
    1.77 -    if (curdata->bo == NULL) {
    1.78 +    if (!curdata->bo) {
    1.79          SDL_SetError("Could not create GBM cursor BO");
    1.80          goto cleanup;
    1.81      }
    1.82 @@ -219,7 +221,7 @@
    1.83      if (surface->pitch != bo_stride) {
    1.84          /* pitch doesn't match stride, must be copied to temp buffer  */
    1.85          buffer = SDL_malloc(bufsize);
    1.86 -        if (buffer == NULL) {
    1.87 +        if (!buffer) {
    1.88              SDL_OutOfMemory();
    1.89              goto cleanup;
    1.90          }
    1.91 @@ -279,14 +281,14 @@
    1.92      return cursor;
    1.93  
    1.94  cleanup:
    1.95 -    if (buffer != NULL) {
    1.96 +    if (buffer) {
    1.97          SDL_free(buffer);
    1.98      }
    1.99 -    if (cursor != NULL) {
   1.100 +    if (cursor) {
   1.101          SDL_free(cursor);
   1.102      }
   1.103 -    if (curdata != NULL) {
   1.104 -        if (curdata->bo != NULL) {
   1.105 +    if (curdata) {
   1.106 +        if (curdata->bo) {
   1.107              KMSDRM_gbm_bo_destroy(curdata->bo);
   1.108          }
   1.109          SDL_free(curdata);
   1.110 @@ -299,33 +301,33 @@
   1.111  KMSDRM_ShowCursor(SDL_Cursor * cursor)
   1.112  {
   1.113      SDL_VideoDevice *dev = SDL_GetVideoDevice();
   1.114 -    SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
   1.115 +    SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
   1.116      SDL_Mouse *mouse;
   1.117      KMSDRM_CursorData *curdata;
   1.118      SDL_VideoDisplay *display = NULL;
   1.119 -    SDL_DisplayData *ddata = NULL;
   1.120 +    SDL_DisplayData *dispdata = NULL;
   1.121      int ret;
   1.122      uint32_t bo_handle;
   1.123  
   1.124      mouse = SDL_GetMouse();
   1.125 -    if (mouse == NULL) {
   1.126 +    if (!mouse) {
   1.127          return SDL_SetError("No mouse.");
   1.128      }
   1.129  
   1.130 -    if (mouse->focus != NULL) {
   1.131 +    if (mouse->focus) {
   1.132          display = SDL_GetDisplayForWindow(mouse->focus);
   1.133 -        if (display != NULL) {
   1.134 -            ddata = (SDL_DisplayData*) display->driverdata;
   1.135 +        if (display) {
   1.136 +            dispdata = (SDL_DisplayData*) display->driverdata;
   1.137          }
   1.138      }
   1.139  
   1.140 -    if (cursor == NULL) {
   1.141 +    if (!cursor) {
   1.142          /* Hide current cursor */
   1.143 -        if ( mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   1.144 +        if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
   1.145              curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
   1.146  
   1.147              if (curdata->crtc_id != 0) {
   1.148 -                ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, curdata->crtc_id, 0, 0, 0);
   1.149 +                ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, curdata->crtc_id, 0, 0, 0);
   1.150                  if (ret) {
   1.151                      SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
   1.152                      return ret;
   1.153 @@ -337,8 +339,8 @@
   1.154              }
   1.155          }
   1.156          /* otherwise if possible, hide global cursor */
   1.157 -        if (ddata != NULL && ddata->crtc_id != 0) {
   1.158 -            ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, 0, 0, 0);
   1.159 +        if (dispdata && dispdata->crtc_id != 0) {
   1.160 +            ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, 0, 0, 0);
   1.161              if (ret) {
   1.162                  SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
   1.163                  return ret;
   1.164 @@ -349,33 +351,32 @@
   1.165          return SDL_SetError("Couldn't find cursor to hide.");
   1.166      }
   1.167      /* If cursor != NULL, show new cursor on display */
   1.168 -    if (display == NULL) {
   1.169 +    if (!display) {
   1.170          return SDL_SetError("Could not get display for mouse.");
   1.171      }
   1.172 -    if (ddata == NULL) {
   1.173 +    if (!dispdata) {
   1.174          return SDL_SetError("Could not get display driverdata.");
   1.175      }
   1.176  
   1.177      curdata = (KMSDRM_CursorData *) cursor->driverdata;
   1.178 -    if (curdata == NULL || curdata->bo == NULL) {
   1.179 +    if (!curdata || !curdata->bo) {
   1.180          return SDL_SetError("Cursor not initialized properly.");
   1.181      }
   1.182  
   1.183      bo_handle = KMSDRM_gbm_bo_get_handle(curdata->bo).u32;
   1.184      if (curdata->hot_x == 0 && curdata->hot_y == 0) {
   1.185 -        ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, bo_handle,
   1.186 +        ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle,
   1.187                                        curdata->w, curdata->h);
   1.188      } else {
   1.189 -        ret = KMSDRM_drmModeSetCursor2(vdata->drm_fd, ddata->crtc_id, bo_handle,
   1.190 -                                       curdata->w, curdata->h,
   1.191 -                                       curdata->hot_x, curdata->hot_y);
   1.192 +        ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc_id, bo_handle,
   1.193 +                                       curdata->w, curdata->h, curdata->hot_x, curdata->hot_y);
   1.194      }
   1.195      if (ret) {
   1.196          SDL_SetError("drmModeSetCursor failed.");
   1.197          return ret;
   1.198      }
   1.199  
   1.200 -    curdata->crtc_id = ddata->crtc_id;
   1.201 +    curdata->crtc_id = dispdata->crtc_id;
   1.202  
   1.203      return 0;
   1.204  }
   1.205 @@ -387,11 +388,11 @@
   1.206      KMSDRM_CursorData *curdata;
   1.207      int drm_fd;
   1.208  
   1.209 -    if (cursor != NULL) {
   1.210 +    if (cursor) {
   1.211          curdata = (KMSDRM_CursorData *) cursor->driverdata;
   1.212  
   1.213 -        if (curdata != NULL) {
   1.214 -            if (curdata->bo != NULL) {
   1.215 +        if (curdata) {
   1.216 +            if (curdata->bo) {
   1.217                  if (curdata->crtc_id != 0) {
   1.218                      drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
   1.219                      /* Hide the cursor if previously shown on a CRTC */
   1.220 @@ -422,13 +423,13 @@
   1.221      KMSDRM_CursorData *curdata;
   1.222      SDL_Mouse *mouse = SDL_GetMouse();
   1.223  
   1.224 -    if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   1.225 +    if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
   1.226          /* Update internal mouse position. */
   1.227          SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
   1.228  
   1.229          /* And now update the cursor graphic position on screen. */
   1.230          curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
   1.231 -        if (curdata->bo != NULL) {
   1.232 +        if (curdata->bo) {
   1.233  
   1.234              if (curdata->crtc_id != 0) {
   1.235                  int ret, drm_fd;
   1.236 @@ -485,7 +486,7 @@
   1.237  
   1.238      /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
   1.239         That's why we move the cursor graphic ONLY. */
   1.240 -    if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
   1.241 +    if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
   1.242          curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
   1.243          drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
   1.244          ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
     2.1 --- a/src/video/kmsdrm/SDL_kmsdrmopengles.c	Fri Feb 07 20:20:37 2020 -0800
     2.2 +++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c	Sun Feb 09 11:44:22 2020 -0800
     2.3 @@ -42,40 +42,6 @@
     2.4  
     2.5  SDL_EGL_CreateContext_impl(KMSDRM)
     2.6  
     2.7 -SDL_bool
     2.8 -KMSDRM_GLES_SetupCrtc(_THIS, SDL_Window * window) {
     2.9 -    SDL_WindowData *wdata = ((SDL_WindowData *) window->driverdata);
    2.10 -    SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
    2.11 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
    2.12 -    KMSDRM_FBInfo *fb_info;
    2.13 -
    2.14 -    if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, wdata->egl_surface))) {
    2.15 -        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed on CRTC setup");
    2.16 -        return SDL_FALSE;
    2.17 -    }
    2.18 -
    2.19 -    wdata->crtc_bo = KMSDRM_gbm_surface_lock_front_buffer(wdata->gs);
    2.20 -    if (wdata->crtc_bo == NULL) {
    2.21 -        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer on CRTC setup");
    2.22 -        return SDL_FALSE;
    2.23 -    }
    2.24 -
    2.25 -    fb_info = KMSDRM_FBFromBO(_this, wdata->crtc_bo);
    2.26 -    if (fb_info == NULL) {
    2.27 -        return SDL_FALSE;
    2.28 -    }
    2.29 -
    2.30 -    if(KMSDRM_drmModeSetCrtc(vdata->drm_fd, displaydata->crtc_id, fb_info->fb_id,
    2.31 -                            0, 0, &vdata->saved_conn_id, 1, &displaydata->cur_mode) != 0) {
    2.32 -       SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not set up CRTC to a GBM buffer");
    2.33 -       return SDL_FALSE;
    2.34 -
    2.35 -    }
    2.36 -
    2.37 -    wdata->crtc_ready = SDL_TRUE;
    2.38 -    return SDL_TRUE;
    2.39 -}
    2.40 -
    2.41  int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
    2.42      if (!_this->egl_data) {
    2.43          return SDL_SetError("EGL not initialized");
    2.44 @@ -92,82 +58,86 @@
    2.45  
    2.46  int
    2.47  KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
    2.48 -    SDL_WindowData *wdata = ((SDL_WindowData *) window->driverdata);
    2.49 -    SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
    2.50 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
    2.51 +    SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
    2.52 +    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
    2.53 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    2.54      KMSDRM_FBInfo *fb_info;
    2.55      int ret;
    2.56  
    2.57 -    /* Do we still need to wait for a flip? */
    2.58 +    /* Recreate the GBM / EGL surfaces if the display mode has changed */
    2.59 +    if (windata->egl_surface_dirty) {
    2.60 +        KMSDRM_CreateSurfaces(_this, window);
    2.61 +    }
    2.62 +
    2.63 +    /* Wait for confirmation that the next front buffer has been flipped, at which
    2.64 +       point the previous front buffer can be released */
    2.65      int timeout = 0;
    2.66      if (_this->egl_data->egl_swapinterval == 1) {
    2.67          timeout = -1;
    2.68      }
    2.69 -    if (!KMSDRM_WaitPageFlip(_this, wdata, timeout)) {
    2.70 +    if (!KMSDRM_WaitPageFlip(_this, windata, timeout)) {
    2.71          return 0;
    2.72      }
    2.73  
    2.74 -    /* Release previously displayed buffer (which is now the backbuffer) and lock a new one */
    2.75 -    if (wdata->next_bo != NULL) {
    2.76 -        KMSDRM_gbm_surface_release_buffer(wdata->gs, wdata->current_bo);
    2.77 -        /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Released GBM surface %p", (void *)wdata->next_bo); */
    2.78 -
    2.79 -        wdata->current_bo = wdata->next_bo;
    2.80 -        wdata->next_bo = NULL;
    2.81 +    /* Release the previous front buffer */
    2.82 +    if (windata->curr_bo) {
    2.83 +        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
    2.84 +        /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Released GBM surface %p", (void *)windata->curr_bo); */
    2.85 +        windata->curr_bo = NULL;
    2.86      }
    2.87  
    2.88 -    if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, wdata->egl_surface))) {
    2.89 +    windata->curr_bo = windata->next_bo;
    2.90 +
    2.91 +    /* Make the current back buffer the next front buffer */
    2.92 +    if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface))) {
    2.93          SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed.");
    2.94          return 0;
    2.95      }
    2.96  
    2.97 -    if (wdata->current_bo == NULL) {
    2.98 -        wdata->current_bo = KMSDRM_gbm_surface_lock_front_buffer(wdata->gs);
    2.99 -        if (wdata->current_bo == NULL) {
   2.100 -            return 0;
   2.101 -        }
   2.102 -    }
   2.103 -
   2.104 -    wdata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(wdata->gs);
   2.105 -    if (wdata->next_bo == NULL) {
   2.106 +    /* Lock the next front buffer so it can't be allocated as a back buffer */
   2.107 +    windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
   2.108 +    if (!windata->next_bo) {
   2.109          SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer");
   2.110          return 0;
   2.111      /* } else {
   2.112 -        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Locked GBM surface %p", (void *)wdata->next_bo); */
   2.113 +        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Locked GBM surface %p", (void *)windata->next_bo); */
   2.114      }
   2.115  
   2.116 -    fb_info = KMSDRM_FBFromBO(_this, wdata->next_bo);
   2.117 -    if (fb_info == NULL) {
   2.118 +    fb_info = KMSDRM_FBFromBO(_this, windata->next_bo);
   2.119 +    if (!fb_info) {
   2.120          return 0;
   2.121      }
   2.122  
   2.123 -    /* Have we already setup the CRTC to one of the GBM buffers? Do so if we have not,
   2.124 -       or FlipPage won't work in some cases. */
   2.125 -    if (!wdata->crtc_ready) {
   2.126 -        if(!KMSDRM_GLES_SetupCrtc(_this, window)) {
   2.127 -            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not set up CRTC for doing pageflips");
   2.128 -            return 0;
   2.129 +    if (!windata->curr_bo) {
   2.130 +        /* On the first swap, immediately present the new front buffer. Before
   2.131 +           drmModePageFlip can be used the CRTC has to be configured to use
   2.132 +           the current connector and mode with drmModeSetCrtc */
   2.133 +        ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id, 0,
   2.134 +                                    0, &dispdata->conn->connector_id, 1, &dispdata->mode);
   2.135 +
   2.136 +        if (ret) {
   2.137 +          SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not configure CRTC");
   2.138          }
   2.139 -    }
   2.140 +    } else {
   2.141 +        /* On subsequent swaps, queue the new front buffer to be flipped during
   2.142 +           the next vertical blank */
   2.143 +        ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id,
   2.144 +                                     DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
   2.145 +        /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip)",
   2.146 +            viddata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */
   2.147  
   2.148 -    /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &wdata->waiting_for_flip)",
   2.149 -        vdata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */
   2.150 -    ret = KMSDRM_drmModePageFlip(vdata->drm_fd, displaydata->crtc_id, fb_info->fb_id,
   2.151 -                                 DRM_MODE_PAGE_FLIP_EVENT, &wdata->waiting_for_flip);
   2.152 -
   2.153 -    if (_this->egl_data->egl_swapinterval == 1) {
   2.154 -        /* Queue page flip at vsync */
   2.155 -
   2.156 -        if (ret == 0) {
   2.157 -            wdata->waiting_for_flip = SDL_TRUE;
   2.158 -        } else {
   2.159 -            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
   2.160 +        if (_this->egl_data->egl_swapinterval == 1) {
   2.161 +            if (ret == 0) {
   2.162 +                windata->waiting_for_flip = SDL_TRUE;
   2.163 +            } else {
   2.164 +                SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
   2.165 +            }
   2.166          }
   2.167  
   2.168          /* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios.
   2.169             Run your SDL2 program with "SDL_KMSDRM_DOUBLE_BUFFER=1 <program_name>" to enable this. */
   2.170 -        if (wdata->double_buffer) {
   2.171 -            KMSDRM_WaitPageFlip(_this, wdata, -1);
   2.172 +        if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
   2.173 +            KMSDRM_WaitPageFlip(_this, windata, -1);
   2.174          }
   2.175      }
   2.176  
     3.1 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c	Fri Feb 07 20:20:37 2020 -0800
     3.2 +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c	Sun Feb 09 11:44:22 2020 -0800
     3.3 @@ -28,6 +28,7 @@
     3.4  #include "SDL_syswm.h"
     3.5  #include "SDL_log.h"
     3.6  #include "SDL_hints.h"
     3.7 +#include "../../events/SDL_events_c.h"
     3.8  #include "../../events/SDL_mouse_c.h"
     3.9  #include "../../events/SDL_keyboard_c.h"
    3.10  
    3.11 @@ -44,6 +45,7 @@
    3.12  #include <sys/stat.h>
    3.13  #include <dirent.h>
    3.14  #include <errno.h>
    3.15 +#include <poll.h>
    3.16  
    3.17  #define KMSDRM_DRI_PATH "/dev/dri/"
    3.18  
    3.19 @@ -60,7 +62,7 @@
    3.20      if (drm_fd >= 0) {
    3.21          if (SDL_KMSDRM_LoadSymbols()) {
    3.22              drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
    3.23 -            if (resources != NULL) {
    3.24 +            if (resources) {
    3.25                  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
    3.26                               KMSDRM_DRI_PATH, devindex,
    3.27                               resources->count_connectors, resources->count_encoders, resources->count_crtcs);
    3.28 @@ -140,22 +142,23 @@
    3.29  }
    3.30  
    3.31  static void
    3.32 -KMSDRM_Destroy(SDL_VideoDevice * device)
    3.33 +KMSDRM_DeleteDevice(SDL_VideoDevice * device)
    3.34  {
    3.35 -    if (device->driverdata != NULL) {
    3.36 +    if (device->driverdata) {
    3.37          SDL_free(device->driverdata);
    3.38          device->driverdata = NULL;
    3.39      }
    3.40  
    3.41      SDL_free(device);
    3.42 +
    3.43      SDL_KMSDRM_UnloadSymbols();
    3.44  }
    3.45  
    3.46  static SDL_VideoDevice *
    3.47 -KMSDRM_Create(int devindex)
    3.48 +KMSDRM_CreateDevice(int devindex)
    3.49  {
    3.50      SDL_VideoDevice *device;
    3.51 -    SDL_VideoData *vdata;
    3.52 +    SDL_VideoData *viddata;
    3.53  
    3.54      if (!devindex || (devindex > 99)) {
    3.55          devindex = get_driindex();
    3.56 @@ -170,29 +173,21 @@
    3.57          return NULL;
    3.58      }
    3.59  
    3.60 -    /* Initialize SDL_VideoDevice structure */
    3.61      device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    3.62 -    if (device == NULL) {
    3.63 +    if (!device) {
    3.64          SDL_OutOfMemory();
    3.65          return NULL;
    3.66      }
    3.67  
    3.68 -    /* Initialize internal data */
    3.69 -    vdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    3.70 -    if (vdata == NULL) {
    3.71 +    viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    3.72 +    if (!viddata) {
    3.73          SDL_OutOfMemory();
    3.74          goto cleanup;
    3.75      }
    3.76 -    vdata->devindex = devindex;
    3.77 -    vdata->drm_fd = -1;
    3.78 +    viddata->devindex = devindex;
    3.79 +    viddata->drm_fd = -1;
    3.80  
    3.81 -    device->driverdata = vdata;
    3.82 -
    3.83 -    /* Setup amount of available displays and current display */
    3.84 -    device->num_displays = 0;
    3.85 -
    3.86 -    /* Set device free function */
    3.87 -    device->free = KMSDRM_Destroy;
    3.88 +    device->driverdata = viddata;
    3.89  
    3.90      /* Setup all functions which we can handle */
    3.91      device->VideoInit = KMSDRM_VideoInit;
    3.92 @@ -225,16 +220,16 @@
    3.93      device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
    3.94      device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
    3.95  #endif
    3.96 -
    3.97      device->PumpEvents = KMSDRM_PumpEvents;
    3.98 +    device->free = KMSDRM_DeleteDevice;
    3.99  
   3.100      return device;
   3.101  
   3.102  cleanup:
   3.103 -    if (device != NULL)
   3.104 +    if (device)
   3.105          SDL_free(device);
   3.106 -    if (vdata != NULL)
   3.107 -        SDL_free(vdata);
   3.108 +    if (viddata)
   3.109 +        SDL_free(viddata);
   3.110      return NULL;
   3.111  }
   3.112  
   3.113 @@ -242,7 +237,7 @@
   3.114      "KMSDRM",
   3.115      "KMS/DRM Video Driver",
   3.116      KMSDRM_Available,
   3.117 -    KMSDRM_Create
   3.118 +    KMSDRM_CreateDevice
   3.119  };
   3.120  
   3.121  
   3.122 @@ -262,178 +257,248 @@
   3.123  KMSDRM_FBInfo *
   3.124  KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
   3.125  {
   3.126 -    uint32_t w, h, stride, handle;
   3.127 -    int ret;
   3.128 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
   3.129 -    KMSDRM_FBInfo *fb_info;
   3.130 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.131  
   3.132 -    fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
   3.133 -    if (fb_info != NULL) {
   3.134 -        /* Have a previously used framebuffer, return it */
   3.135 +    /* Check for an existing framebuffer */
   3.136 +    KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
   3.137 +
   3.138 +    if (fb_info) {
   3.139          return fb_info;
   3.140      }
   3.141  
   3.142 -    /* Here a new DRM FB must be created */
   3.143 +    /* Create a structure that contains enough info to remove the framebuffer
   3.144 +       when the backing buffer is destroyed */
   3.145      fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
   3.146 -    if (fb_info == NULL) {
   3.147 +
   3.148 +    if (!fb_info) {
   3.149          SDL_OutOfMemory();
   3.150          return NULL;
   3.151      }
   3.152 -    fb_info->drm_fd = vdata->drm_fd;
   3.153  
   3.154 -    w  = KMSDRM_gbm_bo_get_width(bo);
   3.155 -    h = KMSDRM_gbm_bo_get_height(bo);
   3.156 -    stride = KMSDRM_gbm_bo_get_stride(bo);
   3.157 -    handle = KMSDRM_gbm_bo_get_handle(bo).u32;
   3.158 +    fb_info->drm_fd = viddata->drm_fd;
   3.159  
   3.160 -    ret = KMSDRM_drmModeAddFB(vdata->drm_fd, w, h, 24, 32, stride, handle, &fb_info->fb_id);
   3.161 -    if (ret < 0) {
   3.162 -       SDL_free(fb_info);
   3.163 -       return NULL;
   3.164 +    /* Create framebuffer object for the buffer */
   3.165 +    unsigned w = KMSDRM_gbm_bo_get_width(bo);
   3.166 +    unsigned h = KMSDRM_gbm_bo_get_height(bo);
   3.167 +    Uint32 stride = KMSDRM_gbm_bo_get_stride(bo);
   3.168 +    Uint32 handle = KMSDRM_gbm_bo_get_handle(bo).u32;
   3.169 +    int ret = KMSDRM_drmModeAddFB(viddata->drm_fd, w, h, 24, 32, stride, handle,
   3.170 +                                  &fb_info->fb_id);
   3.171 +    if (ret) {
   3.172 +      SDL_free(fb_info);
   3.173 +      return NULL;
   3.174      }
   3.175 -    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, stride %u from BO %p", fb_info->fb_id, w, h, stride, (void *)bo);
   3.176 +
   3.177 +    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, stride %u from BO %p",
   3.178 +                 fb_info->fb_id, w, h, stride, (void *)bo);
   3.179  
   3.180      /* Associate our DRM framebuffer with this buffer object */
   3.181      KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
   3.182 +
   3.183      return fb_info;
   3.184  }
   3.185  
   3.186 -SDL_bool
   3.187 -KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *wdata, int timeout) {
   3.188 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
   3.189 -
   3.190 -    while (wdata->waiting_for_flip) {
   3.191 -        vdata->drm_pollfd.revents = 0;
   3.192 -        if (poll(&vdata->drm_pollfd, 1, timeout) < 0) {
   3.193 -            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
   3.194 -            return SDL_FALSE;
   3.195 -        }
   3.196 -
   3.197 -        if (vdata->drm_pollfd.revents & (POLLHUP | POLLERR)) {
   3.198 -            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
   3.199 -            return SDL_FALSE;
   3.200 -        }
   3.201 -
   3.202 -        if (vdata->drm_pollfd.revents & POLLIN) {
   3.203 -            /* Page flip? If so, drmHandleEvent will unset wdata->waiting_for_flip */
   3.204 -            KMSDRM_drmHandleEvent(vdata->drm_fd, &vdata->drm_evctx);
   3.205 -        } else {
   3.206 -            /* Timed out and page flip didn't happen */
   3.207 -            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
   3.208 -            return SDL_FALSE;
   3.209 -        }
   3.210 -    }
   3.211 -    return SDL_TRUE;
   3.212 -}
   3.213 -
   3.214  static void
   3.215  KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
   3.216  {
   3.217      *((SDL_bool *) data) = SDL_FALSE;
   3.218  }
   3.219  
   3.220 +SDL_bool
   3.221 +KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
   3.222 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.223 +
   3.224 +    drmEventContext ev = {0};
   3.225 +    ev.version = DRM_EVENT_CONTEXT_VERSION;
   3.226 +    ev.page_flip_handler = KMSDRM_FlipHandler;
   3.227 +
   3.228 +    struct pollfd pfd = {0};
   3.229 +    pfd.fd = viddata->drm_fd;
   3.230 +    pfd.events = POLLIN;
   3.231 +
   3.232 +    while (windata->waiting_for_flip) {
   3.233 +        pfd.revents = 0;
   3.234 +
   3.235 +        if (poll(&pfd, 1, timeout) < 0) {
   3.236 +            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
   3.237 +            return SDL_FALSE;
   3.238 +        }
   3.239 +
   3.240 +        if (pfd.revents & (POLLHUP | POLLERR)) {
   3.241 +            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
   3.242 +            return SDL_FALSE;
   3.243 +        }
   3.244 +
   3.245 +        if (pfd.revents & POLLIN) {
   3.246 +            /* Page flip? If so, drmHandleEvent will unset windata->waiting_for_flip */
   3.247 +            KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
   3.248 +        } else {
   3.249 +            /* Timed out and page flip didn't happen */
   3.250 +            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
   3.251 +            return SDL_FALSE;
   3.252 +        }
   3.253 +    }
   3.254 +
   3.255 +    return SDL_TRUE;
   3.256 +}
   3.257  
   3.258  /*****************************************************************************/
   3.259  /* SDL Video and Display initialization/handling functions                   */
   3.260  /* _this is a SDL_VideoDevice *                                              */
   3.261  /*****************************************************************************/
   3.262 +static int
   3.263 +KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
   3.264 +{
   3.265 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.266 +    SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   3.267 +
   3.268 +    KMSDRM_WaitPageFlip(_this, windata, -1);
   3.269 +
   3.270 +    if (windata->curr_bo) {
   3.271 +        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
   3.272 +        windata->curr_bo = NULL;
   3.273 +    }
   3.274 +
   3.275 +    if (windata->next_bo) {
   3.276 +        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
   3.277 +        windata->next_bo = NULL;
   3.278 +    }
   3.279 +
   3.280 +#if SDL_VIDEO_OPENGL_EGL
   3.281 +    SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   3.282 +
   3.283 +    if (windata->egl_surface != EGL_NO_SURFACE) {
   3.284 +        SDL_EGL_DestroySurface(_this, windata->egl_surface);
   3.285 +        windata->egl_surface = EGL_NO_SURFACE;
   3.286 +    }
   3.287 +#endif
   3.288 +
   3.289 +    if (windata->gs) {
   3.290 +        KMSDRM_gbm_surface_destroy(windata->gs);
   3.291 +        windata->gs = NULL;
   3.292 +    }
   3.293 +}
   3.294 +
   3.295 +int
   3.296 +KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
   3.297 +{
   3.298 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.299 +    SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   3.300 +    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   3.301 +    Uint32 width = dispdata->mode.hdisplay;
   3.302 +    Uint32 height = dispdata->mode.vdisplay;
   3.303 +    Uint32 surface_fmt = GBM_FORMAT_XRGB8888;
   3.304 +    Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
   3.305 +
   3.306 +    if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) {
   3.307 +        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
   3.308 +    }
   3.309 +
   3.310 +#if SDL_VIDEO_OPENGL_EGL
   3.311 +    SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
   3.312 +
   3.313 +    EGLContext egl_context = (EGLContext)SDL_GL_GetCurrentContext();
   3.314 +#endif
   3.315 +
   3.316 +    KMSDRM_DestroySurfaces(_this, window);
   3.317 +
   3.318 +    windata->gs = KMSDRM_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags);
   3.319 +
   3.320 +    if (!windata->gs) {
   3.321 +        return SDL_SetError("Could not create GBM surface");
   3.322 +    }
   3.323 +
   3.324 +#if SDL_VIDEO_OPENGL_EGL
   3.325 +    windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
   3.326 +
   3.327 +    if (windata->egl_surface == EGL_NO_SURFACE) {
   3.328 +        return SDL_SetError("Could not create EGL window surface");
   3.329 +    }
   3.330 +
   3.331 +    SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
   3.332 +
   3.333 +    windata->egl_surface_dirty = 0;
   3.334 +#endif
   3.335 +
   3.336 +    return 0;
   3.337 +}
   3.338 +
   3.339  int
   3.340  KMSDRM_VideoInit(_THIS)
   3.341  {
   3.342 -    int i, j;
   3.343 -    SDL_bool found;
   3.344      int ret = 0;
   3.345 -    char *devname;
   3.346 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
   3.347 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.348 +    SDL_DisplayData *dispdata = NULL;
   3.349      drmModeRes *resources = NULL;
   3.350 -    drmModeConnector *connector = NULL;
   3.351      drmModeEncoder *encoder = NULL;
   3.352 -    SDL_DisplayMode current_mode;
   3.353 -    SDL_VideoDisplay display;
   3.354  
   3.355 -    /* Allocate display internal data */
   3.356 -    SDL_DisplayData *data = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   3.357 -    if (data == NULL) {
   3.358 +    dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   3.359 +
   3.360 +    if (!dispdata) {
   3.361          return SDL_OutOfMemory();
   3.362      }
   3.363  
   3.364      SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
   3.365  
   3.366      /* Open /dev/dri/cardNN */
   3.367 -    devname = (char *) SDL_calloc(1, 16);
   3.368 -    if (devname == NULL) {
   3.369 -        ret = SDL_OutOfMemory();
   3.370 +    char devname[32];
   3.371 +    SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
   3.372 +
   3.373 +    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
   3.374 +    viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
   3.375 +
   3.376 +    if (viddata->drm_fd < 0) {
   3.377 +        ret = SDL_SetError("Could not open %s", devname);
   3.378          goto cleanup;
   3.379      }
   3.380 -    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device /dev/dri/card%d", vdata->devindex);
   3.381 -    SDL_snprintf(devname, 16, "/dev/dri/card%d", vdata->devindex);
   3.382 -    vdata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
   3.383 -    SDL_free(devname);
   3.384  
   3.385 -    if (vdata->drm_fd < 0) {
   3.386 -        ret = SDL_SetError("Could not open /dev/dri/card%d.", vdata->devindex);
   3.387 -        goto cleanup;
   3.388 -    }
   3.389 -    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", vdata->drm_fd);
   3.390 +    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
   3.391  
   3.392 -    vdata->gbm = KMSDRM_gbm_create_device(vdata->drm_fd);
   3.393 -    if (vdata->gbm == NULL) {
   3.394 +    viddata->gbm = KMSDRM_gbm_create_device(viddata->drm_fd);
   3.395 +    if (!viddata->gbm) {
   3.396          ret = SDL_SetError("Couldn't create gbm device.");
   3.397          goto cleanup;
   3.398      }
   3.399  
   3.400 -    /* Find the first available connector with modes */
   3.401 -    resources = KMSDRM_drmModeGetResources(vdata->drm_fd);
   3.402 +    /* Get all of the available connectors / devices / crtcs */
   3.403 +    resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
   3.404      if (!resources) {
   3.405 -        ret = SDL_SetError("drmModeGetResources(%d) failed", vdata->drm_fd);
   3.406 +        ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
   3.407          goto cleanup;
   3.408      }
   3.409  
   3.410 -    for (i = 0; i < resources->count_connectors; i++) {
   3.411 -        connector = KMSDRM_drmModeGetConnector(vdata->drm_fd, resources->connectors[i]);
   3.412 -        if (connector == NULL)
   3.413 +    for (int i = 0; i < resources->count_connectors; i++) {
   3.414 +        drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
   3.415 +
   3.416 +        if (!conn) {
   3.417              continue;
   3.418 +        }
   3.419  
   3.420 -        if (connector->connection == DRM_MODE_CONNECTED &&
   3.421 -            connector->count_modes > 0) {
   3.422 +        if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
   3.423              SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
   3.424 -                         connector->connector_id, connector->count_modes);
   3.425 -            vdata->saved_conn_id = connector->connector_id;
   3.426 +                         conn->connector_id, conn->count_modes);
   3.427 +            dispdata->conn = conn;
   3.428              break;
   3.429          }
   3.430  
   3.431 -        KMSDRM_drmModeFreeConnector(connector);
   3.432 -        connector = NULL;
   3.433 +        KMSDRM_drmModeFreeConnector(conn);
   3.434      }
   3.435  
   3.436 -    if (i == resources->count_connectors) {
   3.437 +    if (!dispdata->conn) {
   3.438          ret = SDL_SetError("No currently active connector found.");
   3.439          goto cleanup;
   3.440      }
   3.441  
   3.442 -    found = SDL_FALSE;
   3.443 +    /* Try to find the connector's current encoder */
   3.444 +    for (int i = 0; i < resources->count_encoders; i++) {
   3.445 +        encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   3.446  
   3.447 -    for (i = 0; i < resources->count_encoders; i++) {
   3.448 -        encoder = KMSDRM_drmModeGetEncoder(vdata->drm_fd, resources->encoders[i]);
   3.449 -
   3.450 -        if (encoder == NULL)
   3.451 -            continue;
   3.452 -
   3.453 -        if (encoder->encoder_id == connector->encoder_id) {
   3.454 -            data->encoder_id = encoder->encoder_id;
   3.455 -            found = SDL_TRUE;
   3.456 -        } else {
   3.457 -            for (j = 0; j < connector->count_encoders; j++) {
   3.458 -                if (connector->encoders[j] == encoder->encoder_id) {
   3.459 -                    data->encoder_id = encoder->encoder_id;
   3.460 -                    found = SDL_TRUE;
   3.461 -                    break;
   3.462 -                }
   3.463 -            }
   3.464 +        if (!encoder) {
   3.465 +          continue;
   3.466          }
   3.467  
   3.468 -        if (found == SDL_TRUE) {
   3.469 -            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", data->encoder_id);
   3.470 +        if (encoder->encoder_id == dispdata->conn->encoder_id) {
   3.471 +            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   3.472              break;
   3.473          }
   3.474  
   3.475 @@ -441,71 +506,91 @@
   3.476          encoder = NULL;
   3.477      }
   3.478  
   3.479 -    if (i == resources->count_encoders) {
   3.480 +    if (!encoder) {
   3.481 +        /* No encoder was connected, find the first supported one */
   3.482 +        for (int i = 0, j; i < resources->count_encoders; i++) {
   3.483 +            encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   3.484 +
   3.485 +            if (!encoder) {
   3.486 +              continue;
   3.487 +            }
   3.488 +
   3.489 +            for (j = 0; j < dispdata->conn->count_encoders; j++) {
   3.490 +                if (dispdata->conn->encoders[j] == encoder->encoder_id) {
   3.491 +                    break;
   3.492 +                }
   3.493 +            }
   3.494 +
   3.495 +            if (j != dispdata->conn->count_encoders) {
   3.496 +              break;
   3.497 +            }
   3.498 +
   3.499 +            KMSDRM_drmModeFreeEncoder(encoder);
   3.500 +            encoder = NULL;
   3.501 +        }
   3.502 +    }
   3.503 +
   3.504 +    if (!encoder) {
   3.505          ret = SDL_SetError("No connected encoder found.");
   3.506          goto cleanup;
   3.507      }
   3.508  
   3.509 -    vdata->saved_crtc = KMSDRM_drmModeGetCrtc(vdata->drm_fd, encoder->crtc_id);
   3.510 +    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   3.511  
   3.512 -    if (vdata->saved_crtc == NULL) {
   3.513 -        for (i = 0; i < resources->count_crtcs; i++) {
   3.514 +    /* Try to find a CRTC connected to this encoder */
   3.515 +    dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   3.516 +
   3.517 +    if (!dispdata->saved_crtc) {
   3.518 +        /* No CRTC was connected, find the first CRTC that can be connected */
   3.519 +        for (int i = 0; i < resources->count_crtcs; i++) {
   3.520              if (encoder->possible_crtcs & (1 << i)) {
   3.521                  encoder->crtc_id = resources->crtcs[i];
   3.522 -                SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Set encoder's CRTC to %d.", encoder->crtc_id);
   3.523 -                vdata->saved_crtc = KMSDRM_drmModeGetCrtc(vdata->drm_fd, encoder->crtc_id);
   3.524 +                dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   3.525                  break;
   3.526              }
   3.527          }
   3.528      }
   3.529  
   3.530 -    if (vdata->saved_crtc == NULL) {
   3.531 +    if (!dispdata->saved_crtc) {
   3.532          ret = SDL_SetError("No CRTC found.");
   3.533          goto cleanup;
   3.534      }
   3.535 +
   3.536      SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Saved crtc_id %u, fb_id %u, (%u,%u), %ux%u",
   3.537 -                 vdata->saved_crtc->crtc_id, vdata->saved_crtc->buffer_id, vdata->saved_crtc->x,
   3.538 -                 vdata->saved_crtc->y, vdata->saved_crtc->width, vdata->saved_crtc->height);
   3.539 -    data->crtc_id = encoder->crtc_id;
   3.540 -    data->cur_mode = vdata->saved_crtc->mode;
   3.541 -    vdata->crtc_id = encoder->crtc_id;
   3.542 +                 dispdata->saved_crtc->crtc_id, dispdata->saved_crtc->buffer_id, dispdata->saved_crtc->x,
   3.543 +                 dispdata->saved_crtc->y, dispdata->saved_crtc->width, dispdata->saved_crtc->height);
   3.544  
   3.545 -    // select default mode if this one is not valid
   3.546 -    if (vdata->saved_crtc->mode_valid == 0) {
   3.547 +    dispdata->crtc_id = encoder->crtc_id;
   3.548 +
   3.549 +    /* Figure out the default mode to be set. If the current CRTC's mode isn't
   3.550 +       valid, select the first mode supported by the connector
   3.551 +
   3.552 +       FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
   3.553 +    dispdata->mode = dispdata->saved_crtc->mode;
   3.554 +
   3.555 +    if (dispdata->saved_crtc->mode_valid == 0) {
   3.556          SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
   3.557              "Current mode is invalid, selecting connector's mode #0.");
   3.558 -        data->cur_mode = connector->modes[0];
   3.559 +        dispdata->mode = dispdata->conn->modes[0];
   3.560      }
   3.561  
   3.562 -    SDL_zero(current_mode);
   3.563 -
   3.564 -    current_mode.w = data->cur_mode.hdisplay;
   3.565 -    current_mode.h = data->cur_mode.vdisplay;
   3.566 -    current_mode.refresh_rate = data->cur_mode.vrefresh;
   3.567 -
   3.568 -    /* FIXME ?
   3.569 -    drmModeFB *fb = drmModeGetFB(vdata->drm_fd, vdata->saved_crtc->buffer_id);
   3.570 -    current_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
   3.571 +    /* Setup the single display that's available */
   3.572 +    SDL_VideoDisplay display = {0};
   3.573 +    display.desktop_mode.w = dispdata->mode.hdisplay;
   3.574 +    display.desktop_mode.h = dispdata->mode.vdisplay;
   3.575 +    display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
   3.576 +#if 1
   3.577 +    display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
   3.578 +#else
   3.579 +    /* FIXME */
   3.580 +    drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->saved_crtc->buffer_id);
   3.581 +    display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
   3.582      drmModeFreeFB(fb);
   3.583 -    */
   3.584 -    current_mode.format = SDL_PIXELFORMAT_ARGB8888;
   3.585 -
   3.586 -    current_mode.driverdata = NULL;
   3.587 -
   3.588 -    SDL_zero(display);
   3.589 -    display.desktop_mode = current_mode;
   3.590 -    display.current_mode = current_mode;
   3.591 -
   3.592 -    display.driverdata = data;
   3.593 -    /* SDL_VideoQuit will later SDL_free(display.driverdata) */
   3.594 +#endif
   3.595 +    display.current_mode = display.desktop_mode;
   3.596 +    display.driverdata = dispdata;
   3.597      SDL_AddVideoDisplay(&display);
   3.598  
   3.599 -    /* Setup page flip handler */
   3.600 -    vdata->drm_pollfd.fd = vdata->drm_fd;
   3.601 -    vdata->drm_pollfd.events = POLLIN;
   3.602 -    vdata->drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
   3.603 -    vdata->drm_evctx.page_flip_handler = KMSDRM_FlipHandler;
   3.604 -
   3.605  #ifdef SDL_INPUT_LINUXEV
   3.606      SDL_EVDEV_Init();
   3.607  #endif
   3.608 @@ -515,28 +600,30 @@
   3.609      return ret;
   3.610  
   3.611  cleanup:
   3.612 -    if (encoder != NULL)
   3.613 +    if (encoder)
   3.614          KMSDRM_drmModeFreeEncoder(encoder);
   3.615 -    if (connector != NULL)
   3.616 -        KMSDRM_drmModeFreeConnector(connector);
   3.617 -    if (resources != NULL)
   3.618 +    if (resources)
   3.619          KMSDRM_drmModeFreeResources(resources);
   3.620  
   3.621      if (ret != 0) {
   3.622          /* Error (complete) cleanup */
   3.623 -        SDL_free(data);
   3.624 -        if(vdata->saved_crtc != NULL) {
   3.625 -            KMSDRM_drmModeFreeCrtc(vdata->saved_crtc);
   3.626 -            vdata->saved_crtc = NULL;
   3.627 +        if (dispdata->conn) {
   3.628 +            KMSDRM_drmModeFreeConnector(dispdata->conn);
   3.629 +            dispdata->conn = NULL;
   3.630          }
   3.631 -        if (vdata->gbm != NULL) {
   3.632 -            KMSDRM_gbm_device_destroy(vdata->gbm);
   3.633 -            vdata->gbm = NULL;
   3.634 +        if (dispdata->saved_crtc) {
   3.635 +            KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
   3.636 +            dispdata->saved_crtc = NULL;
   3.637          }
   3.638 -        if (vdata->drm_fd >= 0) {
   3.639 -            close(vdata->drm_fd);
   3.640 -            vdata->drm_fd = -1;
   3.641 +        if (viddata->gbm) {
   3.642 +            KMSDRM_gbm_device_destroy(viddata->gbm);
   3.643 +            viddata->gbm = NULL;
   3.644          }
   3.645 +        if (viddata->drm_fd >= 0) {
   3.646 +            close(viddata->drm_fd);
   3.647 +            viddata->drm_fd = -1;
   3.648 +        }
   3.649 +        SDL_free(dispdata);
   3.650      }
   3.651      return ret;
   3.652  }
   3.653 @@ -544,7 +631,8 @@
   3.654  void
   3.655  KMSDRM_VideoQuit(_THIS)
   3.656  {
   3.657 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
   3.658 +    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   3.659 +    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
   3.660  
   3.661      SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
   3.662  
   3.663 @@ -552,27 +640,40 @@
   3.664          SDL_GL_UnloadLibrary();
   3.665      }
   3.666  
   3.667 -    if(vdata->saved_crtc != NULL) {
   3.668 -        if(vdata->drm_fd >= 0 && vdata->saved_conn_id > 0) {
   3.669 -            /* Restore saved CRTC settings */
   3.670 -            drmModeCrtc *crtc = vdata->saved_crtc;
   3.671 -            if(KMSDRM_drmModeSetCrtc(vdata->drm_fd, crtc->crtc_id, crtc->buffer_id,
   3.672 -                                     crtc->x, crtc->y, &vdata->saved_conn_id, 1,
   3.673 -                                     &crtc->mode) != 0) {
   3.674 -                SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode");
   3.675 -            }
   3.676 +    /* Clear out the window list */
   3.677 +    SDL_free(viddata->windows);
   3.678 +    viddata->windows = NULL;
   3.679 +    viddata->max_windows = 0;
   3.680 +    viddata->num_windows = 0;
   3.681 +
   3.682 +    /* Restore saved CRTC settings */
   3.683 +    if (viddata->drm_fd >= 0 && dispdata->conn && dispdata->saved_crtc) {
   3.684 +        drmModeConnector *conn = dispdata->conn;
   3.685 +        drmModeCrtc *crtc = dispdata->saved_crtc;
   3.686 +
   3.687 +        int ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, crtc->crtc_id, crtc->buffer_id,
   3.688 +                                        crtc->x, crtc->y, &conn->connector_id, 1, &crtc->mode);
   3.689 +
   3.690 +        if (ret != 0) {
   3.691 +            SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode");
   3.692          }
   3.693 -        KMSDRM_drmModeFreeCrtc(vdata->saved_crtc);
   3.694 -        vdata->saved_crtc = NULL;
   3.695      }
   3.696 -    if (vdata->gbm != NULL) {
   3.697 -        KMSDRM_gbm_device_destroy(vdata->gbm);
   3.698 -        vdata->gbm = NULL;
   3.699 +    if (dispdata->conn) {
   3.700 +        KMSDRM_drmModeFreeConnector(dispdata->conn);
   3.701 +        dispdata->conn = NULL;
   3.702      }
   3.703 -    if (vdata->drm_fd >= 0) {
   3.704 -        close(vdata->drm_fd);
   3.705 -        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", vdata->drm_fd);
   3.706 -        vdata->drm_fd = -1;
   3.707 +    if (dispdata->saved_crtc) {
   3.708 +        KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
   3.709 +        dispdata->saved_crtc = NULL;
   3.710 +    }
   3.711 +    if (viddata->gbm) {
   3.712 +        KMSDRM_gbm_device_destroy(viddata->gbm);
   3.713 +        viddata->gbm = NULL;
   3.714 +    }
   3.715 +    if (viddata->drm_fd >= 0) {
   3.716 +        close(viddata->drm_fd);
   3.717 +        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
   3.718 +        viddata->drm_fd = -1;
   3.719      }
   3.720  #ifdef SDL_INPUT_LINUXEV
   3.721      SDL_EVDEV_Quit();
   3.722 @@ -582,48 +683,68 @@
   3.723  void
   3.724  KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   3.725  {
   3.726 -    /* Only one display mode available, the current one */
   3.727 -    SDL_AddDisplayMode(display, &display->current_mode);
   3.728 +    SDL_DisplayData *dispdata = display->driverdata;
   3.729 +    drmModeConnector *conn = dispdata->conn;
   3.730 +
   3.731 +    for (int i = 0; i < conn->count_modes; i++) {
   3.732 +        SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
   3.733 +
   3.734 +        if (modedata) {
   3.735 +          modedata->mode_index = i;
   3.736 +        }
   3.737 +
   3.738 +        SDL_DisplayMode mode;
   3.739 +        mode.w = conn->modes[i].hdisplay;
   3.740 +        mode.h = conn->modes[i].vdisplay;
   3.741 +        mode.refresh_rate = conn->modes[i].vrefresh;
   3.742 +        mode.format = SDL_PIXELFORMAT_ARGB8888;
   3.743 +        mode.driverdata = modedata;
   3.744 +
   3.745 +        if (!SDL_AddDisplayMode(display, &mode)) {
   3.746 +            SDL_free(modedata);
   3.747 +        }
   3.748 +    }
   3.749  }
   3.750  
   3.751  int
   3.752  KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   3.753  {
   3.754 +    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   3.755 +    SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
   3.756 +    SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   3.757 +
   3.758 +    if (!modedata) {
   3.759 +        return SDL_SetError("Mode doesn't have an associated index");
   3.760 +    }
   3.761 +
   3.762 +    drmModeConnector *conn = dispdata->conn;
   3.763 +    dispdata->mode = conn->modes[modedata->mode_index];
   3.764 +
   3.765 +    for (int i = 0; i < viddata->num_windows; i++) {
   3.766 +        SDL_Window *window = viddata->windows[i];
   3.767 +        SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   3.768 +
   3.769 +#if SDL_VIDEO_OPENGL_EGL
   3.770 +        /* Can't recreate EGL surfaces right now, need to wait until SwapWindow
   3.771 +           so the correct thread-local surface and context state are available */
   3.772 +        windata->egl_surface_dirty = 1;
   3.773 +#else
   3.774 +        if (KMSDRM_CreateSurfaces(_this, window)) {
   3.775 +            return -1;
   3.776 +        }
   3.777 +#endif
   3.778 +
   3.779 +        /* Tell app about the resize */
   3.780 +        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
   3.781 +    }
   3.782 +
   3.783      return 0;
   3.784  }
   3.785  
   3.786  int
   3.787  KMSDRM_CreateWindow(_THIS, SDL_Window * window)
   3.788  {
   3.789 -    SDL_WindowData *wdata;
   3.790 -    SDL_VideoDisplay *display;
   3.791 -    SDL_VideoData *vdata = ((SDL_VideoData *)_this->driverdata);
   3.792 -    Uint32 surface_fmt, surface_flags;
   3.793 -
   3.794 -    /* Allocate window internal data */
   3.795 -    wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
   3.796 -    if (wdata == NULL) {
   3.797 -        SDL_OutOfMemory();
   3.798 -        goto error;
   3.799 -    }
   3.800 -
   3.801 -    wdata->waiting_for_flip = SDL_FALSE;
   3.802 -    display = SDL_GetDisplayForWindow(window);
   3.803 -
   3.804 -    /* Windows have one size for now */
   3.805 -    window->w = display->desktop_mode.w;
   3.806 -    window->h = display->desktop_mode.h;
   3.807 -
   3.808 -    /* Maybe you didn't ask for a fullscreen OpenGL window, but that's what you get */
   3.809 -    window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
   3.810 -
   3.811 -    surface_fmt = GBM_FORMAT_XRGB8888;
   3.812 -    surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
   3.813 -
   3.814 -    if (!KMSDRM_gbm_device_is_format_supported(vdata->gbm, surface_fmt, surface_flags)) {
   3.815 -        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
   3.816 -    }
   3.817 -    wdata->gs = KMSDRM_gbm_surface_create(vdata->gbm, window->w, window->h, surface_fmt, surface_flags);
   3.818 +    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   3.819  
   3.820  #if SDL_VIDEO_OPENGL_EGL
   3.821      if (!_this->egl_data) {
   3.822 @@ -631,82 +752,94 @@
   3.823              goto error;
   3.824          }
   3.825      }
   3.826 -    SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
   3.827 -    wdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) wdata->gs);
   3.828 +#endif
   3.829  
   3.830 -    if (wdata->egl_surface == EGL_NO_SURFACE) {
   3.831 -        SDL_SetError("Could not create EGL window surface");
   3.832 +    /* Allocate window internal data */
   3.833 +    SDL_WindowData *windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
   3.834 +
   3.835 +    if (!windata) {
   3.836 +        SDL_OutOfMemory();
   3.837          goto error;
   3.838      }
   3.839 -#endif /* SDL_VIDEO_OPENGL_EGL */
   3.840 +
   3.841 +    /* Windows have one size for now */
   3.842 +    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   3.843 +    window->w = display->desktop_mode.w;
   3.844 +    window->h = display->desktop_mode.h;
   3.845 +
   3.846 +    /* Maybe you didn't ask for a fullscreen OpenGL window, but that's what you get */
   3.847 +    window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
   3.848  
   3.849      /* In case we want low-latency, double-buffer video, we take note here */
   3.850 -    wdata->double_buffer = SDL_FALSE;
   3.851 +    windata->double_buffer = SDL_FALSE;
   3.852 +
   3.853      if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
   3.854 -        wdata->double_buffer = SDL_TRUE;
   3.855 +        windata->double_buffer = SDL_TRUE;
   3.856      }
   3.857  
   3.858 -    /* Window is created, but we have yet to set up CRTC to one of the GBM buffers if we want
   3.859 -       drmModePageFlip to work, and we can't do it until EGL is completely setup, because we
   3.860 -       need to do eglSwapBuffers so we can get a valid GBM buffer object to call
   3.861 -       drmModeSetCrtc on it. */
   3.862 -    wdata->crtc_ready = SDL_FALSE;
   3.863 +    /* Setup driver data for this window */
   3.864 +    window->driverdata = windata;
   3.865  
   3.866 -    /* Setup driver data for this window */
   3.867 -    window->driverdata = wdata;
   3.868 +    if (KMSDRM_CreateSurfaces(_this, window)) {
   3.869 +      goto error;
   3.870 +    }
   3.871  
   3.872 -    /* One window, it always has focus */
   3.873 -    SDL_SetMouseFocus(window);
   3.874 -    SDL_SetKeyboardFocus(window);
   3.875 +    /* Add window to the internal list of tracked windows. Note, while it may
   3.876 +       seem odd to support multiple fullscreen windows, some apps create an
   3.877 +       extra window as a dummy surface when working with multiple contexts */
   3.878 +    windata->viddata = viddata;
   3.879  
   3.880 -    /* Window has been successfully created */
   3.881 +    if (viddata->num_windows >= viddata->max_windows) {
   3.882 +        int new_max_windows = viddata->max_windows + 1;
   3.883 +        viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
   3.884 +              new_max_windows * sizeof(SDL_Window *));
   3.885 +        viddata->max_windows = new_max_windows;
   3.886 +
   3.887 +        if (!viddata->windows) {
   3.888 +            SDL_OutOfMemory();
   3.889 +            goto error;
   3.890 +        }
   3.891 +    }
   3.892 +
   3.893 +    viddata->windows[viddata->num_windows++] = window;
   3.894 +
   3.895      return 0;
   3.896  
   3.897  error:
   3.898 -    if (wdata != NULL) {
   3.899 -#if SDL_VIDEO_OPENGL_EGL
   3.900 -        if (wdata->egl_surface != EGL_NO_SURFACE)
   3.901 -            SDL_EGL_DestroySurface(_this, wdata->egl_surface);
   3.902 -#endif /* SDL_VIDEO_OPENGL_EGL */
   3.903 -        if (wdata->gs != NULL)
   3.904 -            KMSDRM_gbm_surface_destroy(wdata->gs);
   3.905 -        SDL_free(wdata);
   3.906 -    }
   3.907 +    KMSDRM_DestroyWindow(_this, window);
   3.908 +
   3.909      return -1;
   3.910  }
   3.911  
   3.912  void
   3.913  KMSDRM_DestroyWindow(_THIS, SDL_Window * window)
   3.914  {
   3.915 -    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   3.916 -    if(data) {
   3.917 -        /* Wait for any pending page flips and unlock buffer */
   3.918 -        KMSDRM_WaitPageFlip(_this, data, -1);
   3.919 -        if (data->crtc_bo != NULL) {
   3.920 -            KMSDRM_gbm_surface_release_buffer(data->gs, data->crtc_bo);
   3.921 -            data->crtc_bo = NULL;
   3.922 +    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   3.923 +
   3.924 +    if (!windata) {
   3.925 +        return;
   3.926 +    }
   3.927 +
   3.928 +    /* Remove from the internal window list */
   3.929 +    SDL_VideoData *viddata = windata->viddata;
   3.930 +
   3.931 +    for (int i = 0; i < viddata->num_windows; i++) {
   3.932 +        if (viddata->windows[i] == window) {
   3.933 +            viddata->num_windows--;
   3.934 +
   3.935 +            for (int j = i; j < viddata->num_windows; j++) {
   3.936 +                viddata->windows[j] = viddata->windows[j + 1];
   3.937 +            }
   3.938 +
   3.939 +            break;
   3.940          }
   3.941 -        if (data->next_bo != NULL) {
   3.942 -            KMSDRM_gbm_surface_release_buffer(data->gs, data->next_bo);
   3.943 -            data->next_bo = NULL;
   3.944 -        }
   3.945 -        if (data->current_bo != NULL) {
   3.946 -            KMSDRM_gbm_surface_release_buffer(data->gs, data->current_bo);
   3.947 -            data->current_bo = NULL;
   3.948 -        }
   3.949 -#if SDL_VIDEO_OPENGL_EGL
   3.950 -        SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   3.951 -        if (data->egl_surface != EGL_NO_SURFACE) {
   3.952 -            SDL_EGL_DestroySurface(_this, data->egl_surface);
   3.953 -        }
   3.954 -#endif /* SDL_VIDEO_OPENGL_EGL */
   3.955 -        if (data->gs != NULL) {
   3.956 -            KMSDRM_gbm_surface_destroy(data->gs);
   3.957 -            data->gs = NULL;
   3.958 -        }
   3.959 -        SDL_free(data);
   3.960 -        window->driverdata = NULL;
   3.961      }
   3.962 +
   3.963 +    KMSDRM_DestroySurfaces(_this, window);
   3.964 +
   3.965 +    window->driverdata = NULL;
   3.966 +
   3.967 +    SDL_free(windata);
   3.968  }
   3.969  
   3.970  int
     4.1 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.h	Fri Feb 07 20:20:37 2020 -0800
     4.2 +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h	Sun Feb 09 11:44:22 2020 -0800
     4.3 @@ -28,7 +28,6 @@
     4.4  
     4.5  #include <fcntl.h>
     4.6  #include <unistd.h>
     4.7 -#include <poll.h>
     4.8  #include <xf86drm.h>
     4.9  #include <xf86drmMode.h>
    4.10  #include <gbm.h>
    4.11 @@ -41,32 +40,39 @@
    4.12      int devindex;               /* device index that was passed on creation */
    4.13      int drm_fd;                 /* DRM file desc */
    4.14      struct gbm_device *gbm;
    4.15 -    drmEventContext drm_evctx;  /* DRM event context */
    4.16 -    struct pollfd drm_pollfd;   /* pollfd containing DRM file desc */
    4.17 -    drmModeCrtc *saved_crtc;    /* Saved CRTC to restore on quit */
    4.18 -    uint32_t saved_conn_id;     /* Saved DRM connector ID */
    4.19 -    uint32_t crtc_id;           /* CRTC in use */
    4.20 +
    4.21 +    SDL_Window **windows;
    4.22 +    int max_windows;
    4.23 +    int num_windows;
    4.24  } SDL_VideoData;
    4.25  
    4.26  
    4.27 +typedef struct SDL_DisplayModeData
    4.28 +{
    4.29 +    int mode_index;
    4.30 +} SDL_DisplayModeData;
    4.31 +
    4.32 +
    4.33  typedef struct SDL_DisplayData
    4.34  {
    4.35 -    uint32_t encoder_id;
    4.36      uint32_t crtc_id;
    4.37 -    drmModeModeInfo cur_mode;
    4.38 +    drmModeConnector *conn;
    4.39 +    drmModeModeInfo mode;
    4.40 +    drmModeCrtc *saved_crtc;    /* CRTC to restore on quit */
    4.41  } SDL_DisplayData;
    4.42  
    4.43  
    4.44  typedef struct SDL_WindowData
    4.45  {
    4.46 +    SDL_VideoData *viddata;
    4.47      struct gbm_surface *gs;
    4.48 -    struct gbm_bo *current_bo;
    4.49 +    struct gbm_bo *curr_bo;
    4.50      struct gbm_bo *next_bo;
    4.51      struct gbm_bo *crtc_bo;
    4.52      SDL_bool waiting_for_flip;
    4.53 -    SDL_bool crtc_ready;
    4.54      SDL_bool double_buffer;
    4.55  #if SDL_VIDEO_OPENGL_EGL
    4.56 +    int egl_surface_dirty;
    4.57      EGLSurface egl_surface;
    4.58  #endif
    4.59  } SDL_WindowData;
    4.60 @@ -78,8 +84,9 @@
    4.61  } KMSDRM_FBInfo;
    4.62  
    4.63  /* Helper functions */
    4.64 +int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window);
    4.65  KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
    4.66 -SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *wdata, int timeout);
    4.67 +SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout);
    4.68  
    4.69  /****************************************************************************/
    4.70  /* SDL_VideoDevice functions declaration                                    */