src/video/kmsdrm/SDL_kmsdrmvideo.c
author Sam Lantinga
Mon, 02 Mar 2020 14:55:40 -0800
changeset 13577 d28975f16368
parent 13555 936b1869a34a
child 13592 3ff45857428d
permissions -rw-r--r--
Fixed bug 5007 - Segfault in KMSDRM_VideoQuit() on Raspberry Pi Zero with no display attached

Charles Huber

This patch fixes the segfault on my Pi, though the valid display index range reported by the CHECK_DISPLAY_INDEX() macro in src/video/SDL_video.c is a little weird:

$ SDL_VIDEO_EGL_DRIVER=libEGL.so SDL_VIDEO_GL_DRIVER=libGLESv2.so ./a.out
SDL_Init(): displayIndex must be in the range 0 - -1
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #include "../../SDL_internal.h"
    23 
    24 #if SDL_VIDEO_DRIVER_KMSDRM
    25 
    26 /* SDL internals */
    27 #include "../SDL_sysvideo.h"
    28 #include "SDL_syswm.h"
    29 #include "SDL_log.h"
    30 #include "SDL_hints.h"
    31 #include "../../events/SDL_events_c.h"
    32 #include "../../events/SDL_mouse_c.h"
    33 #include "../../events/SDL_keyboard_c.h"
    34 
    35 #ifdef SDL_INPUT_LINUXEV
    36 #include "../../core/linux/SDL_evdev.h"
    37 #endif
    38 
    39 /* KMS/DRM declarations */
    40 #include "SDL_kmsdrmvideo.h"
    41 #include "SDL_kmsdrmevents.h"
    42 #include "SDL_kmsdrmopengles.h"
    43 #include "SDL_kmsdrmmouse.h"
    44 #include "SDL_kmsdrmdyn.h"
    45 #include <sys/stat.h>
    46 #include <dirent.h>
    47 #include <errno.h>
    48 #include <poll.h>
    49 
    50 #define KMSDRM_DRI_PATH "/dev/dri/"
    51 
    52 static int
    53 check_modestting(int devindex)
    54 {
    55     SDL_bool available = SDL_FALSE;
    56     char device[512];
    57     int drm_fd;
    58 
    59     SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
    60 
    61     drm_fd = open(device, O_RDWR | O_CLOEXEC);
    62     if (drm_fd >= 0) {
    63         if (SDL_KMSDRM_LoadSymbols()) {
    64             drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
    65             if (resources) {
    66                 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
    67                              KMSDRM_DRI_PATH, devindex,
    68                              resources->count_connectors, resources->count_encoders, resources->count_crtcs);
    69 
    70                 if (resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) {
    71                     available = SDL_TRUE;
    72                 }
    73                 KMSDRM_drmModeFreeResources(resources);
    74             }
    75             SDL_KMSDRM_UnloadSymbols();
    76         }
    77         close(drm_fd);
    78     }
    79 
    80     return available;
    81 }
    82 
    83 static int get_dricount(void)
    84 {
    85     int devcount = 0;
    86     struct dirent *res;
    87     struct stat sb;
    88     DIR *folder;
    89 
    90     if (!(stat(KMSDRM_DRI_PATH, &sb) == 0
    91                 && S_ISDIR(sb.st_mode))) {
    92         printf("The path %s cannot be opened or is not available\n",
    93                KMSDRM_DRI_PATH);
    94         return 0;
    95     }
    96 
    97     if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
    98         printf("The path %s cannot be opened\n",
    99                KMSDRM_DRI_PATH);
   100         return 0;
   101     }
   102 
   103     folder = opendir(KMSDRM_DRI_PATH);
   104     if (folder) {
   105         while ((res = readdir(folder))) {
   106             int len = SDL_strlen(res->d_name);
   107             if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
   108                 devcount++;
   109             }
   110         }
   111         closedir(folder);
   112     }
   113 
   114     return devcount;
   115 }
   116 
   117 static int
   118 get_driindex(void)
   119 {
   120     const int devcount = get_dricount();
   121     int i;
   122 
   123     for (i = 0; i < devcount; i++) {
   124         if (check_modestting(i)) {
   125             return i;
   126         }
   127     }
   128 
   129     return -ENOENT;
   130 }
   131 
   132 static int
   133 KMSDRM_Available(void)
   134 {
   135     int ret = -ENOENT;
   136 
   137     ret = get_driindex();
   138     if (ret >= 0)
   139         return 1;
   140 
   141     return ret;
   142 }
   143 
   144 static void
   145 KMSDRM_DeleteDevice(SDL_VideoDevice * device)
   146 {
   147     if (device->driverdata) {
   148         SDL_free(device->driverdata);
   149         device->driverdata = NULL;
   150     }
   151 
   152     SDL_free(device);
   153 
   154     SDL_KMSDRM_UnloadSymbols();
   155 }
   156 
   157 static SDL_VideoDevice *
   158 KMSDRM_CreateDevice(int devindex)
   159 {
   160     SDL_VideoDevice *device;
   161     SDL_VideoData *viddata;
   162 
   163     if (!devindex || (devindex > 99)) {
   164         devindex = get_driindex();
   165     }
   166 
   167     if (devindex < 0) {
   168         SDL_SetError("devindex (%d) must be between 0 and 99.\n", devindex);
   169         return NULL;
   170     }
   171 
   172     if (!SDL_KMSDRM_LoadSymbols()) {
   173         return NULL;
   174     }
   175 
   176     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   177     if (!device) {
   178         SDL_OutOfMemory();
   179         return NULL;
   180     }
   181 
   182     viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   183     if (!viddata) {
   184         SDL_OutOfMemory();
   185         goto cleanup;
   186     }
   187     viddata->devindex = devindex;
   188     viddata->drm_fd = -1;
   189 
   190     device->driverdata = viddata;
   191 
   192     /* Setup all functions which we can handle */
   193     device->VideoInit = KMSDRM_VideoInit;
   194     device->VideoQuit = KMSDRM_VideoQuit;
   195     device->GetDisplayModes = KMSDRM_GetDisplayModes;
   196     device->SetDisplayMode = KMSDRM_SetDisplayMode;
   197     device->CreateSDLWindow = KMSDRM_CreateWindow;
   198     device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
   199     device->SetWindowTitle = KMSDRM_SetWindowTitle;
   200     device->SetWindowIcon = KMSDRM_SetWindowIcon;
   201     device->SetWindowPosition = KMSDRM_SetWindowPosition;
   202     device->SetWindowSize = KMSDRM_SetWindowSize;
   203     device->ShowWindow = KMSDRM_ShowWindow;
   204     device->HideWindow = KMSDRM_HideWindow;
   205     device->RaiseWindow = KMSDRM_RaiseWindow;
   206     device->MaximizeWindow = KMSDRM_MaximizeWindow;
   207     device->MinimizeWindow = KMSDRM_MinimizeWindow;
   208     device->RestoreWindow = KMSDRM_RestoreWindow;
   209     device->SetWindowGrab = KMSDRM_SetWindowGrab;
   210     device->DestroyWindow = KMSDRM_DestroyWindow;
   211     device->GetWindowWMInfo = KMSDRM_GetWindowWMInfo;
   212 #if SDL_VIDEO_OPENGL_EGL
   213     device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
   214     device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
   215     device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
   216     device->GL_CreateContext = KMSDRM_GLES_CreateContext;
   217     device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
   218     device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
   219     device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
   220     device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
   221     device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
   222 #endif
   223     device->PumpEvents = KMSDRM_PumpEvents;
   224     device->free = KMSDRM_DeleteDevice;
   225 
   226     return device;
   227 
   228 cleanup:
   229     if (device)
   230         SDL_free(device);
   231     if (viddata)
   232         SDL_free(viddata);
   233     return NULL;
   234 }
   235 
   236 VideoBootStrap KMSDRM_bootstrap = {
   237     "KMSDRM",
   238     "KMS/DRM Video Driver",
   239     KMSDRM_Available,
   240     KMSDRM_CreateDevice
   241 };
   242 
   243 
   244 static void
   245 KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
   246 {
   247     KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
   248 
   249     if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
   250         KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
   251         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
   252     }
   253 
   254     SDL_free(fb_info);
   255 }
   256 
   257 KMSDRM_FBInfo *
   258 KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
   259 {
   260     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   261     unsigned w,h;
   262     int ret;
   263     Uint32 stride, handle;
   264 
   265     /* Check for an existing framebuffer */
   266     KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
   267 
   268     if (fb_info) {
   269         return fb_info;
   270     }
   271 
   272     /* Create a structure that contains enough info to remove the framebuffer
   273        when the backing buffer is destroyed */
   274     fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
   275 
   276     if (!fb_info) {
   277         SDL_OutOfMemory();
   278         return NULL;
   279     }
   280 
   281     fb_info->drm_fd = viddata->drm_fd;
   282 
   283     /* Create framebuffer object for the buffer */
   284     w = KMSDRM_gbm_bo_get_width(bo);
   285     h = KMSDRM_gbm_bo_get_height(bo);
   286     stride = KMSDRM_gbm_bo_get_stride(bo);
   287     handle = KMSDRM_gbm_bo_get_handle(bo).u32;
   288     ret = KMSDRM_drmModeAddFB(viddata->drm_fd, w, h, 24, 32, stride, handle,
   289                                   &fb_info->fb_id);
   290     if (ret) {
   291       SDL_free(fb_info);
   292       return NULL;
   293     }
   294 
   295     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, stride %u from BO %p",
   296                  fb_info->fb_id, w, h, stride, (void *)bo);
   297 
   298     /* Associate our DRM framebuffer with this buffer object */
   299     KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
   300 
   301     return fb_info;
   302 }
   303 
   304 static void
   305 KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
   306 {
   307     *((SDL_bool *) data) = SDL_FALSE;
   308 }
   309 
   310 SDL_bool
   311 KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
   312     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   313     drmEventContext ev = {0};
   314     struct pollfd pfd = {0};
   315 
   316     ev.version = DRM_EVENT_CONTEXT_VERSION;
   317     ev.page_flip_handler = KMSDRM_FlipHandler;
   318 
   319     pfd.fd = viddata->drm_fd;
   320     pfd.events = POLLIN;
   321 
   322     while (windata->waiting_for_flip) {
   323         pfd.revents = 0;
   324 
   325         if (poll(&pfd, 1, timeout) < 0) {
   326             SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
   327             return SDL_FALSE;
   328         }
   329 
   330         if (pfd.revents & (POLLHUP | POLLERR)) {
   331             SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
   332             return SDL_FALSE;
   333         }
   334 
   335         if (pfd.revents & POLLIN) {
   336             /* Page flip? If so, drmHandleEvent will unset windata->waiting_for_flip */
   337             KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
   338         } else {
   339             /* Timed out and page flip didn't happen */
   340             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
   341             return SDL_FALSE;
   342         }
   343     }
   344 
   345     return SDL_TRUE;
   346 }
   347 
   348 /*****************************************************************************/
   349 /* SDL Video and Display initialization/handling functions                   */
   350 /* _this is a SDL_VideoDevice *                                              */
   351 /*****************************************************************************/
   352 static void
   353 KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
   354 {
   355     SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   356 
   357     KMSDRM_WaitPageFlip(_this, windata, -1);
   358 
   359     if (windata->curr_bo) {
   360         KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
   361         windata->curr_bo = NULL;
   362     }
   363 
   364     if (windata->next_bo) {
   365         KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
   366         windata->next_bo = NULL;
   367     }
   368 
   369 #if SDL_VIDEO_OPENGL_EGL
   370     SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   371 
   372     if (windata->egl_surface != EGL_NO_SURFACE) {
   373         SDL_EGL_DestroySurface(_this, windata->egl_surface);
   374         windata->egl_surface = EGL_NO_SURFACE;
   375     }
   376 #endif
   377 
   378     if (windata->gs) {
   379         KMSDRM_gbm_surface_destroy(windata->gs);
   380         windata->gs = NULL;
   381     }
   382 }
   383 
   384 int
   385 KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
   386 {
   387     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   388     SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   389     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   390     Uint32 width = dispdata->mode.hdisplay;
   391     Uint32 height = dispdata->mode.vdisplay;
   392     Uint32 surface_fmt = GBM_FORMAT_XRGB8888;
   393     Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
   394     EGLContext egl_context;
   395 
   396     if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) {
   397         SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
   398     }
   399 
   400 #if SDL_VIDEO_OPENGL_EGL
   401     SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
   402     egl_context = (EGLContext)SDL_GL_GetCurrentContext();
   403 #endif
   404 
   405     KMSDRM_DestroySurfaces(_this, window);
   406 
   407     windata->gs = KMSDRM_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags);
   408 
   409     if (!windata->gs) {
   410         return SDL_SetError("Could not create GBM surface");
   411     }
   412 
   413 #if SDL_VIDEO_OPENGL_EGL
   414     windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
   415 
   416     if (windata->egl_surface == EGL_NO_SURFACE) {
   417         return SDL_SetError("Could not create EGL window surface");
   418     }
   419 
   420     SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
   421 
   422     windata->egl_surface_dirty = 0;
   423 #endif
   424 
   425     return 0;
   426 }
   427 
   428 int
   429 KMSDRM_VideoInit(_THIS)
   430 {
   431     int ret = 0;
   432     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   433     SDL_DisplayData *dispdata = NULL;
   434     drmModeRes *resources = NULL;
   435     drmModeEncoder *encoder = NULL;
   436     char devname[32];
   437     SDL_VideoDisplay display = {0};
   438 
   439     dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   440 
   441     if (!dispdata) {
   442         return SDL_OutOfMemory();
   443     }
   444 
   445     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
   446 
   447     /* Open /dev/dri/cardNN */
   448     SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
   449 
   450     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
   451     viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
   452 
   453     if (viddata->drm_fd < 0) {
   454         ret = SDL_SetError("Could not open %s", devname);
   455         goto cleanup;
   456     }
   457 
   458     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
   459 
   460     viddata->gbm = KMSDRM_gbm_create_device(viddata->drm_fd);
   461     if (!viddata->gbm) {
   462         ret = SDL_SetError("Couldn't create gbm device.");
   463         goto cleanup;
   464     }
   465 
   466     /* Get all of the available connectors / devices / crtcs */
   467     resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
   468     if (!resources) {
   469         ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
   470         goto cleanup;
   471     }
   472 
   473     for (int i = 0; i < resources->count_connectors; i++) {
   474         drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
   475 
   476         if (!conn) {
   477             continue;
   478         }
   479 
   480         if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
   481             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
   482                          conn->connector_id, conn->count_modes);
   483             dispdata->conn = conn;
   484             break;
   485         }
   486 
   487         KMSDRM_drmModeFreeConnector(conn);
   488     }
   489 
   490     if (!dispdata->conn) {
   491         ret = SDL_SetError("No currently active connector found.");
   492         goto cleanup;
   493     }
   494 
   495     /* Try to find the connector's current encoder */
   496     for (int i = 0; i < resources->count_encoders; i++) {
   497         encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   498 
   499         if (!encoder) {
   500           continue;
   501         }
   502 
   503         if (encoder->encoder_id == dispdata->conn->encoder_id) {
   504             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   505             break;
   506         }
   507 
   508         KMSDRM_drmModeFreeEncoder(encoder);
   509         encoder = NULL;
   510     }
   511 
   512     if (!encoder) {
   513         /* No encoder was connected, find the first supported one */
   514         for (int i = 0, j; i < resources->count_encoders; i++) {
   515             encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   516 
   517             if (!encoder) {
   518               continue;
   519             }
   520 
   521             for (j = 0; j < dispdata->conn->count_encoders; j++) {
   522                 if (dispdata->conn->encoders[j] == encoder->encoder_id) {
   523                     break;
   524                 }
   525             }
   526 
   527             if (j != dispdata->conn->count_encoders) {
   528               break;
   529             }
   530 
   531             KMSDRM_drmModeFreeEncoder(encoder);
   532             encoder = NULL;
   533         }
   534     }
   535 
   536     if (!encoder) {
   537         ret = SDL_SetError("No connected encoder found.");
   538         goto cleanup;
   539     }
   540 
   541     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   542 
   543     /* Try to find a CRTC connected to this encoder */
   544     dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   545 
   546     if (!dispdata->saved_crtc) {
   547         /* No CRTC was connected, find the first CRTC that can be connected */
   548         for (int i = 0; i < resources->count_crtcs; i++) {
   549             if (encoder->possible_crtcs & (1 << i)) {
   550                 encoder->crtc_id = resources->crtcs[i];
   551                 dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   552                 break;
   553             }
   554         }
   555     }
   556 
   557     if (!dispdata->saved_crtc) {
   558         ret = SDL_SetError("No CRTC found.");
   559         goto cleanup;
   560     }
   561 
   562     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Saved crtc_id %u, fb_id %u, (%u,%u), %ux%u",
   563                  dispdata->saved_crtc->crtc_id, dispdata->saved_crtc->buffer_id, dispdata->saved_crtc->x,
   564                  dispdata->saved_crtc->y, dispdata->saved_crtc->width, dispdata->saved_crtc->height);
   565 
   566     dispdata->crtc_id = encoder->crtc_id;
   567 
   568     /* Figure out the default mode to be set. If the current CRTC's mode isn't
   569        valid, select the first mode supported by the connector
   570 
   571        FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
   572     dispdata->mode = dispdata->saved_crtc->mode;
   573 
   574     if (dispdata->saved_crtc->mode_valid == 0) {
   575         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
   576             "Current mode is invalid, selecting connector's mode #0.");
   577         dispdata->mode = dispdata->conn->modes[0];
   578     }
   579 
   580     /* Setup the single display that's available */
   581 
   582     display.desktop_mode.w = dispdata->mode.hdisplay;
   583     display.desktop_mode.h = dispdata->mode.vdisplay;
   584     display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
   585 #if 1
   586     display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
   587 #else
   588     /* FIXME */
   589     drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->saved_crtc->buffer_id);
   590     display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
   591     drmModeFreeFB(fb);
   592 #endif
   593     display.current_mode = display.desktop_mode;
   594     display.driverdata = dispdata;
   595     SDL_AddVideoDisplay(&display);
   596 
   597 #ifdef SDL_INPUT_LINUXEV
   598     SDL_EVDEV_Init();
   599 #endif
   600 
   601     KMSDRM_InitMouse(_this);
   602 
   603     return ret;
   604 
   605 cleanup:
   606     if (encoder)
   607         KMSDRM_drmModeFreeEncoder(encoder);
   608     if (resources)
   609         KMSDRM_drmModeFreeResources(resources);
   610 
   611     if (ret != 0) {
   612         /* Error (complete) cleanup */
   613         if (dispdata->conn) {
   614             KMSDRM_drmModeFreeConnector(dispdata->conn);
   615             dispdata->conn = NULL;
   616         }
   617         if (dispdata->saved_crtc) {
   618             KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
   619             dispdata->saved_crtc = NULL;
   620         }
   621         if (viddata->gbm) {
   622             KMSDRM_gbm_device_destroy(viddata->gbm);
   623             viddata->gbm = NULL;
   624         }
   625         if (viddata->drm_fd >= 0) {
   626             close(viddata->drm_fd);
   627             viddata->drm_fd = -1;
   628         }
   629         SDL_free(dispdata);
   630     }
   631     return ret;
   632 }
   633 
   634 void
   635 KMSDRM_VideoQuit(_THIS)
   636 {
   637     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   638     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
   639 
   640     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
   641 
   642     if (_this->gl_config.driver_loaded) {
   643         SDL_GL_UnloadLibrary();
   644     }
   645 
   646     /* Clear out the window list */
   647     SDL_free(viddata->windows);
   648     viddata->windows = NULL;
   649     viddata->max_windows = 0;
   650     viddata->num_windows = 0;
   651 
   652     /* Restore saved CRTC settings */
   653     if (viddata->drm_fd >= 0 && dispdata && dispdata->conn && dispdata->saved_crtc) {
   654         drmModeConnector *conn = dispdata->conn;
   655         drmModeCrtc *crtc = dispdata->saved_crtc;
   656 
   657         int ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, crtc->crtc_id, crtc->buffer_id,
   658                                         crtc->x, crtc->y, &conn->connector_id, 1, &crtc->mode);
   659 
   660         if (ret != 0) {
   661             SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode");
   662         }
   663     }
   664     if (dispdata && dispdata->conn) {
   665         KMSDRM_drmModeFreeConnector(dispdata->conn);
   666         dispdata->conn = NULL;
   667     }
   668     if (dispdata && dispdata->saved_crtc) {
   669         KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
   670         dispdata->saved_crtc = NULL;
   671     }
   672     if (viddata->gbm) {
   673         KMSDRM_gbm_device_destroy(viddata->gbm);
   674         viddata->gbm = NULL;
   675     }
   676     if (viddata->drm_fd >= 0) {
   677         close(viddata->drm_fd);
   678         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
   679         viddata->drm_fd = -1;
   680     }
   681 #ifdef SDL_INPUT_LINUXEV
   682     SDL_EVDEV_Quit();
   683 #endif
   684 }
   685 
   686 void
   687 KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   688 {
   689     SDL_DisplayData *dispdata = display->driverdata;
   690     drmModeConnector *conn = dispdata->conn;
   691     SDL_DisplayMode mode;
   692 
   693     for (int i = 0; i < conn->count_modes; i++) {
   694         SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
   695 
   696         if (modedata) {
   697           modedata->mode_index = i;
   698         }
   699 
   700         mode.w = conn->modes[i].hdisplay;
   701         mode.h = conn->modes[i].vdisplay;
   702         mode.refresh_rate = conn->modes[i].vrefresh;
   703         mode.format = SDL_PIXELFORMAT_ARGB8888;
   704         mode.driverdata = modedata;
   705 
   706         if (!SDL_AddDisplayMode(display, &mode)) {
   707             SDL_free(modedata);
   708         }
   709     }
   710 }
   711 
   712 int
   713 KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   714 {
   715     SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   716     SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
   717     SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   718     drmModeConnector *conn = dispdata->conn;
   719 
   720     if (!modedata) {
   721         return SDL_SetError("Mode doesn't have an associated index");
   722     }
   723 
   724     dispdata->mode = conn->modes[modedata->mode_index];
   725 
   726     for (int i = 0; i < viddata->num_windows; i++) {
   727         SDL_Window *window = viddata->windows[i];
   728         SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   729 
   730 #if SDL_VIDEO_OPENGL_EGL
   731         /* Can't recreate EGL surfaces right now, need to wait until SwapWindow
   732            so the correct thread-local surface and context state are available */
   733         windata->egl_surface_dirty = 1;
   734 #else
   735         if (KMSDRM_CreateSurfaces(_this, window)) {
   736             return -1;
   737         }
   738 #endif
   739 
   740         /* Tell app about the resize */
   741         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
   742     }
   743 
   744     return 0;
   745 }
   746 
   747 int
   748 KMSDRM_CreateWindow(_THIS, SDL_Window * window)
   749 {
   750     SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   751     SDL_WindowData *windata;
   752     SDL_VideoDisplay *display;
   753 
   754 #if SDL_VIDEO_OPENGL_EGL
   755     if (!_this->egl_data) {
   756         if (SDL_GL_LoadLibrary(NULL) < 0) {
   757             goto error;
   758         }
   759     }
   760 #endif
   761 
   762     /* Allocate window internal data */
   763     windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
   764 
   765     if (!windata) {
   766         SDL_OutOfMemory();
   767         goto error;
   768     }
   769 
   770     /* Windows have one size for now */
   771     display = SDL_GetDisplayForWindow(window);
   772     window->w = display->desktop_mode.w;
   773     window->h = display->desktop_mode.h;
   774 
   775     /* Maybe you didn't ask for a fullscreen OpenGL window, but that's what you get */
   776     window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
   777 
   778     /* In case we want low-latency, double-buffer video, we take note here */
   779     windata->double_buffer = SDL_FALSE;
   780 
   781     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
   782         windata->double_buffer = SDL_TRUE;
   783     }
   784 
   785     /* Setup driver data for this window */
   786     window->driverdata = windata;
   787 
   788     if (KMSDRM_CreateSurfaces(_this, window)) {
   789       goto error;
   790     }
   791 
   792     /* Add window to the internal list of tracked windows. Note, while it may
   793        seem odd to support multiple fullscreen windows, some apps create an
   794        extra window as a dummy surface when working with multiple contexts */
   795     windata->viddata = viddata;
   796 
   797     if (viddata->num_windows >= viddata->max_windows) {
   798         int new_max_windows = viddata->max_windows + 1;
   799         viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
   800               new_max_windows * sizeof(SDL_Window *));
   801         viddata->max_windows = new_max_windows;
   802 
   803         if (!viddata->windows) {
   804             SDL_OutOfMemory();
   805             goto error;
   806         }
   807     }
   808 
   809     viddata->windows[viddata->num_windows++] = window;
   810 
   811     return 0;
   812 
   813 error:
   814     KMSDRM_DestroyWindow(_this, window);
   815 
   816     return -1;
   817 }
   818 
   819 void
   820 KMSDRM_DestroyWindow(_THIS, SDL_Window * window)
   821 {
   822     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   823     SDL_VideoData *viddata;
   824     if (!windata) {
   825         return;
   826     }
   827 
   828     /* Remove from the internal window list */
   829     viddata = windata->viddata;
   830 
   831     for (int i = 0; i < viddata->num_windows; i++) {
   832         if (viddata->windows[i] == window) {
   833             viddata->num_windows--;
   834 
   835             for (int j = i; j < viddata->num_windows; j++) {
   836                 viddata->windows[j] = viddata->windows[j + 1];
   837             }
   838 
   839             break;
   840         }
   841     }
   842 
   843     KMSDRM_DestroySurfaces(_this, window);
   844 
   845     window->driverdata = NULL;
   846 
   847     SDL_free(windata);
   848 }
   849 
   850 int
   851 KMSDRM_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   852 {
   853     return -1;
   854 }
   855 
   856 void
   857 KMSDRM_SetWindowTitle(_THIS, SDL_Window * window)
   858 {
   859 }
   860 void
   861 KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   862 {
   863 }
   864 void
   865 KMSDRM_SetWindowPosition(_THIS, SDL_Window * window)
   866 {
   867 }
   868 void
   869 KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
   870 {
   871 }
   872 void
   873 KMSDRM_ShowWindow(_THIS, SDL_Window * window)
   874 {
   875 }
   876 void
   877 KMSDRM_HideWindow(_THIS, SDL_Window * window)
   878 {
   879 }
   880 void
   881 KMSDRM_RaiseWindow(_THIS, SDL_Window * window)
   882 {
   883 }
   884 void
   885 KMSDRM_MaximizeWindow(_THIS, SDL_Window * window)
   886 {
   887 }
   888 void
   889 KMSDRM_MinimizeWindow(_THIS, SDL_Window * window)
   890 {
   891 }
   892 void
   893 KMSDRM_RestoreWindow(_THIS, SDL_Window * window)
   894 {
   895 }
   896 void
   897 KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
   898 {
   899 
   900 }
   901 
   902 /*****************************************************************************/
   903 /* SDL Window Manager function                                               */
   904 /*****************************************************************************/
   905 SDL_bool
   906 KMSDRM_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
   907 {
   908     if (info->version.major <= SDL_MAJOR_VERSION) {
   909         return SDL_TRUE;
   910     } else {
   911         SDL_SetError("application not compiled with SDL %d.%d\n",
   912                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   913         return SDL_FALSE;
   914     }
   915 
   916     /* Failed to get window manager information */
   917     return SDL_FALSE;
   918 }
   919 
   920 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
   921 
   922 /* vi: set ts=4 sw=4 expandtab: */