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