sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_kmsdrmvideo.c (63394B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4   Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
      5 
      6   This software is provided 'as-is', without any express or implied
      7   warranty.  In no event will the authors be held liable for any damages
      8   arising from the use of this software.
      9 
     10   Permission is granted to anyone to use this software for any purpose,
     11   including commercial applications, and to alter it and redistribute it
     12   freely, subject to the following restrictions:
     13 
     14   1. The origin of this software must not be misrepresented; you must not
     15      claim that you wrote the original software. If you use this software
     16      in a product, an acknowledgment in the product documentation would be
     17      appreciated but is not required.
     18   2. Altered source versions must be plainly marked as such, and must not be
     19      misrepresented as being the original software.
     20   3. This notice may not be removed or altered from any source distribution.
     21 */
     22 
     23 #include "../../SDL_internal.h"
     24 
     25 #if SDL_VIDEO_DRIVER_KMSDRM
     26 
     27 /* SDL internals */
     28 #include "../SDL_sysvideo.h"
     29 #include "SDL_syswm.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 "SDL_kmsdrmvulkan.h"
     45 #include <sys/stat.h>
     46 #include <dirent.h>
     47 #include <errno.h>
     48 #include <poll.h>
     49 
     50 /* for older KMSDRM headers... */
     51 #ifndef DRM_FORMAT_MOD_VENDOR_NONE
     52 #define DRM_FORMAT_MOD_VENDOR_NONE 0
     53 #endif
     54 #ifndef DRM_FORMAT_MOD_LINEAR
     55 #define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
     56 #endif
     57 
     58 #define KMSDRM_DRI_PATH "/dev/dri/"
     59 
     60 static int set_client_caps (int fd)
     61 {
     62     if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
     63          return SDL_SetError("no atomic modesetting support.");
     64     }
     65     if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
     66          return SDL_SetError("no universal planes support.");
     67     }
     68     return 0;
     69 }
     70 
     71 static int
     72 check_modesetting(int devindex)
     73 {
     74     SDL_bool available = SDL_FALSE;
     75     char device[512];
     76     unsigned int i;
     77     int drm_fd;
     78 
     79     SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
     80     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "check_modesetting: probing \"%s\"", device);
     81 
     82     drm_fd = open(device, O_RDWR | O_CLOEXEC);
     83     if (drm_fd >= 0) {
     84         if (SDL_KMSDRM_LoadSymbols()) {
     85             drmModeRes *resources = (set_client_caps(drm_fd) < 0) ? NULL : KMSDRM_drmModeGetResources(drm_fd);
     86             if (resources) {
     87                 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
     88                              KMSDRM_DRI_PATH, devindex,
     89                              resources->count_connectors, resources->count_encoders, resources->count_crtcs);
     90 
     91                 if (resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) {
     92                     for (i = 0; i < resources->count_connectors; i++) {
     93                         drmModeConnector *conn = KMSDRM_drmModeGetConnector(drm_fd, resources->connectors[i]);
     94 
     95                         if (!conn) {
     96                             continue;
     97                         }
     98 
     99                         if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
    100                             available = SDL_TRUE;
    101                         }
    102 
    103                         KMSDRM_drmModeFreeConnector(conn);
    104                         if (available) {
    105                             break;
    106                         }
    107                     }
    108                 }
    109                 KMSDRM_drmModeFreeResources(resources);
    110             }
    111             SDL_KMSDRM_UnloadSymbols();
    112         }
    113         close(drm_fd);
    114     }
    115 
    116     return available;
    117 }
    118 
    119 static unsigned int get_dricount(void)
    120 {
    121     unsigned int devcount = 0;
    122     struct dirent *res;
    123     struct stat sb;
    124     DIR *folder;
    125 
    126     if (!(stat(KMSDRM_DRI_PATH, &sb) == 0 && S_ISDIR(sb.st_mode))) {
    127         SDL_SetError("The path %s cannot be opened or is not available",
    128         KMSDRM_DRI_PATH);
    129         return 0;
    130     }
    131 
    132     if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
    133         SDL_SetError("The path %s cannot be opened",
    134             KMSDRM_DRI_PATH);
    135         return 0;
    136     }
    137 
    138     folder = opendir(KMSDRM_DRI_PATH);
    139     if (folder) {
    140         while ((res = readdir(folder))) {
    141             size_t len = SDL_strlen(res->d_name);
    142             if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
    143                 devcount++;
    144             }
    145         }
    146         closedir(folder);
    147     }
    148 
    149     return devcount;
    150 }
    151 
    152 static int
    153 get_driindex(void)
    154 {
    155     const unsigned int devcount = get_dricount();
    156     unsigned int i;
    157 
    158     for (i = 0; i < devcount; i++) {
    159         if (check_modesetting(i)) {
    160             return i;
    161         }
    162     }
    163 
    164     return -ENOENT;
    165 }
    166 
    167 #if 0
    168 
    169 /**********************/
    170 /* DUMB BUFFER Block. */
    171 /**********************/
    172 
    173 /* Create a dumb buffer, mmap the dumb buffer and fill it with pixels, */
    174 /* then create a KMS framebuffer wrapping the dumb buffer.             */
    175 static dumb_buffer *KMSDRM_CreateDumbBuffer(_THIS)
    176 {
    177     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    178     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    179 
    180     struct drm_mode_create_dumb create;
    181     struct drm_mode_map_dumb map;
    182     struct drm_mode_destroy_dumb destroy;
    183 
    184     dumb_buffer *ret = SDL_calloc(1, sizeof(*ret));
    185     if (!ret) {
    186         SDL_OutOfMemory();
    187         return NULL;
    188     }
    189 
    190     /*
    191      * The create ioctl uses the combination of depth and bpp to infer
    192      * a format; 24/32 refers to DRM_FORMAT_XRGB8888 as defined in
    193      * the drm_fourcc.h header. These arguments are the same as given
    194      * to drmModeAddFB, which has since been superseded by
    195      * drmModeAddFB2 as the latter takes an explicit format token.
    196      *
    197      * We only specify these arguments; the driver calculates the
    198      * pitch (also known as stride or row length) and total buffer size
    199      * for us, also returning us the GEM handle.
    200      */
    201     create = (struct drm_mode_create_dumb) {
    202         .width = dispdata->mode.hdisplay,
    203         .height = dispdata->mode.vdisplay,
    204         .bpp = 32,
    205     };
    206 
    207     if (KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)) {
    208         SDL_SetError("failed to create dumb buffer\n");
    209         goto err;
    210     }
    211 
    212     ret->gem_handles[0] = create.handle;
    213     ret->format = DRM_FORMAT_XRGB8888;
    214     ret->modifier = DRM_FORMAT_MOD_LINEAR;
    215     ret->width = create.width;
    216     ret->height = create.height;
    217     ret->pitches[0] = create.pitch;
    218 
    219     /*
    220      * In order to map the buffer, we call an ioctl specific to the buffer
    221      * type, which returns us a fake offset to use with the mmap syscall.
    222      * mmap itself then works as you expect.
    223      *
    224      * Note this means it is not possible to map arbitrary offsets of
    225      * buffers without specifically requesting it from the kernel.
    226      */
    227     map = (struct drm_mode_map_dumb) {
    228         .handle = ret->gem_handles[0],
    229     };
    230 
    231     if (KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map)) {
    232         SDL_SetError("failed to get mmap offset for the dumb buffer.");
    233         goto err_dumb;
    234     }
    235 
    236     ret->dumb.mem = mmap(NULL, create.size, PROT_WRITE, MAP_SHARED,
    237                          viddata->drm_fd, map.offset);
    238 
    239     if (ret->dumb.mem == MAP_FAILED) {
    240         SDL_SetError("failed to get mmap offset for the dumb buffer.");
    241         goto err_dumb;
    242     }
    243     ret->dumb.size = create.size;
    244 
    245     return ret;
    246 
    247 err_dumb:
    248     destroy = (struct drm_mode_destroy_dumb) { .handle = create.handle };
    249     KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
    250 err:
    251     SDL_free(ret);
    252     return NULL;
    253 }
    254 
    255 static void
    256 KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer **buffer)
    257 {
    258     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    259 
    260     struct drm_mode_destroy_dumb destroy = {
    261         .handle = (*buffer)->gem_handles[0],
    262     };
    263 
    264     KMSDRM_drmModeRmFB(viddata->drm_fd, (*buffer)->fb_id);
    265 
    266     munmap((*buffer)->dumb.mem, (*buffer)->dumb.size);
    267     KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
    268     free(*buffer);
    269     *buffer = NULL;
    270 }
    271 
    272 /* Using the CPU mapping, fill the dumb buffer with black pixels. */
    273 static void
    274 KMSDRM_FillDumbBuffer(dumb_buffer *buffer)
    275 {
    276     unsigned int x, y;
    277     for (y = 0; y < buffer->height; y++) {
    278     uint32_t *pix = (uint32_t *) ((uint8_t *) buffer->dumb.mem + (y * buffer->pitches[0]));
    279         for (x = 0; x < buffer->width; x++) {
    280             *pix++ = (0x00000000);
    281         }
    282     }
    283 }
    284 
    285 static dumb_buffer *KMSDRM_CreateBuffer(_THIS)
    286 {
    287     dumb_buffer *ret;
    288     int err;
    289 
    290     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    291 
    292     ret = KMSDRM_CreateDumbBuffer(_this);
    293 
    294     if (!ret)
    295         return NULL;
    296 
    297     /*
    298      * Wrap our GEM buffer in a KMS framebuffer, so we can then attach it
    299      * to a plane. Here's where we get out fb_id!
    300      */
    301     err = KMSDRM_drmModeAddFB2(viddata->drm_fd, ret->width, ret->height,
    302         ret->format, ret->gem_handles, ret->pitches,
    303         ret->offsets, &ret->fb_id, 0);
    304 
    305     if (err != 0 || ret->fb_id == 0) {
    306         SDL_SetError("Failed AddFB2 on dumb buffer\n");
    307         goto err;
    308     }
    309     return ret;
    310 
    311 err:
    312     KMSDRM_DestroyDumbBuffer(_this, &ret);
    313     return NULL;
    314 }
    315 
    316 /***************************/
    317 /* DUMB BUFFER Block ends. */
    318 /***************************/
    319 
    320 #endif
    321 
    322 /*********************************/
    323 /* Atomic helper functions block */
    324 /*********************************/
    325 
    326 #define VOID2U64(x) ((uint64_t)(unsigned long)(x))
    327 
    328 int add_connector_property(drmModeAtomicReq *req, struct connector *connector,
    329                                      const char *name, uint64_t value)
    330 {
    331     unsigned int i;
    332     int prop_id = 0;
    333 
    334     for (i = 0 ; i < connector->props->count_props ; i++) {
    335         if (strcmp(connector->props_info[i]->name, name) == 0) {
    336             prop_id = connector->props_info[i]->prop_id;
    337             break;
    338         }
    339     }
    340 
    341     if (prop_id < 0) {
    342         SDL_SetError("no connector property: %s", name);
    343         return -EINVAL;
    344     }
    345 
    346     return KMSDRM_drmModeAtomicAddProperty(req, connector->connector->connector_id, prop_id, value);
    347 }
    348 
    349 int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc,
    350                          const char *name, uint64_t value)
    351 {
    352     unsigned int i;
    353     int prop_id = -1;
    354 
    355     for (i = 0 ; i < crtc->props->count_props ; i++) {
    356         if (strcmp(crtc->props_info[i]->name, name) == 0) {
    357             prop_id = crtc->props_info[i]->prop_id;
    358             break;
    359         }
    360     }
    361 
    362     if (prop_id < 0) {
    363         SDL_SetError("no crtc property: %s", name);
    364         return -EINVAL;
    365     }
    366 
    367     return KMSDRM_drmModeAtomicAddProperty(req, crtc->crtc->crtc_id, prop_id, value);
    368 }
    369 
    370 int add_plane_property(drmModeAtomicReq *req, struct plane *plane,
    371                           const char *name, uint64_t value)
    372 {
    373     unsigned int i;
    374     int prop_id = -1;
    375 
    376     for (i = 0 ; i < plane->props->count_props ; i++) {
    377         if (strcmp(plane->props_info[i]->name, name) == 0) {
    378             prop_id = plane->props_info[i]->prop_id;
    379             break;
    380         }
    381     }
    382 
    383     if (prop_id < 0) {
    384         SDL_SetError("no plane property: %s", name);
    385         return -EINVAL;
    386     }
    387 
    388     return KMSDRM_drmModeAtomicAddProperty(req, plane->plane->plane_id, prop_id, value);
    389 }
    390 
    391 #if 0
    392 
    393 void print_plane_info(_THIS, drmModePlanePtr plane)
    394 {
    395     char *plane_type;
    396     drmModeRes *resources;
    397     uint32_t type = 0;
    398     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    399     int i;
    400 
    401     drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
    402         plane->plane_id, DRM_MODE_OBJECT_PLANE);
    403 
    404     /* Search the plane props for the plane type. */
    405     for (i = 0; i < props->count_props; i++) {
    406         drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[i]);
    407         if ((strcmp(p->name, "type") == 0)) {
    408             type = props->prop_values[i];
    409         }
    410 
    411         KMSDRM_drmModeFreeProperty(p);
    412     }
    413 
    414     switch (type) {
    415         case DRM_PLANE_TYPE_OVERLAY:
    416             plane_type = "overlay";
    417             break;
    418 
    419         case DRM_PLANE_TYPE_PRIMARY:
    420             plane_type = "primary";
    421             break;
    422 
    423         case DRM_PLANE_TYPE_CURSOR:
    424             plane_type = "cursor";
    425             break;
    426     }
    427 
    428 
    429     /* Remember that to present a plane on screen, it has to be
    430        connected to a CRTC so the CRTC scans it,
    431        scales it, etc... and presents it on screen. */
    432 
    433     /* Now we look for the CRTCs supported by the plane. */
    434     resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
    435     if (!resources)
    436         return;
    437 
    438     printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ",  plane->plane_id, plane_type, plane->crtc_id);
    439     for (i = 0; i < resources->count_crtcs; i++) {
    440         if (plane->possible_crtcs & (1 << i)) {
    441             uint32_t crtc_id = resources->crtcs[i];
    442             printf ("%d", crtc_id);
    443             break;
    444         }
    445     }
    446 
    447     printf ("\n\n");
    448 }
    449 
    450 void get_planes_info(_THIS)
    451 {
    452     drmModePlaneResPtr plane_resources;
    453     uint32_t i;
    454 
    455     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    456     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    457 
    458     plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
    459     if (!plane_resources) {
    460         printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
    461         return;
    462     }
    463 
    464     printf("--Number of planes found: %d-- \n", plane_resources->count_planes);
    465     printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc->crtc->crtc_id);
    466 
    467     /* Iterate on all the available planes. */
    468     for (i = 0; (i < plane_resources->count_planes); i++) {
    469 
    470         uint32_t plane_id = plane_resources->planes[i];
    471 
    472         drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
    473         if (!plane) {
    474             printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
    475             continue;
    476         }
    477 
    478         /* Print plane info. */
    479         print_plane_info(_this, plane);
    480         KMSDRM_drmModeFreePlane(plane);
    481     }
    482 
    483     KMSDRM_drmModeFreePlaneResources(plane_resources);
    484 }
    485 
    486 #endif
    487 
    488 /* Get the plane_id of a plane that is of the specified plane type (primary,
    489    overlay, cursor...) and can use the CRTC we have chosen previously. */
    490 static int get_plane_id(_THIS, uint32_t plane_type)
    491 {
    492     drmModeRes *resources = NULL;
    493     drmModePlaneResPtr plane_resources = NULL;
    494     uint32_t i, j;
    495     unsigned int crtc_index = 0;
    496     int ret = -EINVAL;
    497     int found = 0;
    498 
    499     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    500     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    501 
    502     resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
    503 
    504     /* Get the crtc_index for the current CRTC.
    505        It's needed to find out if a plane supports the CRTC. */
    506     for (i = 0; i < resources->count_crtcs; i++) {
    507         if (resources->crtcs[i] == dispdata->crtc->crtc->crtc_id) {
    508             crtc_index = i;
    509             break;
    510         }
    511     }
    512 
    513     plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
    514     if (!plane_resources) {
    515         return SDL_SetError("drmModeGetPlaneResources failed.");
    516     }
    517 
    518     /* Iterate on all the available planes. */
    519     for (i = 0; (i < plane_resources->count_planes) && !found; i++) {
    520 
    521         uint32_t plane_id = plane_resources->planes[i];
    522 
    523         drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
    524         if (!plane) {
    525             continue;
    526         }
    527 
    528         /* See if the current CRTC is available for this plane. */
    529         if (plane->possible_crtcs & (1 << crtc_index)) {
    530 
    531             drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(
    532                 viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
    533             ret = plane_id;
    534 
    535             /* Iterate on the plane props to find the type of the plane,
    536                to see if it's of the type we want. */
    537             for (j = 0; j < props->count_props; j++) {
    538 
    539                 drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd,
    540                     props->props[j]);
    541 
    542                 if ((strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) {
    543                     /* found our plane, use that: */
    544                     found = 1;
    545                 }
    546 
    547                 KMSDRM_drmModeFreeProperty(p);
    548             }
    549 
    550             KMSDRM_drmModeFreeObjectProperties(props);
    551         }
    552 
    553         KMSDRM_drmModeFreePlane(plane);
    554     }
    555 
    556     KMSDRM_drmModeFreePlaneResources(plane_resources);
    557     KMSDRM_drmModeFreeResources(resources);
    558 
    559     return ret;
    560 }
    561 
    562 /* Setup a plane and it's props. */
    563 int
    564 setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
    565 {
    566     uint32_t plane_id;
    567     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    568     int ret = 0;
    569 
    570     *plane = SDL_calloc(1, sizeof(**plane));
    571     if (!(*plane)) {
    572         ret = SDL_OutOfMemory();
    573         goto cleanup;
    574     }
    575 
    576     /* Get plane ID. */
    577     plane_id = get_plane_id(_this, plane_type);
    578 
    579     if (!plane_id) {
    580         ret = SDL_SetError("Invalid Plane ID");
    581         goto cleanup;
    582     }
    583 
    584     /* Get the DRM plane itself. */
    585     (*plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
    586 
    587     /* Get the DRM plane properties. */
    588     if ((*plane)->plane) {
    589         unsigned int i;
    590         (*plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
    591         (*plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE);
    592         (*plane)->props_info = SDL_calloc((*plane)->props->count_props,
    593             sizeof(*(*plane)->props_info));
    594 
    595         if ( !((*plane)->props_info) ) {
    596             ret = SDL_OutOfMemory();
    597             goto cleanup;
    598         }
    599 
    600         for (i = 0; i < (*plane)->props->count_props; i++) {
    601             (*plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
    602                 (*plane)->props->props[i]);
    603         }
    604     }
    605 
    606 cleanup:
    607 
    608     if (ret) {
    609         if (*plane) {
    610             SDL_free(*plane);
    611             *plane = NULL;
    612         }
    613     }
    614     return ret;
    615 }
    616 
    617 /* Free a plane and it's props. */
    618 void
    619 free_plane(struct plane **plane)
    620 {
    621     if (*plane) {
    622         if ((*plane)->plane) {
    623             KMSDRM_drmModeFreePlane((*plane)->plane);
    624             (*plane)->plane = NULL;
    625         }
    626         if ((*plane)->props_info) {
    627             SDL_free((*plane)->props_info);
    628             (*plane)->props_info = NULL;
    629         }
    630         SDL_free(*plane);
    631         *plane = NULL;
    632     }
    633 }
    634 
    635 /**********************************************************************************/
    636 /* The most important ATOMIC fn of the backend.                                   */
    637 /* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents       */
    638 /*   over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected     */
    639 /*   to the same PLANE).                                                          */
    640 /*   Think of a plane as a "frame" sorrounding a picture, where the "picture"     */
    641 /*   is the buffer, and we move the "frame" from  a picture to another,           */
    642 /*   and the one that has the "frame" is the one sent over to the screen          */
    643 /*   via the CONNECTOR->ENCODER system.                                           */
    644 /*   Think of a PLANE as being "in the middle", it's the CENTRAL part             */
    645 /*   bewteen the CRTC and the BUFFER that is shown on screen.                     */
    646 /*   What we do here is connect a PLANE to a CRTC and a BUFFER.                   */
    647 /*   -ALWAYS set the CRTC_ID and FB_ID attribs of a plane at the same time,       */
    648 /*   meaning IN THE SAME atomic request.                                          */
    649 /*   -And NEVER destroy a GBM surface whose buffers are being read by a plane:    */
    650 /*   first, move the plane away from those buffers and ONLY THEN destroy the      */
    651 /*   buffers and/or the GBM surface containig them.                               */
    652 /**********************************************************************************/
    653 void
    654 drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info)
    655 {
    656     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    657 
    658     /* Do we have a set of changes already in the making? If not, allocate a new one. */
    659     if (!dispdata->atomic_req)
    660         dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
    661 
    662     add_plane_property(dispdata->atomic_req, info->plane, "FB_ID", info->fb_id);
    663     add_plane_property(dispdata->atomic_req, info->plane, "CRTC_ID", info->crtc_id);
    664     add_plane_property(dispdata->atomic_req, info->plane, "SRC_W", info->src_w << 16);
    665     add_plane_property(dispdata->atomic_req, info->plane, "SRC_H", info->src_h << 16);
    666     add_plane_property(dispdata->atomic_req, info->plane, "SRC_X", info->src_x);
    667     add_plane_property(dispdata->atomic_req, info->plane, "SRC_Y", info->src_y);
    668     add_plane_property(dispdata->atomic_req, info->plane, "CRTC_W", info->crtc_w);
    669     add_plane_property(dispdata->atomic_req, info->plane, "CRTC_H", info->crtc_h);
    670     add_plane_property(dispdata->atomic_req, info->plane, "CRTC_X", info->crtc_x);
    671     add_plane_property(dispdata->atomic_req, info->plane, "CRTC_Y", info->crtc_y);
    672 }
    673 
    674 int drm_atomic_commit(_THIS, SDL_bool blocking)
    675 {
    676     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    677     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    678     int ret;
    679 
    680     if (!blocking)
    681         dispdata->atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
    682 
    683     /* Never issue a new atomic commit if previous has not yet completed, or it will error. */
    684     drm_atomic_waitpending(_this);
    685 
    686     ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, dispdata->atomic_flags, NULL);
    687     if (ret) {
    688         SDL_SetError("Atomic commit failed, returned %d.", ret);
    689         /* Uncomment this for fast-debugging */
    690 #if 0
    691         printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno));
    692 #endif
    693         goto out;
    694     }
    695 
    696     if (dispdata->kms_in_fence_fd != -1) {
    697         close(dispdata->kms_in_fence_fd);
    698         dispdata->kms_in_fence_fd = -1;
    699     }
    700 
    701 out:
    702     KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
    703     dispdata->atomic_req = NULL;
    704     dispdata->atomic_flags = 0;
    705 
    706     return ret;
    707 }
    708 
    709 void
    710 drm_atomic_waitpending(_THIS)
    711 {
    712     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
    713 
    714     /* Will return immediately if we have already destroyed the fence, because we NULL-ify it just after.
    715        Also, will return immediately in double-buffer mode, because kms_fence will alsawys be NULL. */
    716     if (dispdata->kms_fence) {
    717         EGLint status;
    718 
    719         do {
    720             status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
    721                 dispdata->kms_fence, 0, EGL_FOREVER_KHR);
    722         } while (status != EGL_CONDITION_SATISFIED_KHR);
    723 
    724         _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
    725             dispdata->kms_fence = NULL;
    726     }
    727 }
    728 
    729 /***************************************/
    730 /* End of Atomic helper functions block*/
    731 /***************************************/
    732 
    733 static int
    734 KMSDRM_Available(void)
    735 {
    736     int ret = -ENOENT;
    737 
    738     ret = get_driindex();
    739     if (ret >= 0)
    740         return 1;
    741 
    742     return ret;
    743 }
    744 
    745 static void
    746 KMSDRM_DeleteDevice(SDL_VideoDevice * device)
    747 {
    748     if (device->driverdata) {
    749         SDL_free(device->driverdata);
    750         device->driverdata = NULL;
    751     }
    752 
    753     SDL_free(device);
    754 
    755     SDL_KMSDRM_UnloadSymbols();
    756 }
    757 
    758 static int
    759 KMSDRM_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
    760 {
    761     int w, h;
    762 
    763     uint32_t display_mm_width;
    764     uint32_t display_mm_height;
    765 
    766     SDL_DisplayData *dispdata;
    767 
    768     dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); //viddata->devindex);
    769 
    770 
    771     if (!dispdata) {
    772         return SDL_SetError("No available displays");
    773     }
    774 
    775     display_mm_width = dispdata->connector->connector->mmWidth;
    776     display_mm_height = dispdata->connector->connector->mmHeight;
    777 
    778     w = dispdata->mode.hdisplay;
    779     h = dispdata->mode.vdisplay;
    780 
    781     *hdpi = display_mm_width ? (((float) w) * 25.4f / display_mm_width) : 0.0f;
    782     *vdpi = display_mm_height ? (((float) h) * 25.4f / display_mm_height) : 0.0f;
    783     *ddpi = SDL_ComputeDiagonalDPI(w, h, ((float) display_mm_width) / 25.4f,((float) display_mm_height) / 25.4f);
    784 
    785     return 0;
    786 }
    787 
    788 static SDL_VideoDevice *
    789 KMSDRM_CreateDevice(int devindex)
    790 {
    791     SDL_VideoDevice *device;
    792     SDL_VideoData *viddata;
    793 
    794     if (!KMSDRM_Available()) {
    795         return NULL;
    796     }
    797 
    798     if (!devindex || (devindex > 99)) {
    799         devindex = get_driindex();
    800     }
    801 
    802     if (devindex < 0) {
    803         SDL_SetError("devindex (%d) must be between 0 and 99.", devindex);
    804         return NULL;
    805     }
    806 
    807     if (!SDL_KMSDRM_LoadSymbols()) {
    808         return NULL;
    809     }
    810 
    811     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    812     if (!device) {
    813         SDL_OutOfMemory();
    814         return NULL;
    815     }
    816 
    817     viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
    818     if (!viddata) {
    819         SDL_OutOfMemory();
    820         goto cleanup;
    821     }
    822     viddata->devindex = devindex;
    823     viddata->drm_fd = -1;
    824 
    825     device->driverdata = viddata;
    826 
    827     /* Setup all functions that can be handled from this backend. */
    828     device->VideoInit = KMSDRM_VideoInit;
    829     device->VideoQuit = KMSDRM_VideoQuit;
    830     device->GetDisplayModes = KMSDRM_GetDisplayModes;
    831     device->SetDisplayMode = KMSDRM_SetDisplayMode;
    832     device->GetDisplayDPI = KMSDRM_GetDisplayDPI;
    833     device->CreateSDLWindow = KMSDRM_CreateWindow;
    834     device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
    835     device->SetWindowTitle = KMSDRM_SetWindowTitle;
    836     device->SetWindowIcon = KMSDRM_SetWindowIcon;
    837     device->SetWindowPosition = KMSDRM_SetWindowPosition;
    838     device->SetWindowSize = KMSDRM_SetWindowSize;
    839     device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen;
    840     device->ShowWindow = KMSDRM_ShowWindow;
    841     device->HideWindow = KMSDRM_HideWindow;
    842     device->RaiseWindow = KMSDRM_RaiseWindow;
    843     device->MaximizeWindow = KMSDRM_MaximizeWindow;
    844     device->MinimizeWindow = KMSDRM_MinimizeWindow;
    845     device->RestoreWindow = KMSDRM_RestoreWindow;
    846     device->SetWindowGrab = KMSDRM_SetWindowGrab;
    847     device->DestroyWindow = KMSDRM_DestroyWindow;
    848     device->GetWindowWMInfo = KMSDRM_GetWindowWMInfo;
    849 #if SDL_VIDEO_OPENGL_EGL
    850     device->GL_DefaultProfileConfig = KMSDRM_GLES_DefaultProfileConfig;
    851     device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
    852     device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
    853     device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
    854     device->GL_CreateContext = KMSDRM_GLES_CreateContext;
    855     device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
    856     device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
    857     device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
    858     device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
    859     device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
    860 #endif
    861     device->PumpEvents = KMSDRM_PumpEvents;
    862     device->free = KMSDRM_DeleteDevice;
    863 #if SDL_VIDEO_VULKAN
    864     device->Vulkan_LoadLibrary = KMSDRM_Vulkan_LoadLibrary;
    865     device->Vulkan_UnloadLibrary = KMSDRM_Vulkan_UnloadLibrary;
    866     device->Vulkan_GetInstanceExtensions = KMSDRM_Vulkan_GetInstanceExtensions;
    867     device->Vulkan_CreateSurface = KMSDRM_Vulkan_CreateSurface;
    868     device->Vulkan_GetDrawableSize = KMSDRM_Vulkan_GetDrawableSize;
    869 #endif
    870     return device;
    871 
    872 cleanup:
    873     if (device)
    874         SDL_free(device);
    875     if (viddata)
    876         SDL_free(viddata);
    877     return NULL;
    878 }
    879 
    880 VideoBootStrap KMSDRM_bootstrap = {
    881     "KMSDRM",
    882     "KMS/DRM Video Driver",
    883     KMSDRM_CreateDevice
    884 };
    885 
    886 
    887 static void
    888 KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
    889 {
    890     KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
    891 
    892     if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
    893         KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
    894         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
    895     }
    896 
    897     SDL_free(fb_info);
    898 }
    899 
    900 KMSDRM_FBInfo *
    901 KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
    902 {
    903     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    904     unsigned width, height;
    905     uint32_t format, strides[4] = {0}, handles[4] = {0}, offsets[4] = {0};
    906     const int num_planes = KMSDRM_gbm_bo_get_plane_count(bo);
    907     unsigned int i;
    908     int ret;
    909 
    910     /* Check for an existing framebuffer */
    911     KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
    912 
    913     if (fb_info) {
    914         return fb_info;
    915     }
    916 
    917     /* Create a structure that contains the info about framebuffer
    918        that we need to use it. */
    919     fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
    920     if (!fb_info) {
    921         SDL_OutOfMemory();
    922         return NULL;
    923     }
    924 
    925     fb_info->drm_fd = viddata->drm_fd;
    926 
    927     width = KMSDRM_gbm_bo_get_width(bo);
    928     height = KMSDRM_gbm_bo_get_height(bo);
    929     format = KMSDRM_gbm_bo_get_format(bo);
    930 
    931     for (i = 0; i < num_planes; i++) {
    932         strides[i] = KMSDRM_gbm_bo_get_stride_for_plane(bo, i);
    933         handles[i] = KMSDRM_gbm_bo_get_handle(bo).u32;
    934         offsets[i] = KMSDRM_gbm_bo_get_offset(bo, i);
    935     }
    936 
    937     /* Create framebuffer object for the buffer.
    938        It's VERY important to note that fb_id is what we use to set the FB_ID prop
    939        of a plane when using the ATOMIC interface, and we get the fb_id here. */
    940     ret = KMSDRM_drmModeAddFB2(viddata->drm_fd, width, height, format,
    941             handles, strides, offsets, &fb_info->fb_id, 0);
    942 
    943     if (ret) {
    944       SDL_free(fb_info);
    945       return NULL;
    946     }
    947 
    948     /* Set the userdata pointer. This pointer is used to store custom data that we need
    949        to access in the future, so we store the fb_id here for later use, because fb_id is
    950        what we need to set the FB_ID property of a plane when using the ATOMIC interface. */
    951     KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
    952 
    953     return fb_info;
    954 }
    955 
    956 /*****************************************************************************/
    957 /* SDL Video and Display initialization/handling functions                   */
    958 /* _this is a SDL_VideoDevice *                                              */
    959 /*****************************************************************************/
    960 
    961 /* Deinitializes the dispdata members needed for KMSDRM operation that are
    962    inoffeensive for VK compatibility. */
    963 void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
    964     /* Free connector */
    965     if (dispdata && dispdata->connector) {
    966         if (dispdata->connector->connector) {
    967             KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
    968             dispdata->connector->connector = NULL;
    969         }
    970         if (dispdata->connector->props_info) {
    971             SDL_free(dispdata->connector->props_info);
    972             dispdata->connector->props_info = NULL;
    973         }
    974         SDL_free(dispdata->connector);
    975         dispdata->connector = NULL;
    976     }
    977 
    978     /* Free CRTC */
    979     if (dispdata && dispdata->crtc) {
    980         if (dispdata->crtc->crtc) {
    981             KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
    982             dispdata->crtc->crtc = NULL;
    983         }
    984         if (dispdata->crtc->props_info) {
    985             SDL_free(dispdata->crtc->props_info);
    986             dispdata->crtc->props_info = NULL;
    987         }
    988         SDL_free(dispdata->crtc);
    989         dispdata->crtc = NULL;
    990     }
    991 }
    992 
    993 /* Initializes the dispdata members needed for KMSDRM operation that are
    994    inoffeensive for VK compatibility, except we must leave the drm_fd
    995    closed when we get to the end of this function.
    996    This is to be called early, in VideoInit(), because it gets us
    997    the videomode information, which SDL needs immediately after VideoInit(). */
    998 int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
    999     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   1000 
   1001     drmModeRes *resources = NULL;
   1002     drmModeEncoder *encoder = NULL;
   1003     drmModeConnector *connector = NULL;
   1004     drmModeCrtc *crtc = NULL;
   1005 
   1006     char devname[32];
   1007     int ret = 0;
   1008     unsigned i,j;
   1009 
   1010     dispdata->atomic_flags = 0;
   1011     dispdata->atomic_req = NULL;
   1012     dispdata->kms_fence = NULL;
   1013     dispdata->gpu_fence = NULL;
   1014     dispdata->kms_out_fence_fd = -1;
   1015     dispdata->modeset_pending = SDL_FALSE;
   1016     dispdata->gbm_init = SDL_FALSE;
   1017 
   1018     dispdata->display_plane = NULL;
   1019     dispdata->cursor_plane = NULL;
   1020 
   1021     dispdata->cursor_bo = NULL;
   1022 
   1023     /* Open /dev/dri/cardNN */
   1024     SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "/dev/dri/card%d", viddata->devindex);
   1025 
   1026     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
   1027     viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
   1028 
   1029     if (viddata->drm_fd < 0) {
   1030         ret = SDL_SetError("Could not open %s", devname);
   1031         goto cleanup;
   1032     }
   1033 
   1034     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
   1035 
   1036     /********************************************/
   1037     /* Block for enabling ATOMIC compatibility. */
   1038     /********************************************/
   1039 
   1040     /* Set ATOMIC & UNIVERSAL PLANES compatibility */
   1041     ret = set_client_caps(viddata->drm_fd);
   1042     if (ret) {
   1043         goto cleanup;
   1044     }
   1045 
   1046     /*******************************************/
   1047     /* Block for getting the ATOMIC resources. */
   1048     /*******************************************/
   1049 
   1050     /* Get all of the available connectors / devices / crtcs */
   1051     resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
   1052     if (!resources) {
   1053         ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
   1054         goto cleanup;
   1055     }
   1056 
   1057     /* Iterate on the available connectors to find a connected connector. */
   1058     for (i = 0; i < resources->count_connectors; i++) {
   1059         drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd,
   1060             resources->connectors[i]);
   1061 
   1062         if (!conn) {
   1063             continue;
   1064         }
   1065 
   1066         if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
   1067             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
   1068                          conn->connector_id, conn->count_modes);
   1069             connector = conn;
   1070 
   1071             break;
   1072         }
   1073 
   1074         KMSDRM_drmModeFreeConnector(conn);
   1075     }
   1076 
   1077     if (!connector) {
   1078         ret = SDL_SetError("No currently active connector found.");
   1079         goto cleanup;
   1080     }
   1081 
   1082     /* Try to find the connector's current encoder */
   1083     for (i = 0; i < resources->count_encoders; i++) {
   1084         encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   1085 
   1086         if (!encoder) {
   1087           continue;
   1088         }
   1089 
   1090         if (encoder->encoder_id == connector->encoder_id) {
   1091             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   1092             break;
   1093         }
   1094 
   1095         KMSDRM_drmModeFreeEncoder(encoder);
   1096         encoder = NULL;
   1097     }
   1098 
   1099     if (!encoder) {
   1100         /* No encoder was connected, find the first supported one */
   1101         for (i = 0; i < resources->count_encoders; i++) {
   1102             encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
   1103 
   1104             if (!encoder) {
   1105               continue;
   1106             }
   1107 
   1108             for (j = 0; j < connector->count_encoders; j++) {
   1109                 if (connector->encoders[j] == encoder->encoder_id) {
   1110                     break;
   1111                 }
   1112             }
   1113 
   1114             if (j != connector->count_encoders) {
   1115               break;
   1116             }
   1117 
   1118             KMSDRM_drmModeFreeEncoder(encoder);
   1119             encoder = NULL;
   1120         }
   1121     }
   1122 
   1123     if (!encoder) {
   1124         ret = SDL_SetError("No connected encoder found.");
   1125         goto cleanup;
   1126     }
   1127 
   1128     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
   1129 
   1130     /* Try to find a CRTC connected to this encoder */
   1131     crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   1132 
   1133     /* If no CRTC was connected to the encoder, find the first CRTC
   1134        that is supported by the encoder, and use that. */
   1135     if (!crtc) {
   1136         for (i = 0; i < resources->count_crtcs; i++) {
   1137             if (encoder->possible_crtcs & (1 << i)) {
   1138                 encoder->crtc_id = resources->crtcs[i];
   1139                 crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
   1140                 break;
   1141             }
   1142         }
   1143     }
   1144 
   1145     if (!crtc) {
   1146         ret = SDL_SetError("No CRTC found.");
   1147         goto cleanup;
   1148     }
   1149 
   1150     /* Figure out the default mode to be set. */
   1151     dispdata->mode = crtc->mode;
   1152 
   1153     /* Find the connector's preferred mode, to be used in case the current mode
   1154        is not valid, or if restoring the current mode fails.
   1155        We can always count on the preferred mode! */
   1156     for (i = 0; i < connector->count_modes; i++) {
   1157         if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
   1158             dispdata->preferred_mode = connector->modes[i];
   1159         }
   1160     }
   1161 
   1162     /* If the current CRTC's mode isn't valid, select the preferred
   1163        mode of the connector. */
   1164     if (crtc->mode_valid == 0) {
   1165         dispdata->mode = dispdata->preferred_mode;
   1166     }
   1167 
   1168     if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) {
   1169         ret = SDL_SetError("Couldn't get a valid connector videomode.");
   1170         goto cleanup;
   1171     }
   1172 
   1173     /* Get CRTC properties */
   1174     dispdata->crtc->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
   1175         crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
   1176 
   1177     dispdata->crtc->props_info = SDL_calloc(dispdata->crtc->props->count_props,
   1178         sizeof(*dispdata->crtc->props_info));
   1179 
   1180     if (!dispdata->crtc->props_info) {
   1181         ret = SDL_OutOfMemory();
   1182         goto cleanup;
   1183     }
   1184 
   1185     for (i = 0; i < dispdata->crtc->props->count_props; i++) {
   1186         dispdata->crtc->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
   1187         dispdata->crtc->props->props[i]);
   1188     }
   1189 
   1190     /* Get connector properties */
   1191     dispdata->connector->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
   1192         connector->connector_id, DRM_MODE_OBJECT_CONNECTOR);
   1193 
   1194     dispdata->connector->props_info = SDL_calloc(dispdata->connector->props->count_props,
   1195         sizeof(*dispdata->connector->props_info));
   1196 
   1197     if (!dispdata->connector->props_info) {
   1198         ret = SDL_OutOfMemory();
   1199         goto cleanup;
   1200     }
   1201 
   1202     for (i = 0; i < dispdata->connector->props->count_props; i++) {
   1203         dispdata->connector->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
   1204         dispdata->connector->props->props[i]);
   1205     }
   1206 
   1207     /* Store the connector and crtc for future use. This is all we keep from this function,
   1208        and these are just structs, inoffensive to VK. */
   1209     dispdata->connector->connector = connector;
   1210     dispdata->crtc->crtc = crtc;
   1211 
   1212     /***********************************/
   1213     /* Block fpr Vulkan compatibility. */
   1214     /***********************************/
   1215 
   1216     /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
   1217        Will reopen this in CreateWindow, but only if requested a non-VK window. */
   1218     KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0);
   1219     KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
   1220     close (viddata->drm_fd);
   1221     viddata->drm_fd = -1;
   1222 
   1223 cleanup:
   1224     if (encoder)
   1225         KMSDRM_drmModeFreeEncoder(encoder);
   1226     if (resources)
   1227         KMSDRM_drmModeFreeResources(resources);
   1228     if (ret) {
   1229         /* Error (complete) cleanup */
   1230         if (dispdata->connector->connector) {
   1231             KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
   1232             dispdata->connector->connector = NULL;
   1233         }
   1234         if (dispdata->crtc->props_info) {
   1235             SDL_free(dispdata->crtc->props_info);
   1236             dispdata->crtc->props_info = NULL;
   1237         }
   1238         if (dispdata->connector->props_info) {
   1239             SDL_free(dispdata->connector->props_info);
   1240             dispdata->connector->props_info = NULL;
   1241         }
   1242         if (dispdata->crtc->crtc) {
   1243             KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
   1244             dispdata->crtc->crtc = NULL;
   1245         }
   1246         if (viddata->drm_fd >= 0) {
   1247             close(viddata->drm_fd);
   1248             viddata->drm_fd = -1;
   1249         }
   1250     }
   1251 
   1252     return ret;
   1253 }
   1254 
   1255 /* Init the Vulkan-INCOMPATIBLE stuff:
   1256    Reopen FD, create gbm dev, create dumb buffer and setup display plane.
   1257    This is to be called late, in WindowCreate(), and ONLY if this is not
   1258    a Vulkan window.
   1259    We are doing this so late to allow Vulkan to work if we build a VK window.
   1260    These things are incompatible with Vulkan, which accesses the same resources
   1261    internally so they must be free when trying to build a Vulkan surface.
   1262 */
   1263 int
   1264 KMSDRM_GBMInit (_THIS, SDL_DisplayData *dispdata)
   1265 {
   1266     SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   1267     int ret = 0;
   1268 
   1269     /* Reopen the FD! */
   1270     viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
   1271     set_client_caps(viddata->drm_fd);
   1272 
   1273     /* Create the GBM device. */
   1274     viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
   1275     if (!viddata->gbm_dev) {
   1276         ret = SDL_SetError("Couldn't create gbm device.");
   1277     }
   1278 
   1279     /* Setup the display plane. ONLY do this after dispdata has the right
   1280        crtc and connector, because these are used in this function. */
   1281     ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
   1282     if (ret) {
   1283         ret = SDL_SetError("can't find suitable display plane.");
   1284     }
   1285 
   1286     dispdata->gbm_init = SDL_TRUE;
   1287 
   1288     return ret;
   1289 }
   1290 
   1291 /* Deinit the Vulkan-incompatible KMSDRM stuff. */
   1292 void
   1293 KMSDRM_GBMDeinit (_THIS, SDL_DisplayData *dispdata)
   1294 {
   1295     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   1296 
   1297     /* Free display plane */
   1298     free_plane(&dispdata->display_plane);
   1299 
   1300     /* Free cursor plane (if still not freed) */
   1301     free_plane(&dispdata->cursor_plane);
   1302 
   1303     /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
   1304        already called when we get here. */
   1305     if (viddata->gbm_dev) {
   1306         KMSDRM_gbm_device_destroy(viddata->gbm_dev);
   1307         viddata->gbm_dev = NULL;
   1308     }
   1309 
   1310     /* Finally close DRM FD. May be reopen on next non-vulkan window creation. */
   1311     if (viddata->drm_fd >= 0) {
   1312         close(viddata->drm_fd);
   1313         viddata->drm_fd = -1;
   1314     }
   1315 
   1316     dispdata->gbm_init = SDL_FALSE;
   1317 }
   1318 
   1319 /* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
   1320    disconnected from these buffers before doing so, or have the PRIMARY PLANE
   1321    reading the original FB where the TTY lives, before doing this, or CRTC will
   1322    be disconnected by the kernel. */
   1323 void
   1324 KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
   1325 {
   1326     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   1327     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
   1328     KMSDRM_PlaneInfo plane_info = {0};
   1329 
   1330 #if SDL_VIDEO_OPENGL_EGL
   1331     EGLContext egl_context;
   1332 #endif
   1333 
   1334     /********************************************************************/
   1335     /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
   1336     /* it's using, by making it point to the original CRTC buffer,      */
   1337     /* where the TTY console should be.                                 */
   1338     /********************************************************************/
   1339 
   1340     plane_info.plane = dispdata->display_plane;
   1341     plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
   1342     plane_info.fb_id = dispdata->crtc->crtc->buffer_id;
   1343     plane_info.src_w = dispdata->mode.hdisplay;
   1344     plane_info.src_h = dispdata->mode.vdisplay;
   1345     plane_info.crtc_w = dispdata->mode.hdisplay;
   1346     plane_info.crtc_h = dispdata->mode.vdisplay;
   1347 
   1348     drm_atomic_set_plane_props(&plane_info);
   1349 
   1350     /* Issue blocking atomic commit. */
   1351     if (drm_atomic_commit(_this, SDL_TRUE)) {
   1352         SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
   1353     }
   1354 
   1355     /****************************************************************************/
   1356     /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and     */
   1357     /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
   1358     /* anymore.                                                                 */
   1359     /****************************************************************************/
   1360 
   1361     /* Destroy the GBM surface and buffers. */
   1362     if (windata->bo) {
   1363         KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
   1364         windata->bo = NULL;
   1365     }
   1366 
   1367     if (windata->next_bo) {
   1368         KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
   1369         windata->next_bo = NULL;
   1370     }
   1371 
   1372     /***************************************************************************/
   1373     /* Destroy the EGL surface.                                                */
   1374     /* In this eglMakeCurrent() call, we disable the current EGL surface       */
   1375     /* because we're going to destroy it, but DON'T disable the EGL context,   */
   1376     /* because it won't be enabled again until the programs ask for a pageflip */
   1377     /* so we get to SwapWindow().                                              */
   1378     /* If we disable the context until then and a program tries to retrieve    */
   1379     /* the context version info before calling for a pageflip, the program     */
   1380     /* will get wrong info and we will be in trouble.                          */
   1381     /***************************************************************************/
   1382 
   1383 #if SDL_VIDEO_OPENGL_EGL
   1384     egl_context = (EGLContext)SDL_GL_GetCurrentContext();
   1385     SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
   1386 
   1387     if (windata->egl_surface != EGL_NO_SURFACE) {
   1388         SDL_EGL_DestroySurface(_this, windata->egl_surface);
   1389         windata->egl_surface = EGL_NO_SURFACE;
   1390     }
   1391 #endif
   1392 
   1393     if (windata->gs) {
   1394         KMSDRM_gbm_surface_destroy(windata->gs);
   1395         windata->gs = NULL;
   1396     }
   1397 
   1398 }
   1399 
   1400 int
   1401 KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
   1402 {
   1403     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   1404     SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
   1405     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   1406     uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
   1407     uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
   1408     uint32_t width, height;
   1409 
   1410     EGLContext egl_context;
   1411 
   1412     int ret = 0;
   1413 
   1414     if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
   1415        ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
   1416 
   1417         width = dispdata->mode.hdisplay;
   1418         height = dispdata->mode.vdisplay;
   1419     } else {
   1420         width = window->w;
   1421         height = window->h;
   1422     }
   1423 
   1424     if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
   1425         SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
   1426     }
   1427 
   1428     windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
   1429 
   1430     if (!windata->gs) {
   1431         return SDL_SetError("Could not create GBM surface");
   1432     }
   1433 
   1434 #if SDL_VIDEO_OPENGL_EGL
   1435     /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
   1436        but we need an EGL surface NOW, or GL won't be able to render into any surface
   1437        and we won't see the first frame. */
   1438     SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
   1439     windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
   1440 
   1441     if (windata->egl_surface == EGL_NO_SURFACE) {
   1442         ret = SDL_SetError("Could not create EGL window surface");
   1443         goto cleanup;
   1444     }
   1445 
   1446     /* Current context passing to EGL is now done here. If something fails,
   1447        go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */
   1448     egl_context = (EGLContext)SDL_GL_GetCurrentContext();
   1449     ret = SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
   1450 
   1451 #endif
   1452 
   1453 cleanup:
   1454 
   1455     if (ret) {
   1456         /* Error (complete) cleanup. */
   1457         if (windata->gs) {
   1458             KMSDRM_gbm_surface_destroy(windata->gs);
   1459             windata->gs = NULL;
   1460         }
   1461     }
   1462 
   1463     return ret;
   1464 }
   1465 
   1466 void
   1467 KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
   1468 {
   1469     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   1470     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   1471     SDL_VideoData *viddata = windata->viddata;
   1472     SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
   1473     unsigned int i, j;
   1474 
   1475     if (!windata) {
   1476         return;
   1477     }
   1478 
   1479     if (!is_vulkan) {
   1480         KMSDRM_DestroySurfaces(_this, window);
   1481 #if SDL_VIDEO_OPENGL_EGL
   1482         if (_this->egl_data) {
   1483             SDL_EGL_UnloadLibrary(_this);
   1484         }
   1485 #endif
   1486         if (dispdata->gbm_init) {
   1487             KMSDRM_DeinitMouse(_this);
   1488             KMSDRM_GBMDeinit(_this, dispdata);
   1489         }
   1490     }
   1491 
   1492     /********************************************/
   1493     /* Remove from the internal SDL window list */
   1494     /********************************************/
   1495 
   1496     for (i = 0; i < viddata->num_windows; i++) {
   1497         if (viddata->windows[i] == window) {
   1498             viddata->num_windows--;
   1499 
   1500             for (j = i; j < viddata->num_windows; j++) {
   1501                 viddata->windows[j] = viddata->windows[j + 1];
   1502             }
   1503 
   1504             break;
   1505         }
   1506     }
   1507 
   1508     /*********************************************************************/
   1509     /* Free the window driverdata. Bye bye, surface and buffer pointers! */
   1510     /*********************************************************************/
   1511     window->driverdata = NULL;
   1512     SDL_free(windata);
   1513 }
   1514 
   1515 /*****************************************************************************/
   1516 /* Reconfigure the window scaling parameters and re-construct it's surfaces, */
   1517 /* without destroying the window itself.                                     */
   1518 /* To be used by SetWindowSize() and SetWindowFullscreen().                  */
   1519 /*****************************************************************************/
   1520 static int
   1521 KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
   1522     SDL_WindowData *windata = window->driverdata;
   1523     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   1524     SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
   1525     float ratio;
   1526 
   1527     if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
   1528        ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
   1529 
   1530         windata->src_w = dispdata->mode.hdisplay;
   1531         windata->src_h = dispdata->mode.vdisplay;
   1532         windata->output_w = dispdata->mode.hdisplay;
   1533         windata->output_h = dispdata->mode.vdisplay;
   1534         windata->output_x = 0;
   1535 
   1536     } else {
   1537 
   1538         /* Normal non-fullscreen windows are scaled using the CRTC,
   1539            so get output (CRTC) size and position, for AR correction. */
   1540         ratio = (float)window->w / (float)window->h;
   1541         windata->src_w = window->w;
   1542         windata->src_h = window->h;
   1543         windata->output_w = dispdata->mode.vdisplay * ratio;
   1544         windata->output_h = dispdata->mode.vdisplay;
   1545         windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
   1546 
   1547     }
   1548 
   1549     if (!is_vulkan) {
   1550         if (KMSDRM_CreateSurfaces(_this, window)) {
   1551             return -1;
   1552         }
   1553     }
   1554     return 0;
   1555 }
   1556 
   1557 int
   1558 KMSDRM_VideoInit(_THIS)
   1559 {
   1560     int ret = 0;
   1561 
   1562     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   1563     SDL_DisplayData *dispdata = NULL;
   1564     SDL_VideoDisplay display = {0};
   1565 
   1566     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
   1567 
   1568     viddata->video_init = SDL_FALSE;
   1569 
   1570     dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
   1571     if (!dispdata) {
   1572         return SDL_OutOfMemory();
   1573     }
   1574 
   1575     /* Alloc memory for these. */
   1576     dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
   1577     dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
   1578     dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
   1579     if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
   1580         ret = SDL_OutOfMemory();
   1581         goto cleanup;
   1582     }
   1583 
   1584     /* Get KMSDRM resources info and store what we need. Getting and storing
   1585        this info isn't a problem for VK compatibility.
   1586        For VK-incompatible initializations we have KMSDRM_GBMInit(), which is
   1587        called on window creation, and only when we know it's not a VK window. */
   1588     if (KMSDRM_DisplayDataInit(_this, dispdata)) {
   1589         ret = SDL_SetError("error getting KMS/DRM information");
   1590         goto cleanup;
   1591     }
   1592 
   1593     /* Setup the single display that's available.
   1594        There's no problem with it being still incomplete. */
   1595     display.driverdata = dispdata;
   1596     display.desktop_mode.w = dispdata->mode.hdisplay;
   1597     display.desktop_mode.h = dispdata->mode.vdisplay;
   1598     display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
   1599     display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
   1600     display.current_mode = display.desktop_mode;
   1601 
   1602     /* Add the display only when it's ready, */
   1603     SDL_AddVideoDisplay(&display, SDL_FALSE);
   1604 
   1605     /* Use this if you ever need to see info on all available planes. */
   1606 #if 0
   1607     get_planes_info(_this);
   1608 #endif
   1609 
   1610 #ifdef SDL_INPUT_LINUXEV
   1611     SDL_EVDEV_Init();
   1612 #endif
   1613 
   1614     viddata->video_init = SDL_TRUE;
   1615 
   1616 cleanup:
   1617 
   1618     if (ret) {
   1619         /* Error (complete) cleanup */
   1620         if (dispdata->display_plane) {
   1621             SDL_free(dispdata->display_plane);
   1622         }
   1623         if (dispdata->crtc) {
   1624             SDL_free(dispdata->crtc);
   1625         }
   1626         if (dispdata->connector) {
   1627             SDL_free(dispdata->connector);
   1628         }
   1629 
   1630         SDL_free(dispdata);
   1631     }
   1632 
   1633     return ret;
   1634 }
   1635 
   1636 /* The driverdata pointers, like dispdata, viddata, windata, etc...
   1637    are freed by SDL internals, so not our job. */
   1638 void
   1639 KMSDRM_VideoQuit(_THIS)
   1640 {
   1641     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
   1642     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
   1643 
   1644     KMSDRM_DisplayDataDeinit(_this, dispdata);
   1645 
   1646 #ifdef SDL_INPUT_LINUXEV
   1647     SDL_EVDEV_Quit();
   1648 #endif
   1649 
   1650     /* Clear out the window list */
   1651     SDL_free(viddata->windows);
   1652     viddata->windows = NULL;
   1653     viddata->max_windows = 0;
   1654     viddata->num_windows = 0;
   1655     viddata->video_init = SDL_FALSE;
   1656 }
   1657 
   1658 #if 0
   1659 void
   1660 KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   1661 {
   1662     /* Only one display mode available: the current one */
   1663     SDL_AddDisplayMode(display, &display->current_mode);
   1664 }
   1665 #endif
   1666 
   1667 /* We only change the video mode for FULLSCREEN windows
   1668    that are not FULLSCREEN_DESKTOP.
   1669    Normal non-fullscreen windows are scaled using the CRTC. */
   1670 void
   1671 KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   1672 {
   1673     SDL_DisplayData *dispdata = display->driverdata;
   1674     drmModeConnector *conn = dispdata->connector->connector;
   1675     SDL_DisplayMode mode;
   1676     int i;
   1677 
   1678     for (i = 0; i < conn->count_modes; i++) {
   1679         SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
   1680 
   1681         if (!modedata) {
   1682             SDL_OutOfMemory();
   1683             return;
   1684         }
   1685 
   1686         modedata->mode_index = i;
   1687 
   1688         mode.w = conn->modes[i].hdisplay;
   1689         mode.h = conn->modes[i].vdisplay;
   1690         mode.refresh_rate = conn->modes[i].vrefresh;
   1691         mode.format = SDL_PIXELFORMAT_ARGB8888;
   1692         mode.driverdata = modedata;
   1693 
   1694         if (!SDL_AddDisplayMode(display, &mode)) {
   1695             SDL_free(modedata);
   1696         }
   1697     }
   1698 }
   1699 
   1700 int
   1701 KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   1702 {
   1703     /* Set the dispdata->mode to the new mode and leave actual modesetting
   1704        pending to be done on SwapWindow(), to be included on next atomic
   1705        commit changeset. */
   1706 
   1707     SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   1708     SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
   1709     SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   1710     drmModeConnector *conn = dispdata->connector->connector;
   1711     int i;
   1712 
   1713     if (!modedata) {
   1714         return SDL_SetError("Mode doesn't have an associated index");
   1715     }
   1716 
   1717     /* Take note of the new mode. It will be used in SwapWindow to
   1718        set the props needed for mode setting. */
   1719     dispdata->mode = conn->modes[modedata->mode_index];
   1720 
   1721     dispdata->modeset_pending = SDL_TRUE;
   1722 
   1723     for (i = 0; i < viddata->num_windows; i++) {
   1724         SDL_Window *window = viddata->windows[i];
   1725 
   1726     if (KMSDRM_CreateSurfaces(_this, window)) {
   1727         return -1;
   1728     }
   1729 
   1730         /* Tell app about the window resize */
   1731         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
   1732     }
   1733 
   1734     return 0;
   1735 }
   1736 
   1737 int
   1738 KMSDRM_CreateWindow(_THIS, SDL_Window * window)
   1739 {
   1740     SDL_WindowData *windata = NULL;
   1741     SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
   1742     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   1743     SDL_DisplayData *dispdata = display->driverdata;
   1744     SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
   1745     NativeDisplayType egl_display;
   1746     float ratio;
   1747     int ret = 0;
   1748 
   1749     if ( !(dispdata->gbm_init) && (!is_vulkan)) {
   1750          /* Reopen FD, create gbm dev, setup display plane, etc,.
   1751             but only when we come here for the first time,
   1752             and only if it's not a VK window. */
   1753          if ((ret = KMSDRM_GBMInit(_this, dispdata))) {
   1754                  goto cleanup;
   1755          }
   1756 
   1757 #if SDL_VIDEO_OPENGL_EGL
   1758          /* Manually load the EGL library. KMSDRM_EGL_LoadLibrary() has already
   1759             been called by SDL_CreateWindow() but we don't do anything there,
   1760             precisely to be able to load it here.
   1761             If we let SDL_CreateWindow() load the lib, it will be loaded
   1762             before we call KMSDRM_GBMInit(), causing GLES programs to fail. */
   1763          if (!_this->egl_data) {
   1764              egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
   1765              if ((ret = SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA))) {
   1766                  goto cleanup;
   1767              }
   1768          }
   1769 #endif
   1770 
   1771          /* Can't init mouse stuff sooner because cursor plane is not ready. */
   1772          KMSDRM_InitMouse(_this);
   1773 
   1774          /* Since we take cursor buffer way from the cursor plane and
   1775             destroy the cursor GBM BO when we destroy a window, we must
   1776             also manually re-show the cursor on screen, if necessary,
   1777             when we create a window. */
   1778          KMSDRM_InitCursor();
   1779     }
   1780 
   1781     /* Allocate window internal data */
   1782     windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
   1783     if (!windata) {
   1784         ret = SDL_OutOfMemory();
   1785         goto cleanup;
   1786     }
   1787 
   1788     if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
   1789        ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN))
   1790     {
   1791         windata->src_w = dispdata->mode.hdisplay;
   1792         windata->src_h = dispdata->mode.vdisplay;
   1793         windata->output_w = dispdata->mode.hdisplay;
   1794         windata->output_h = dispdata->mode.vdisplay;
   1795         windata->output_x = 0;
   1796     } else {
   1797         /* Normal non-fullscreen windows are scaled using the CRTC,
   1798            so get output (CRTC) size and position, for AR correction. */
   1799         ratio = (float)window->w / (float)window->h;
   1800         windata->src_w = window->w;
   1801         windata->src_h = window->h;
   1802         windata->output_w = dispdata->mode.vdisplay * ratio;
   1803         windata->output_h = dispdata->mode.vdisplay;
   1804         windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
   1805     }
   1806 
   1807     /* Don't force fullscreen on all windows: it confuses programs that try
   1808        to set a window fullscreen after creating it as non-fullscreen (sm64ex) */
   1809     // window->flags |= SDL_WINDOW_FULLSCREEN;
   1810 
   1811     /* Setup driver data for this window */
   1812     windata->viddata = viddata;
   1813     window->driverdata = windata;
   1814 
   1815     if (!is_vulkan) {
   1816         if ((ret = KMSDRM_CreateSurfaces(_this, window))) {
   1817             goto cleanup;
   1818         }
   1819     }
   1820 
   1821     /* Add window to the internal list of tracked windows. Note, while it may
   1822        seem odd to support multiple fullscreen windows, some apps create an
   1823        extra window as a dummy surface when working with multiple contexts */
   1824     if (viddata->num_windows >= viddata->max_windows) {
   1825         unsigned int new_max_windows = viddata->max_windows + 1;
   1826         viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
   1827               new_max_windows * sizeof(SDL_Window *));
   1828         viddata->max_windows = new_max_windows;
   1829 
   1830         if (!viddata->windows) {
   1831             ret = SDL_OutOfMemory();
   1832             goto cleanup;
   1833         }
   1834     }
   1835 
   1836     viddata->windows[viddata->num_windows++] = window;
   1837 
   1838     /* Focus on the newly created window */
   1839     SDL_SetMouseFocus(window);
   1840     SDL_SetKeyboardFocus(window);
   1841 
   1842     /***********************************************************/
   1843     /* Tell SDL that the mouse has entered the window using an */
   1844     /* artificial event: we have no windowing system to tell   */
   1845     /* SDL that it has happened. This makes SDL set the        */
   1846     /* SDL_WINDOW_MOUSE_FOCUS on this window, thus fixing      */
   1847     /* Scummvm sticky-on-sides software cursor.                */
   1848     /***********************************************************/
   1849     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_ENTER, 0, 0);
   1850 
   1851 cleanup:
   1852 
   1853     if (ret) {
   1854         /* Allocated windata will be freed in KMSDRM_DestroyWindow */
   1855         KMSDRM_DestroyWindow(_this, window);
   1856     }
   1857     return ret;
   1858 }
   1859 
   1860 int
   1861 KMSDRM_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   1862 {
   1863     return -1;
   1864 }
   1865 
   1866 void
   1867 KMSDRM_SetWindowTitle(_THIS, SDL_Window * window)
   1868 {
   1869 }
   1870 void
   1871 KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   1872 {
   1873 }
   1874 void
   1875 KMSDRM_SetWindowPosition(_THIS, SDL_Window * window)
   1876 {
   1877 }
   1878 
   1879 void
   1880 KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
   1881 {
   1882     if (KMSDRM_ReconfigureWindow(_this, window)) {
   1883         SDL_SetError("Can't reconfigure window on SetWindowSize.");
   1884     }
   1885 }
   1886 
   1887 void
   1888 KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   1889 {
   1890     if (KMSDRM_ReconfigureWindow(_this, window)) {
   1891         SDL_SetError("Can't reconfigure window on SetWindowFullscreen.");
   1892     }
   1893 }
   1894 
   1895 void
   1896 KMSDRM_ShowWindow(_THIS, SDL_Window * window)
   1897 {
   1898 }
   1899 void
   1900 KMSDRM_HideWindow(_THIS, SDL_Window * window)
   1901 {
   1902 }
   1903 void
   1904 KMSDRM_RaiseWindow(_THIS, SDL_Window * window)
   1905 {
   1906 }
   1907 void
   1908 KMSDRM_MaximizeWindow(_THIS, SDL_Window * window)
   1909 {
   1910 }
   1911 void
   1912 KMSDRM_MinimizeWindow(_THIS, SDL_Window * window)
   1913 {
   1914 }
   1915 void
   1916 KMSDRM_RestoreWindow(_THIS, SDL_Window * window)
   1917 {
   1918 }
   1919 void
   1920 KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
   1921 {
   1922 
   1923 }
   1924 
   1925 /*****************************************************************************/
   1926 /* SDL Window Manager function                                               */
   1927 /*****************************************************************************/
   1928 SDL_bool
   1929 KMSDRM_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
   1930 {
   1931     if (info->version.major <= SDL_MAJOR_VERSION) {
   1932         return SDL_TRUE;
   1933     } else {
   1934         SDL_SetError("application not compiled with SDL %d.%d\n",
   1935                       SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   1936         return SDL_FALSE;
   1937     }
   1938 
   1939     /* Failed to get window manager information */
   1940     return SDL_FALSE;
   1941 }
   1942 
   1943 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
   1944 
   1945 /* vi: set ts=4 sw=4 expandtab: */