Atlas - SDL_kmsdrmvideo.c
Home / ext / SDL / src / video / kmsdrm Lines: 11 | Size: 81751 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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#ifdef SDL_VIDEO_DRIVER_KMSDRM 25 26/* Include this first, as some system headers may pull in EGL headers that 27 * define EGL types as native types for other enabled platforms, which can 28 * result in type-mismatch warnings when building with LTO. 29 */ 30#include "../SDL_egl_c.h" 31 32// SDL internals 33#include "../../events/SDL_events_c.h" 34#include "../../events/SDL_keyboard_c.h" 35#include "../../events/SDL_mouse_c.h" 36 37#ifdef SDL_INPUT_LINUXEV 38#include "../../core/linux/SDL_evdev.h" 39#elif defined SDL_INPUT_WSCONS 40#include "../../core/openbsd/SDL_wscons.h" 41#endif 42 43// KMS/DRM declarations 44#include "SDL_kmsdrmdyn.h" 45#include "SDL_kmsdrmevents.h" 46#include "SDL_kmsdrmmouse.h" 47#include "SDL_kmsdrmvideo.h" 48#include "SDL_kmsdrmopengles.h" 49#include "SDL_kmsdrmvulkan.h" 50#include <dirent.h> 51#include <errno.h> 52#include <poll.h> 53#include <sys/param.h> 54#include <sys/stat.h> 55#include <sys/utsname.h> 56 57#ifdef SDL_PLATFORM_OPENBSD 58static bool moderndri = false; 59#else 60static bool moderndri = true; 61#endif 62 63static char kmsdrm_dri_path[16]; 64static int kmsdrm_dri_pathsize = 0; 65static char kmsdrm_dri_devname[8]; 66static int kmsdrm_dri_devnamesize = 0; 67static char kmsdrm_dri_cardpath[32]; 68 69/* for older KMSDRM headers... */ 70#ifndef DRM_FORMAT_MOD_VENDOR_NONE 71#define DRM_FORMAT_MOD_VENDOR_NONE 0 72#endif 73#ifndef DRM_FORMAT_MOD_LINEAR 74#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) 75#endif 76 77static int get_driindex(void) 78{ 79 int available = -ENOENT; 80 char device[sizeof(kmsdrm_dri_cardpath)]; 81 int drm_fd; 82 int i; 83 int devindex = -1; 84 DIR *folder; 85 const char *hint; 86 struct dirent *res; 87 88 hint = SDL_GetHint(SDL_HINT_KMSDRM_DEVICE_INDEX); 89 if (hint && *hint) { 90 char *endptr = NULL; 91 const int idx = (int)SDL_strtol(hint, &endptr, 10); 92 if ((*endptr == '\0') && (idx >= 0)) { /* *endptr==0 means "whole string was a valid number" */ 93 return idx; // we'll take the user's request here. 94 } 95 } 96 97 SDL_strlcpy(device, kmsdrm_dri_path, sizeof(device)); 98 folder = opendir(device); 99 if (!folder) { 100 SDL_SetError("Failed to open directory '%s'", device); 101 return -ENOENT; 102 } 103 104 SDL_strlcpy(device + kmsdrm_dri_pathsize, kmsdrm_dri_devname, 105 sizeof(device) - kmsdrm_dri_pathsize); 106 while((res = readdir(folder)) != NULL && available < 0) { 107 if (SDL_memcmp(res->d_name, kmsdrm_dri_devname, 108 kmsdrm_dri_devnamesize) == 0) { 109 SDL_strlcpy(device + kmsdrm_dri_pathsize + kmsdrm_dri_devnamesize, 110 res->d_name + kmsdrm_dri_devnamesize, 111 sizeof(device) - kmsdrm_dri_pathsize - 112 kmsdrm_dri_devnamesize); 113 114 drm_fd = open(device, O_RDWR | O_CLOEXEC); 115 if (drm_fd >= 0) { 116 devindex = SDL_atoi(device + kmsdrm_dri_pathsize + 117 kmsdrm_dri_devnamesize); 118 if (SDL_KMSDRM_LoadSymbols()) { 119 drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd); 120 if (resources) { 121 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, 122 "%s%d connector, encoder and CRTC counts are: %d %d %d", 123 kmsdrm_dri_cardpath, devindex, 124 resources->count_connectors, 125 resources->count_encoders, 126 resources->count_crtcs); 127 128 if (resources->count_connectors > 0 && 129 resources->count_encoders > 0 && 130 resources->count_crtcs > 0) { 131 available = -ENOENT; 132 for (i = 0; i < resources->count_connectors && available < 0; i++) { 133 drmModeConnector *conn = 134 KMSDRM_drmModeGetConnector( 135 drm_fd, resources->connectors[i]); 136 137 if (!conn) { 138 continue; 139 } 140 141 if (conn->connection == DRM_MODE_CONNECTED && 142 conn->count_modes) { 143 bool access_denied = false; 144 if (SDL_GetHintBoolean( 145 SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER, 146 true)) { 147 /* Skip this device if we can't obtain 148 * DRM master */ 149 KMSDRM_drmSetMaster(drm_fd); 150 if (KMSDRM_drmAuthMagic(drm_fd, 0) == -EACCES) { 151 access_denied = true; 152 } 153 } 154 155 if (!access_denied) { 156 available = devindex; 157 } 158 } 159 160 KMSDRM_drmModeFreeConnector(conn); 161 } 162 } 163 KMSDRM_drmModeFreeResources(resources); 164 } 165 SDL_KMSDRM_UnloadSymbols(); 166 } 167 close(drm_fd); 168 } else { 169 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, 170 "Failed to open KMSDRM device %s, errno: %d", device, errno); 171 } 172 } 173 } 174 175 closedir(folder); 176 177 return available; 178} 179 180static void CalculateRefreshRate(drmModeModeInfo *mode, int *numerator, int *denominator) 181{ 182 *numerator = mode->clock * 1000; 183 *denominator = mode->htotal * mode->vtotal; 184 185 if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 186 *numerator *= 2; 187 } 188 189 if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { 190 *denominator *= 2; 191 } 192 193 if (mode->vscan > 1) { 194 *denominator *= mode->vscan; 195 } 196} 197 198 199int add_connector_property(drmModeAtomicReq *req, KMSDRM_connector *conn, const char *name, uint64_t value) 200{ 201 unsigned int i; 202 int prop_id = 0; 203 204 for (i = 0 ; i < conn->props->count_props ; i++) { 205 if (SDL_strcmp(conn->props_info[i]->name, name) == 0) { 206 prop_id = conn->props_info[i]->prop_id; 207 break; 208 } 209 } 210 211 if (prop_id < 0) { 212 SDL_SetError("no connector property: %s", name); 213 return -EINVAL; 214 } 215 216 return KMSDRM_drmModeAtomicAddProperty(req, conn->connector->connector_id, prop_id, value); 217} 218 219int add_crtc_property(drmModeAtomicReq *req, KMSDRM_crtc *c, const char *name, uint64_t value) 220{ 221 unsigned int i; 222 int prop_id = -1; 223 224 for (i = 0 ; i < c->props->count_props ; i++) { 225 if (SDL_strcmp(c->props_info[i]->name, name) == 0) { 226 prop_id = c->props_info[i]->prop_id; 227 break; 228 } 229 } 230 231 if (prop_id < 0) { 232 SDL_SetError("no crtc property: %s", name); 233 return -EINVAL; 234 } 235 236 return KMSDRM_drmModeAtomicAddProperty(req, c->crtc->crtc_id, prop_id, value); 237} 238 239int add_plane_property(drmModeAtomicReq *req, KMSDRM_plane *p, const char *name, uint64_t value) 240{ 241 unsigned int i; 242 int prop_id = -1; 243 244 for (i = 0 ; i < p->props->count_props ; i++) { 245 if (SDL_strcmp(p->props_info[i]->name, name) == 0) { 246 prop_id = p->props_info[i]->prop_id; 247 break; 248 } 249 } 250 251 if (prop_id < 0) { 252 SDL_SetError("no plane property: %s", name); 253 return -EINVAL; 254 } 255 256 return KMSDRM_drmModeAtomicAddProperty(req, p->plane->plane_id, prop_id, value); 257} 258 259#ifdef DEBUG_KMSDRM 260static void print_plane_info(SDL_VideoDevice *_this, drmModePlanePtr plane) 261{ 262 SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); 263 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, 264 plane->plane_id, DRM_MODE_OBJECT_PLANE); 265 266 /* Search the plane props for the plane type. */ 267 uint32_t type = 0; 268 for (int i = 0; i < props->count_props; i++) { 269 drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[i]); 270 if ((SDL_strcmp(p->name, "type") == 0)) { 271 type = props->prop_values[i]; 272 } 273 274 KMSDRM_drmModeFreeProperty(p); 275 } 276 277 char *plane_type = "unknown"; 278 switch (type) { 279 case DRM_PLANE_TYPE_OVERLAY: 280 plane_type = "overlay"; 281 break; 282 283 case DRM_PLANE_TYPE_PRIMARY: 284 plane_type = "primary"; 285 break; 286 287 case DRM_PLANE_TYPE_CURSOR: 288 plane_type = "cursor"; 289 break; 290 } 291 292 293 /* Remember that to present a plane on screen, it has to be 294 connected to a CRTC so the CRTC scans it, 295 scales it, etc... and presents it on screen. */ 296 297 /* Now we look for the CRTCs supported by the plane. */ 298 drmModeRes *resources = KMSDRM_drmModeGetResources(viddata->drm_fd); 299 if (resources) { 300 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); 301 for (int i = 0; i < resources->count_crtcs; i++) { 302 if (plane->possible_crtcs & (1 << i)) { 303 uint32_t crtc_id = resources->crtcs[i]; 304 printf ("%d", crtc_id); 305 break; 306 } 307 } 308 printf ("\n\n"); 309 } 310} 311 312static void get_planes_info(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 313{ 314 SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); 315 316 drmModePlaneResPtr plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd); 317 if (!plane_resources) { 318 printf("drmModeGetPlaneResources failed: %s\n", strerror(errno)); 319 return; 320 } 321 322 printf("--Number of planes found: %d-- \n", plane_resources->count_planes); 323 printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc.crtc->crtc_id); 324 325 /* Iterate on all the available planes. */ 326 for (uint32_t i = 0; (i < plane_resources->count_planes); i++) { 327 const uint32_t plane_id = plane_resources->planes[i]; 328 329 drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); 330 if (!plane) { 331 printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno)); 332 continue; 333 } 334 335 /* Print plane info. */ 336 print_plane_info(_this, plane); 337 KMSDRM_drmModeFreePlane(plane); 338 } 339 340 KMSDRM_drmModeFreePlaneResources(plane_resources); 341} 342#endif 343 344/* Get the plane_id of a plane that is of the specified plane type (primary, 345 overlay, cursor...) and can use specified CRTC. */ 346static int get_plane_id(SDL_VideoDevice *_this, unsigned int crtc_id, uint32_t plane_type) 347{ 348 drmModeRes *resources = NULL; 349 drmModePlaneResPtr plane_resources = NULL; 350 uint32_t i, j; 351 unsigned int crtc_index = 0; 352 int ret = -EINVAL; 353 int found = 0; 354 355 SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); 356 357 resources = KMSDRM_drmModeGetResources(viddata->drm_fd); 358 359 /* Get the crtc_index for the current CRTC. 360 It's needed to find out if a plane supports the CRTC. */ 361 for (i = 0; i < resources->count_crtcs; i++) { 362 if (resources->crtcs[i] == crtc_id) { 363 crtc_index = i; 364 break; 365 } 366 } 367 368 plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd); 369 if (!plane_resources) { 370 return SDL_SetError("drmModeGetPlaneResources failed."); 371 } 372 373 /* Iterate on all the available planes. */ 374 for (i = 0; (i < plane_resources->count_planes) && !found; i++) { 375 376 uint32_t plane_id = plane_resources->planes[i]; 377 378 drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); 379 if (!plane) { 380 continue; 381 } 382 383 /* See if the current CRTC is available for this plane. */ 384 if (plane->possible_crtcs & (1 << crtc_index)) { 385 386 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties( 387 viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE); 388 ret = plane_id; 389 390 /* Iterate on the plane props to find the type of the plane, 391 to see if it's of the type we want. */ 392 for (j = 0; j < props->count_props; j++) { 393 394 drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, 395 props->props[j]); 396 397 if ((SDL_strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) { 398 /* found our plane, use that: */ 399 found = 1; 400 } 401 402 KMSDRM_drmModeFreeProperty(p); 403 } 404 405 KMSDRM_drmModeFreeObjectProperties(props); 406 } 407 408 KMSDRM_drmModeFreePlane(plane); 409 } 410 411 KMSDRM_drmModeFreePlaneResources(plane_resources); 412 KMSDRM_drmModeFreeResources(resources); 413 414 return ret; 415} 416 417/* Setup a plane and it's props. */ 418bool setup_plane(SDL_VideoDevice *_this, SDL_DisplayData *dispdata, KMSDRM_plane **_plane, uint32_t plane_type) 419{ 420 uint32_t plane_id; 421 SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); 422 bool ret = true; 423 424 *_plane = SDL_calloc(1, sizeof(**_plane)); 425 if (!(*_plane)) { 426 ret = false; 427 goto cleanup; 428 } 429 430 /* Get plane ID for a given CRTC and plane type. */ 431 plane_id = get_plane_id(_this, dispdata->crtc.crtc->crtc_id, plane_type); 432 433 if (!plane_id) { 434 ret = SDL_SetError("Invalid Plane ID"); 435 goto cleanup; 436 } 437 438 /* Get the DRM plane itself. */ 439 (*_plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); 440 441 /* Get the DRM plane properties. */ 442 if ((*_plane)->plane) { 443 unsigned int i; 444 (*_plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, 445 (*_plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE); 446 (*_plane)->props_info = SDL_calloc((*_plane)->props->count_props, sizeof(*(*_plane)->props_info)); 447 448 if ( !((*_plane)->props_info) ) { 449 ret = false; 450 goto cleanup; 451 } 452 453 for (i = 0; i < (*_plane)->props->count_props; i++) { 454 (*_plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, (*_plane)->props->props[i]); 455 } 456 } 457 458cleanup: 459 if (!ret) { 460 if (*_plane) { 461 SDL_free(*_plane); 462 *_plane = NULL; 463 } 464 } 465 return ret; 466} 467 468/* Free a plane and it's props. */ 469void free_plane(KMSDRM_plane **_plane) 470{ 471 if (*_plane) { 472 if ((*_plane)->plane) { 473 KMSDRM_drmModeFreePlane((*_plane)->plane); 474 (*_plane)->plane = NULL; 475 } 476 if ((*_plane)->props_info) { 477 SDL_free((*_plane)->props_info); 478 (*_plane)->props_info = NULL; 479 } 480 SDL_free(*_plane); 481 *_plane = NULL; 482 } 483} 484 485/**********************************************************************************/ 486/* The most important ATOMIC fn of the backend. */ 487/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */ 488/* over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected */ 489/* to the same PLANE). */ 490/* Think of a plane as a "frame" surrounding a picture, where the "picture" */ 491/* is the buffer, and we move the "frame" from a picture to another, */ 492/* and the one that has the "frame" is the one sent over to the screen */ 493/* via the CONNECTOR->ENCODER system. */ 494/* Think of a PLANE as being "in the middle", it's the CENTRAL part */ 495/* between the CRTC and the BUFFER that is shown on screen. */ 496/* What we do here is connect a PLANE to a CRTC and a BUFFER. */ 497/* -ALWAYS set the CRTC_ID and FB_ID attribs of a plane at the same time, */ 498/* meaning IN THE SAME atomic request. */ 499/* -And NEVER destroy a GBM surface whose buffers are being read by a plane: */ 500/* first, move the plane away from those buffers and ONLY THEN destroy the */ 501/* buffers and/or the GBM surface containing them. */ 502/**********************************************************************************/ 503void 504drm_atomic_set_plane_props(SDL_DisplayData *dispdata, struct KMSDRM_PlaneInfo *info) 505{ 506 /* Do we have a set of changes already in the making? If not, allocate a new one. */ 507 if (!dispdata->atomic_req) { 508 dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); 509 } 510 511 add_plane_property(dispdata->atomic_req, info->plane, "FB_ID", info->fb_id); 512 add_plane_property(dispdata->atomic_req, info->plane, "CRTC_ID", info->crtc_id); 513 add_plane_property(dispdata->atomic_req, info->plane, "SRC_W", info->src_w << 16); 514 add_plane_property(dispdata->atomic_req, info->plane, "SRC_H", info->src_h << 16); 515 add_plane_property(dispdata->atomic_req, info->plane, "SRC_X", info->src_x); 516 add_plane_property(dispdata->atomic_req, info->plane, "SRC_Y", info->src_y); 517 add_plane_property(dispdata->atomic_req, info->plane, "CRTC_W", info->crtc_w); 518 add_plane_property(dispdata->atomic_req, info->plane, "CRTC_H", info->crtc_h); 519 add_plane_property(dispdata->atomic_req, info->plane, "CRTC_X", info->crtc_x); 520 add_plane_property(dispdata->atomic_req, info->plane, "CRTC_Y", info->crtc_y); 521} 522 523int drm_atomic_commit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata, bool blocking, bool allow_modeset) 524{ 525 SDL_VideoData *viddata = ((SDL_VideoData *)_this->internal); 526 uint32_t atomic_flags = 0; 527 int ret; 528 529 if (!blocking) { 530 atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK; 531 } 532 533 if (allow_modeset) { 534 atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; 535 } 536 537 /* Never issue a new atomic commit if previous has not yet completed, 538 or it will error. */ 539 drm_atomic_waitpending(_this, dispdata); 540 541 ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, 542 atomic_flags, NULL); 543 544 if (ret) { 545 SDL_SetError("Atomic commit failed, returned %d.", ret); 546 /* Uncomment this for fast-debugging */ 547#if 0 548 printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno)); 549#endif 550 goto out; 551 } 552 553 if (dispdata->kms_in_fence_fd != -1) { 554 close(dispdata->kms_in_fence_fd); 555 dispdata->kms_in_fence_fd = -1; 556 } 557 558out: 559 KMSDRM_drmModeAtomicFree(dispdata->atomic_req); 560 dispdata->atomic_req = NULL; 561 562 return ret; 563} 564 565void 566drm_atomic_waitpending(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 567{ 568 /* Will return immediately if we have already destroyed the fence, because we NULL-ify it just after. 569 Also, will return immediately in double-buffer mode, because kms_fence will alsawys be NULL. */ 570 if (dispdata->kms_fence) { 571 EGLint status; 572 573 do { 574 status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, 575 dispdata->kms_fence, 0, EGL_FOREVER_KHR); 576 } while (status != EGL_CONDITION_SATISFIED_KHR); 577 578 _this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence); 579 dispdata->kms_fence = NULL; 580 } 581} 582 583static bool KMSDRM_Available(void) 584{ 585#ifdef SDL_PLATFORM_OPENBSD 586 struct utsname nameofsystem; 587 double releaseversion; 588#endif 589 int ret = -ENOENT; 590 591#ifdef SDL_PLATFORM_OPENBSD 592 if (!(uname(&nameofsystem) < 0)) { 593 releaseversion = SDL_atof(nameofsystem.release); 594 if (releaseversion >= 6.9) { 595 moderndri = true; 596 } 597 } 598#endif 599 600 if (moderndri) { 601 SDL_strlcpy(kmsdrm_dri_path, "/dev/dri/", sizeof(kmsdrm_dri_path)); 602 SDL_strlcpy(kmsdrm_dri_devname, "card", sizeof(kmsdrm_dri_devname)); 603 } else { 604 SDL_strlcpy(kmsdrm_dri_path, "/dev/", sizeof(kmsdrm_dri_path)); 605 SDL_strlcpy(kmsdrm_dri_devname, "drm", sizeof(kmsdrm_dri_devname)); 606 } 607 608 kmsdrm_dri_pathsize = SDL_strlen(kmsdrm_dri_path); 609 kmsdrm_dri_devnamesize = SDL_strlen(kmsdrm_dri_devname); 610 (void)SDL_snprintf(kmsdrm_dri_cardpath, sizeof(kmsdrm_dri_cardpath), "%s%s", 611 kmsdrm_dri_path, kmsdrm_dri_devname); 612 613 ret = get_driindex(); 614 if (ret >= 0) { 615 return true; 616 } 617 618 return false; 619} 620 621static void KMSDRM_DeleteDevice(SDL_VideoDevice *device) 622{ 623 if (device->internal) { 624 SDL_free(device->internal); 625 device->internal = NULL; 626 } 627 628 SDL_free(device); 629 630 SDL_KMSDRM_UnloadSymbols(); 631} 632 633static SDL_VideoDevice *KMSDRM_CreateDevice(void) 634{ 635 SDL_VideoDevice *device; 636 SDL_VideoData *viddata; 637 int devindex; 638 639 if (!KMSDRM_Available()) { 640 return NULL; 641 } 642 643 devindex = get_driindex(); 644 if (devindex < 0) { 645 SDL_SetError("devindex (%d) must not be negative.", devindex); 646 return NULL; 647 } 648 649 if (!SDL_KMSDRM_LoadSymbols()) { 650 return NULL; 651 } 652 653 device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); 654 if (!device) { 655 return NULL; 656 } 657 658 viddata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); 659 if (!viddata) { 660 goto cleanup; 661 } 662 viddata->devindex = devindex; 663 viddata->drm_fd = -1; 664 665 device->internal = viddata; 666 667 // Setup all functions which we can handle 668 device->VideoInit = KMSDRM_VideoInit; 669 device->VideoQuit = KMSDRM_VideoQuit; 670 device->GetDisplayModes = KMSDRM_GetDisplayModes; 671 device->SetDisplayMode = KMSDRM_SetDisplayMode; 672 device->CreateSDLWindow = KMSDRM_CreateWindow; 673 device->SetWindowTitle = KMSDRM_SetWindowTitle; 674 device->SetWindowPosition = KMSDRM_SetWindowPosition; 675 device->SetWindowSize = KMSDRM_SetWindowSize; 676 device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen; 677 device->ShowWindow = KMSDRM_ShowWindow; 678 device->HideWindow = KMSDRM_HideWindow; 679 device->RaiseWindow = KMSDRM_RaiseWindow; 680 device->MaximizeWindow = KMSDRM_MaximizeWindow; 681 device->MinimizeWindow = KMSDRM_MinimizeWindow; 682 device->RestoreWindow = KMSDRM_RestoreWindow; 683 device->DestroyWindow = KMSDRM_DestroyWindow; 684 device->SetWindowFocusable = KMSDRM_SetWindowFocusable; 685 686 device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary; 687 device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress; 688 device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary; 689 device->GL_CreateContext = KMSDRM_GLES_CreateContext; 690 device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent; 691 device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval; 692 device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval; 693 device->GL_SwapWindow = KMSDRM_GLES_SwapWindow; 694 device->GL_DestroyContext = KMSDRM_GLES_DestroyContext; 695 device->GL_SetDefaultProfileConfig = KMSDRM_GLES_SetDefaultProfileConfig; 696 697#ifdef SDL_VIDEO_VULKAN 698 device->Vulkan_LoadLibrary = KMSDRM_Vulkan_LoadLibrary; 699 device->Vulkan_UnloadLibrary = KMSDRM_Vulkan_UnloadLibrary; 700 device->Vulkan_GetInstanceExtensions = KMSDRM_Vulkan_GetInstanceExtensions; 701 device->Vulkan_CreateSurface = KMSDRM_Vulkan_CreateSurface; 702 device->Vulkan_DestroySurface = KMSDRM_Vulkan_DestroySurface; 703#endif 704 705 device->PumpEvents = KMSDRM_PumpEvents; 706 device->free = KMSDRM_DeleteDevice; 707 708 return device; 709 710cleanup: 711 SDL_free(device); 712 713 SDL_free(viddata); 714 return NULL; 715} 716 717VideoBootStrap KMSDRM_bootstrap = { 718 "kmsdrm", 719 "KMS/DRM Video Driver", 720 KMSDRM_CreateDevice, 721 NULL, // no ShowMessageBox implementation 722 false 723}; 724 725static void KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data) 726{ 727 KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data; 728 729 if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) { 730 KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id); 731 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id); 732 } 733 734 SDL_free(fb_info); 735} 736 737KMSDRM_FBInfo *KMSDRM_FBFromBO(SDL_VideoDevice *_this, struct gbm_bo *bo) 738{ 739 SDL_VideoData *viddata = _this->internal; 740 unsigned w, h; 741 int rc = -1; 742 int num_planes = 0; 743 uint32_t format, strides[4] = { 0 }, handles[4] = { 0 }, offsets[4] = { 0 }, flags = 0; 744 uint64_t modifiers[4] = { 0 }; 745 746 // Check for an existing framebuffer 747 KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo); 748 749 if (fb_info) { 750 return fb_info; 751 } 752 753 /* Create a structure that contains enough info to remove the framebuffer 754 when the backing buffer is destroyed */ 755 fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo)); 756 757 if (!fb_info) { 758 return NULL; 759 } 760 761 fb_info->drm_fd = viddata->drm_fd; 762 763 /* Create framebuffer object for the buffer using the modifiers requested by GBM. 764 Use of the modifiers is necessary on some platforms. */ 765 w = KMSDRM_gbm_bo_get_width(bo); 766 h = KMSDRM_gbm_bo_get_height(bo); 767 format = KMSDRM_gbm_bo_get_format(bo); 768 769 if (KMSDRM_drmModeAddFB2WithModifiers && 770 KMSDRM_gbm_bo_get_modifier && 771 KMSDRM_gbm_bo_get_plane_count && 772 KMSDRM_gbm_bo_get_offset && 773 KMSDRM_gbm_bo_get_stride_for_plane && 774 KMSDRM_gbm_bo_get_handle_for_plane) { 775 776 modifiers[0] = KMSDRM_gbm_bo_get_modifier(bo); 777 num_planes = KMSDRM_gbm_bo_get_plane_count(bo); 778 for (int i = 0; i < num_planes; i++) { 779 strides[i] = KMSDRM_gbm_bo_get_stride_for_plane(bo, i); 780 handles[i] = KMSDRM_gbm_bo_get_handle_for_plane(bo, i).u32; 781 offsets[i] = KMSDRM_gbm_bo_get_offset(bo, i); 782 modifiers[i] = modifiers[0]; 783 } 784 785 if (modifiers[0] && modifiers[0] != DRM_FORMAT_MOD_INVALID) { 786 flags = DRM_MODE_FB_MODIFIERS; 787 } 788 789 rc = KMSDRM_drmModeAddFB2WithModifiers(viddata->drm_fd, w, h, format, handles, strides, offsets, modifiers, &fb_info->fb_id, flags); 790 } 791 792 if (rc < 0) { 793 strides[0] = KMSDRM_gbm_bo_get_stride(bo); 794 handles[0] = KMSDRM_gbm_bo_get_handle(bo).u32; 795 rc = KMSDRM_drmModeAddFB(viddata->drm_fd, w, h, 24, 32, strides[0], handles[0], &fb_info->fb_id); 796 } 797 798 if (rc < 0) { 799 SDL_free(fb_info); 800 return NULL; 801 } 802 803 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, from BO %p", 804 fb_info->fb_id, w, h, (void *)bo); 805 806 // Associate our DRM framebuffer with this buffer object 807 KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback); 808 809 return fb_info; 810} 811 812static void KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) 813{ 814 *((bool *)data) = false; 815} 816 817bool KMSDRM_WaitPageflip(SDL_VideoDevice *_this, SDL_WindowData *windata) 818{ 819 820 SDL_VideoData *viddata = _this->internal; 821 drmEventContext ev = { 0 }; 822 struct pollfd pfd = { 0 }; 823 int ret; 824 825 ev.version = DRM_EVENT_CONTEXT_VERSION; 826 ev.page_flip_handler = KMSDRM_FlipHandler; 827 828 pfd.fd = viddata->drm_fd; 829 pfd.events = POLLIN; 830 831 /* Stay on the while loop until we get the desired event. 832 We need the while the loop because we could be in a situation where: 833 -We get and event on the FD in time, thus not on exiting on return number 1. 834 -The event is not an error, thus not exiting on return number 2. 835 -The event is of POLLIN type, but even then, if the event is not a pageflip, 836 drmHandleEvent() won't unset wait_for_pageflip, so we have to iterate 837 and go polling again. 838 839 If it wasn't for the while loop, we could erroneously exit the function 840 without the pageflip event to arrive! 841 842 For example, vblank events hit the FD and they are POLLIN events too (POLLIN 843 means "there's data to read on the FD"), but they are not the pageflip event 844 we are waiting for, so the drmEventHandle() doesn't run the flip handler, and 845 since waiting_for_flip is set on the pageflip handle, it's not set and we stay 846 on the loop, until we get the event for the pageflip, which is fine. 847 */ 848 while (windata->waiting_for_flip) { 849 850 pfd.revents = 0; 851 852 /* poll() waits for events arriving on the FD, and returns < 0 if timeout passes 853 with no events or a signal occurred before any requested event (-EINTR). 854 We wait forever (timeout = -1), but even if we DO get an event, we have yet 855 to see if it's of the required type, then if it's a pageflip, etc */ 856 ret = poll(&pfd, 1, -1); 857 858 if (ret < 0) { 859 if (errno == EINTR) { 860 /* poll() returning < 0 and setting errno = EINTR means there was a signal before 861 any requested event, so we immediately poll again. */ 862 continue; 863 } else { 864 // There was another error. Don't pull again or we could get into a busy loop. 865 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error"); 866 return false; 867 } 868 } 869 870 if (pfd.revents & (POLLHUP | POLLERR)) { 871 // An event arrived on the FD in time, but it's an error. 872 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error"); 873 return false; 874 } 875 876 if (pfd.revents & POLLIN) { 877 /* There is data to read on the FD! 878 Is the event a pageflip? We know it is a pageflip if it matches the 879 event we are passing in &ev. If it does, drmHandleEvent() will unset 880 windata->waiting_for_flip and we will get out of the "while" loop. 881 If it's not, we keep iterating on the loop. */ 882 KMSDRM_drmHandleEvent(viddata->drm_fd, &ev); 883 } 884 885 /* If we got to this point in the loop, we may iterate or exit the loop: 886 -A legit (non-error) event arrived, and it was a POLLING event, and it was consumed 887 by drmHandleEvent(). 888 -If it was a PAGEFLIP event, waiting_for_flip will be unset by drmHandleEvent() 889 and we will exit the loop. 890 -If it wasn't a PAGEFLIP, drmHandleEvent() won't unset waiting_for_flip, so we 891 iterare back to polling. 892 -A legit (non-error) event arrived, but it's not a POLLIN event, so it hasn't to be 893 consumed by drmHandleEvent(), so waiting_for_flip isn't set and we iterate back 894 to polling. */ 895 } 896 897 return true; 898} 899 900/* Given w, h and refresh rate, returns the closest DRM video mode 901 available on the DRM connector of the display. 902 We use the SDL mode list (which we filled in KMSDRM_GetDisplayModes) 903 because it's ordered, while the list on the connector is mostly random.*/ 904static drmModeModeInfo *KMSDRM_GetClosestDisplayMode(SDL_VideoDisplay *display, int width, int height) 905{ 906 907 SDL_DisplayData *dispdata = display->internal; 908 drmModeConnector *conn = dispdata->connector.connector; 909 910 SDL_DisplayMode closest; 911 drmModeModeInfo *drm_mode; 912 913 if (SDL_GetClosestFullscreenDisplayMode(display->id, width, height, 0.0f, false, &closest)) { 914 const SDL_DisplayModeData *modedata = closest.internal; 915 drm_mode = &conn->modes[modedata->mode_index]; 916 return drm_mode; 917 } else { 918 return NULL; 919 } 920} 921 922/*****************************************************************************/ 923// SDL Video and Display initialization/handling functions 924/* _this is a SDL_VideoDevice * */ 925/*****************************************************************************/ 926 927static bool KMSDRM_DropMaster(SDL_VideoDevice *_this) 928{ 929 SDL_VideoData *viddata = _this->internal; 930 931 if (viddata->is_atomic) { // turn off atomic support until we are in control again. 932 KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0); 933 KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0); 934 } 935 936 /* Check if we have DRM master to begin with */ 937 if (KMSDRM_drmAuthMagic(viddata->drm_fd, 0) == -EACCES) { 938 /* Nope, nothing to do then */ 939 return true; 940 } 941 942 return KMSDRM_drmDropMaster(viddata->drm_fd) == 0; 943} 944 945// Deinitializes the internal of the SDL Displays in the SDL display list. 946static void KMSDRM_DeinitDisplays(SDL_VideoDevice *_this) 947{ 948 SDL_VideoData *viddata = _this->internal; 949 SDL_DisplayID *displays; 950 SDL_DisplayData *dispdata; 951 int i; 952 953 displays = SDL_GetDisplays(NULL); 954 if (displays) { 955 // Iterate on the SDL Display list. 956 for (i = 0; displays[i]; ++i) { 957 958 // Get the internal for this display 959 dispdata = SDL_GetDisplayDriverData(displays[i]); 960 961 // Free connector 962 if (dispdata && dispdata->connector.connector) { 963 KMSDRM_drmModeFreeConnector(dispdata->connector.connector); 964 dispdata->connector.connector = NULL; 965 } 966 967 // Free CRTC 968 if (dispdata && dispdata->crtc.crtc) { 969 KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc); 970 dispdata->crtc.crtc = NULL; 971 } 972 } 973 SDL_free(displays); 974 } 975 976 if (viddata->drm_fd >= 0) { 977 close(viddata->drm_fd); 978 viddata->drm_fd = -1; 979 } 980} 981 982static bool KMSDRM_ConnectorCheckVrrCapable(uint32_t drm_fd, uint32_t output_id) 983{ 984 bool found = false; 985 uint64_t prop_value = 0; 986 987 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(drm_fd, output_id, DRM_MODE_OBJECT_CONNECTOR); 988 if (props) { 989 for (uint32_t i = 0; !found && i < props->count_props; ++i) { 990 drmModePropertyPtr prop = KMSDRM_drmModeGetProperty(drm_fd, props->props[i]); 991 if (prop) { 992 if (SDL_strcasecmp(prop->name, "VRR_CAPABLE") == 0) { 993 prop_value = props->prop_values[i]; 994 found = true; 995 } 996 KMSDRM_drmModeFreeProperty(prop); 997 } 998 } 999 KMSDRM_drmModeFreeObjectProperties(props); 1000 } 1001 if (found) { 1002 return prop_value ? true : false; 1003 } 1004 return false; 1005} 1006 1007static bool KMSDRM_VrrPropId(uint32_t drm_fd, uint32_t crtc_id, uint32_t *vrr_prop_id) 1008{ 1009 bool found = false; 1010 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC); 1011 if (props) { 1012 for (uint32_t i = 0; !found && i < props->count_props; ++i) { 1013 drmModePropertyPtr prop = KMSDRM_drmModeGetProperty(drm_fd, props->props[i]); 1014 if (prop) { 1015 if (SDL_strcmp(prop->name, "VRR_ENABLED") == 0) { 1016 *vrr_prop_id = prop->prop_id; 1017 found = true; 1018 } 1019 KMSDRM_drmModeFreeProperty(prop); 1020 } 1021 } 1022 KMSDRM_drmModeFreeObjectProperties(props); 1023 } 1024 return found; 1025} 1026 1027static void KMSDRM_CrtcSetVrr(uint32_t drm_fd, uint32_t crtc_id, bool enabled) 1028{ 1029 uint32_t vrr_prop_id; 1030 if (!KMSDRM_VrrPropId(drm_fd, crtc_id, &vrr_prop_id)) { 1031 return; 1032 } 1033 1034 KMSDRM_drmModeObjectSetProperty(drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC, vrr_prop_id, enabled); 1035} 1036 1037static bool KMSDRM_CrtcGetVrr(uint32_t drm_fd, uint32_t crtc_id) 1038{ 1039 uint32_t vrr_prop_id = 0; 1040 bool found = false; 1041 uint64_t prop_value = 0; 1042 1043 if (!KMSDRM_VrrPropId(drm_fd, crtc_id, &vrr_prop_id)) { 1044 return false; 1045 } 1046 1047 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC); 1048 if (props) { 1049 for (uint32_t i = 0; !found && i < props->count_props; ++i) { 1050 drmModePropertyPtr prop = KMSDRM_drmModeGetProperty(drm_fd, props->props[i]); 1051 if (prop) { 1052 if (prop->prop_id == vrr_prop_id) { 1053 prop_value = props->prop_values[i]; 1054 found = true; 1055 } 1056 KMSDRM_drmModeFreeProperty(prop); 1057 } 1058 } 1059 KMSDRM_drmModeFreeObjectProperties(props); 1060 } 1061 if (found) { 1062 return prop_value ? true : false; 1063 } 1064 return false; 1065} 1066 1067static int KMSDRM_CrtcGetOrientation(uint32_t drm_fd, uint32_t crtc_id) 1068{ 1069 bool found = false; 1070 int orientation = 0; 1071 1072 drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(drm_fd, crtc_id, DRM_MODE_OBJECT_CONNECTOR); 1073 if (props) { 1074 for (uint32_t i = 0; !found && i < props->count_props; ++i) { 1075 drmModePropertyPtr prop = KMSDRM_drmModeGetProperty(drm_fd, props->props[i]); 1076 if (prop) { 1077 if (SDL_strcasecmp(prop->name, "panel orientation") == 0 && (prop->flags & DRM_MODE_PROP_ENUM)) { 1078 if (prop->count_enums) { 1079 // "Normal" is the default of no rotation (0 degrees) 1080 if (SDL_strcmp(prop->enums[0].name, "Left Side Up") == 0) { 1081 orientation = 90; 1082 } else if (SDL_strcmp(prop->enums[0].name, "Upside Down") == 0) { 1083 orientation = 180; 1084 } else if (SDL_strcmp(prop->enums[0].name, "Right Side Up") == 0) { 1085 orientation = 270; 1086 } 1087 } 1088 found = true; 1089 } 1090 KMSDRM_drmModeFreeProperty(prop); 1091 } 1092 } 1093 KMSDRM_drmModeFreeObjectProperties(props); 1094 } 1095 return orientation; 1096} 1097 1098/* Gets a DRM connector, builds an SDL_Display with it, and adds it to the 1099 list of SDL Displays in _this->displays[] */ 1100static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *conn, drmModeRes *resources) 1101{ 1102 SDL_VideoData *viddata = _this->internal; 1103 SDL_DisplayData *dispdata = NULL; 1104 SDL_VideoDisplay display = { 0 }; 1105 SDL_DisplayModeData *modedata = NULL; 1106 drmModeEncoder *encoder = NULL; 1107 drmModeCrtc *crtc = NULL; 1108 const char *connector_type = NULL; 1109 SDL_DisplayID display_id; 1110 SDL_PropertiesID display_properties; 1111 char name_fmt[64]; 1112 int orientation; 1113 int mode_index; 1114 int i, j; 1115 int ret = 0; 1116 1117 // Reserve memory for the new display's internal. 1118 dispdata = (SDL_DisplayData *)SDL_calloc(1, sizeof(SDL_DisplayData)); 1119 if (!dispdata) { 1120 ret = -1; 1121 goto cleanup; 1122 } 1123 1124 /* Initialize some of the members of the new display's internal 1125 to sane values. */ 1126 dispdata->cursor_bo = NULL; 1127 dispdata->cursor_bo_drm_fd = -1; 1128 dispdata->kms_out_fence_fd = -1; 1129 1130 /* Since we create and show the default cursor on KMSDRM_InitMouse(), 1131 and we call KMSDRM_InitMouse() when we create a window, we have to know 1132 if the display used by the window already has a default cursor or not. 1133 If we don't, new default cursors would stack up on mouse->cursors and SDL 1134 would have to hide and delete them at quit, not to mention the memory leak... */ 1135 dispdata->default_cursor_init = false; 1136 1137 // Try to find the connector's current encoder 1138 for (i = 0; i < resources->count_encoders; i++) { 1139 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]); 1140 1141 if (!encoder) { 1142 continue; 1143 } 1144 1145 if (encoder->encoder_id == conn->encoder_id) { 1146 break; 1147 } 1148 1149 KMSDRM_drmModeFreeEncoder(encoder); 1150 encoder = NULL; 1151 } 1152 1153 if (!encoder) { 1154 // No encoder was connected, find the first supported one 1155 for (i = 0; i < resources->count_encoders; i++) { 1156 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, 1157 resources->encoders[i]); 1158 1159 if (!encoder) { 1160 continue; 1161 } 1162 1163 for (j = 0; j < conn->count_encoders; j++) { 1164 if (conn->encoders[j] == encoder->encoder_id) { 1165 break; 1166 } 1167 } 1168 1169 if (j != conn->count_encoders) { 1170 break; 1171 } 1172 1173 KMSDRM_drmModeFreeEncoder(encoder); 1174 encoder = NULL; 1175 } 1176 } 1177 1178 if (!encoder) { 1179 ret = SDL_SetError("No connected encoder found for connector."); 1180 goto cleanup; 1181 } 1182 1183 // Try to find a CRTC connected to this encoder 1184 crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); 1185 1186 /* If no CRTC was connected to the encoder, find the first CRTC 1187 that is supported by the encoder, and use that. */ 1188 if (!crtc) { 1189 for (i = 0; i < resources->count_crtcs; i++) { 1190 if (encoder->possible_crtcs & (1 << i)) { 1191 encoder->crtc_id = resources->crtcs[i]; 1192 crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); 1193 break; 1194 } 1195 } 1196 } 1197 1198 if (!crtc) { 1199 ret = SDL_SetError("No CRTC found for connector."); 1200 goto cleanup; 1201 } 1202 1203 // Find the index of the mode attached to this CRTC 1204 mode_index = -1; 1205 1206 for (i = 0; i < conn->count_modes; i++) { 1207 drmModeModeInfo *mode = &conn->modes[i]; 1208 1209 if (!SDL_memcmp(mode, &crtc->mode, sizeof(crtc->mode))) { 1210 mode_index = i; 1211 break; 1212 } 1213 } 1214 1215 if (mode_index == -1) { 1216 int current_area, largest_area = 0; 1217 1218 // Find the preferred mode or the highest resolution mode 1219 for (i = 0; i < conn->count_modes; i++) { 1220 drmModeModeInfo *mode = &conn->modes[i]; 1221 1222 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 1223 mode_index = i; 1224 break; 1225 } 1226 1227 current_area = mode->hdisplay * mode->vdisplay; 1228 if (current_area > largest_area) { 1229 mode_index = i; 1230 largest_area = current_area; 1231 } 1232 } 1233 if (mode_index != -1) { 1234 crtc->mode = conn->modes[mode_index]; 1235 } 1236 } 1237 1238 if (mode_index == -1) { 1239 ret = SDL_SetError("Failed to find index of mode attached to the CRTC."); 1240 goto cleanup; 1241 } 1242 1243 /*********************************************/ 1244 // Create an SDL Display for this connector. 1245 /*********************************************/ 1246 1247 /*********************************************/ 1248 // Part 1: setup the SDL_Display internal. 1249 /*********************************************/ 1250 1251 /* Get the mode currently setup for this display, 1252 which is the mode currently setup on the CRTC 1253 we found for the active connector. */ 1254 dispdata->mode = crtc->mode; 1255 dispdata->original_mode = crtc->mode; 1256 dispdata->fullscreen_mode = crtc->mode; 1257 1258 if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0) { 1259 ret = SDL_SetError("Couldn't get a valid connector videomode."); 1260 goto cleanup; 1261 } 1262 1263 // Store the connector and crtc for this display. 1264 dispdata->connector.connector = conn; 1265 dispdata->crtc.crtc = crtc; 1266 1267 // save previous vrr state 1268 dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, crtc->crtc_id); 1269 // try to enable vrr 1270 if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, conn->connector_id)) { 1271 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Enabling VRR"); 1272 KMSDRM_CrtcSetVrr(viddata->drm_fd, crtc->crtc_id, true); 1273 } 1274 1275 // Set the name by the connector type, if possible 1276 if (KMSDRM_drmModeGetConnectorTypeName) { 1277 connector_type = KMSDRM_drmModeGetConnectorTypeName(conn->connector_type); 1278 if (connector_type == NULL) { 1279 connector_type = "Unknown"; 1280 } 1281 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, conn->connector_type_id); 1282 } 1283 1284 dispdata->crtc.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); 1285 dispdata->crtc.props_info = SDL_calloc(dispdata->crtc.props->count_props, sizeof(*dispdata->crtc.props_info)); 1286 if (!dispdata->crtc.props_info) { 1287 ret = false; 1288 goto cleanup; 1289 } 1290 1291 for (i = 0; i < dispdata->crtc.props->count_props; i++) { 1292 dispdata->crtc.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, dispdata->crtc.props->props[i]); 1293 } 1294 1295 /* Get connector properties */ 1296 dispdata->connector.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); 1297 dispdata->connector.props_info = SDL_calloc(dispdata->connector.props->count_props, sizeof(*dispdata->connector.props_info)); 1298 if (!dispdata->connector.props_info) { 1299 ret = false; 1300 goto cleanup; 1301 } 1302 1303 for (i = 0; i < dispdata->connector.props->count_props; i++) { 1304 dispdata->connector.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, 1305 dispdata->connector.props->props[i]); 1306 } 1307 1308 /*****************************************/ 1309 // Part 2: setup the SDL_Display itself. 1310 /*****************************************/ 1311 1312 /* Setup the display. 1313 There's no problem with it being still incomplete. */ 1314 modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData)); 1315 1316 if (!modedata) { 1317 ret = -1; 1318 goto cleanup; 1319 } 1320 1321 modedata->mode_index = mode_index; 1322 1323 display.internal = dispdata; 1324 display.desktop_mode.w = dispdata->mode.hdisplay; 1325 display.desktop_mode.h = dispdata->mode.vdisplay; 1326 CalculateRefreshRate(&dispdata->mode, &display.desktop_mode.refresh_rate_numerator, &display.desktop_mode.refresh_rate_denominator); 1327 display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888; 1328 display.desktop_mode.internal = modedata; 1329 if (connector_type) { 1330 display.name = name_fmt; 1331 } 1332 1333 // Add the display to the list of SDL displays. 1334 display_id = SDL_AddVideoDisplay(&display, false); 1335 if (!display_id) { 1336 ret = -1; 1337 goto cleanup; 1338 } 1339 1340 orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, crtc->crtc_id); 1341 display_properties = SDL_GetDisplayProperties(display_id); 1342 SDL_SetNumberProperty(display_properties, SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER, orientation); 1343 1344#ifdef DEBUG_KMSDRM // Use this if you ever need to see info on all available planes. 1345 get_planes_info(_this, dispdata); 1346#endif 1347 1348cleanup: 1349 if (encoder) { 1350 KMSDRM_drmModeFreeEncoder(encoder); 1351 } 1352 if (ret) { 1353 // Error (complete) cleanup 1354 if (dispdata) { 1355 if (dispdata->connector.connector) { 1356 KMSDRM_drmModeFreeConnector(dispdata->connector.connector); 1357 } 1358 if (dispdata->crtc.crtc) { 1359 KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc); 1360 } 1361 SDL_free(dispdata->connector.props_info); 1362 SDL_free(dispdata->crtc.props_info); 1363 SDL_free(dispdata->display_plane); 1364 SDL_free(dispdata); 1365 } 1366 } 1367} // NOLINT(clang-analyzer-unix.Malloc): If no error `dispdata` is saved in the display 1368 1369static void KMSDRM_SortDisplays(SDL_VideoDevice *_this) 1370{ 1371 const char *name_hint = SDL_GetHint(SDL_HINT_VIDEO_DISPLAY_PRIORITY); 1372 1373 if (name_hint) { 1374 char *saveptr; 1375 char *str = SDL_strdup(name_hint); 1376 SDL_VideoDisplay **sorted_list = SDL_malloc(sizeof(SDL_VideoDisplay *) * _this->num_displays); 1377 1378 if (str && sorted_list) { 1379 int sorted_index = 0; 1380 1381 // Sort the requested displays to the front of the list. 1382 const char *token = SDL_strtok_r(str, ",", &saveptr); 1383 while (token) { 1384 for (int i = 0; i < _this->num_displays; ++i) { 1385 SDL_VideoDisplay *d = _this->displays[i]; 1386 if (d && SDL_strcmp(token, d->name) == 0) { 1387 sorted_list[sorted_index++] = d; 1388 _this->displays[i] = NULL; 1389 break; 1390 } 1391 } 1392 1393 token = SDL_strtok_r(NULL, ",", &saveptr); 1394 } 1395 1396 // Append the remaining displays to the end of the list. 1397 for (int i = 0; i < _this->num_displays; ++i) { 1398 if (_this->displays[i]) { 1399 sorted_list[sorted_index++] = _this->displays[i]; 1400 } 1401 } 1402 1403 // Copy the sorted list back to the display list. 1404 SDL_memcpy(_this->displays, sorted_list, sizeof(SDL_VideoDisplay *) * _this->num_displays); 1405 } 1406 1407 SDL_free(str); 1408 SDL_free(sorted_list); 1409 } 1410} 1411 1412static bool set_client_atomic_caps(int fd) 1413{ 1414 if (!SDL_GetHintBoolean(SDL_HINT_KMSDRM_ATOMIC, true)) { 1415 return false; // emergency escape hatch. 1416 } else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) { 1417 return false; // no atomic modesetting support. 1418 } else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { 1419 KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 0); // turn this back off again. 1420 return false; // no universal planes support. 1421 } 1422 return true; 1423} 1424 1425/* Initializes the list of SDL displays: we build a new display for each 1426 connecter connector we find. 1427 This is to be called early, in VideoInit(), because it gets us 1428 the videomode information, which SDL needs immediately after VideoInit(). */ 1429static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this) 1430{ 1431 SDL_VideoData *viddata = _this->internal; 1432 drmModeRes *resources = NULL; 1433 uint64_t async_pageflip = 0; 1434 int i; 1435 bool result = true; 1436 1437 // Open /dev/dri/cardNN (/dev/drmN if on OpenBSD version less than 6.9) 1438 (void)SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "%s%d", 1439 kmsdrm_dri_cardpath, viddata->devindex); 1440 1441 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", viddata->devpath); 1442 viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); 1443 1444 if (viddata->drm_fd < 0) { 1445 result = SDL_SetError("Could not open %s", viddata->devpath); 1446 goto cleanup; 1447 } 1448 1449 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); 1450 1451 // Set ATOMIC & UNIVERSAL PLANES compatibility 1452 viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); 1453 1454 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "DRM FD (%d) %s atomic", viddata->drm_fd, viddata->is_atomic ? "SUPPORTS" : "DOES NOT SUPPORT"); 1455 1456 // Get all of the available connectors / devices / crtcs 1457 resources = KMSDRM_drmModeGetResources(viddata->drm_fd); 1458 if (!resources) { 1459 result = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd); 1460 goto cleanup; 1461 } 1462 1463 /* Iterate on the available connectors. For every connected connector, 1464 we create an SDL_Display and add it to the list of SDL Displays. */ 1465 for (i = 0; i < resources->count_connectors; i++) { 1466 drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]); 1467 if (!conn) { 1468 continue; 1469 } 1470 1471 if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) { 1472 /* If it's a connected connector with available videomodes, try to add 1473 an SDL Display representing it. KMSDRM_AddDisplay() is purposely void, 1474 so if it fails (no encoder for connector, no valid video mode for 1475 connector etc...) we can keep looking for connected connectors. */ 1476 KMSDRM_AddDisplay(_this, conn, resources); 1477 } else { 1478 // If it's not, free it now. 1479 KMSDRM_drmModeFreeConnector(conn); 1480 } 1481 } 1482 1483 // Have we added any SDL displays? 1484 if (SDL_GetPrimaryDisplay() == 0) { 1485 result = SDL_SetError("No connected displays found."); 1486 goto cleanup; 1487 } 1488 1489 // Sort the displays, if necessary 1490 KMSDRM_SortDisplays(_this); 1491 1492 // Determine if video hardware supports async pageflips. 1493 if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_ASYNC_PAGE_FLIP, &async_pageflip) != 0) { 1494 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not determine async page flip capability."); 1495 } 1496 viddata->async_pageflip_support = async_pageflip ? true : false; 1497 1498 /***********************************/ 1499 // Block for Vulkan compatibility. 1500 /***********************************/ 1501 1502 /* Vulkan requires DRM master on its own FD to work, so try to drop master 1503 on our FD. This will only work without root on kernels v5.8 and later. 1504 If it doesn't work, just close the FD and we'll reopen it later. */ 1505 if (!KMSDRM_DropMaster(_this)) { 1506 close(viddata->drm_fd); 1507 viddata->drm_fd = -1; 1508 } 1509 1510cleanup: 1511 if (resources) { 1512 KMSDRM_drmModeFreeResources(resources); 1513 } 1514 if (!result) { 1515 if (viddata->drm_fd >= 0) { 1516 close(viddata->drm_fd); 1517 viddata->drm_fd = -1; 1518 } 1519 } 1520 return result; 1521} 1522 1523/* Init the Vulkan-INCOMPATIBLE stuff: 1524 Reopen FD, create gbm dev, create dumb buffer and setup display plane. 1525 This is to be called late, in WindowCreate(), and ONLY if this is not 1526 a Vulkan window. 1527 We are doing this so late to allow Vulkan to work if we build a VK window. 1528 These things are incompatible with Vulkan, which accesses the same resources 1529 internally so they must be free when trying to build a Vulkan surface. 1530*/ 1531static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 1532{ 1533 SDL_VideoData *viddata = _this->internal; 1534 bool result = true; 1535 1536 // Reopen the FD if we weren't able to drop master on the original one 1537 if (viddata->drm_fd < 0) { 1538 viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); 1539 if (viddata->drm_fd < 0) { 1540 return SDL_SetError("Could not reopen %s", viddata->devpath); 1541 } 1542 } 1543 1544 viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); 1545 1546 // Set the FD as current DRM master. 1547 KMSDRM_drmSetMaster(viddata->drm_fd); 1548 1549 // Create the GBM device. 1550 viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd); 1551 if (!viddata->gbm_dev) { 1552 result = SDL_SetError("Couldn't create gbm device."); 1553 } else { 1554 // Setup the display plane. ONLY do this after dispdata has the right 1555 // crtc and connector, because these are used in this function. 1556 result = setup_plane(_this, dispdata, &dispdata->display_plane, DRM_PLANE_TYPE_PRIMARY); 1557 if (!result) { 1558 SDL_SetError("can't find suitable display plane."); 1559 } 1560 } 1561 1562 viddata->gbm_init = true; 1563 1564 return result; 1565} 1566 1567// Deinit the Vulkan-incompatible KMSDRM stuff. 1568static void KMSDRM_GBMDeinit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 1569{ 1570 SDL_VideoData *viddata = _this->internal; 1571 1572 // Free display plane 1573 free_plane(&dispdata->display_plane); 1574 1575 // Free cursor plane (if still not freed) 1576 free_plane(&dispdata->cursor_plane); 1577 1578 /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(), 1579 already called when we get here. */ 1580 if (viddata->gbm_dev) { 1581 KMSDRM_gbm_device_destroy(viddata->gbm_dev); 1582 viddata->gbm_dev = NULL; 1583 } 1584 1585 /* Finally drop DRM master if possible, otherwise close DRM FD. 1586 May be reopened on next non-vulkan window creation. */ 1587 if (viddata->drm_fd >= 0 && !KMSDRM_DropMaster(_this)) { 1588 close(viddata->drm_fd); 1589 viddata->drm_fd = -1; 1590 } 1591 1592 viddata->gbm_init = false; 1593} 1594 1595static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window) 1596{ 1597 SDL_VideoData *viddata = _this->internal; 1598 SDL_WindowData *windata = window->internal; 1599 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 1600 int ret; 1601 1602 /**********************************************/ 1603 // Wait for last issued pageflip to complete. 1604 /**********************************************/ 1605 // KMSDRM_WaitPageflip(_this, windata); 1606 1607 if (viddata->is_atomic) { 1608 1609 /* TODO : Continue investigating why this doesn't work. We should do this instead 1610 of making the display plane point to the TTY console, which isn't there 1611 after creating and destroying a Vulkan window. */ 1612 1613#if 0 // (note that this code has bitrotted a little, in addition to TODO comment above.) 1614 /* Disconnect the connector from the CRTC (remember: several connectors 1615 can read a CRTC), deactivate the CRTC, and set the PRIMARY PLANE props 1616 CRTC_ID and FB_ID to 0. Then we can destroy the GBM buffers and surface. */ 1617 add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0); 1618 add_crtc_property(dispdata->atomic_req, dispdata->crtc , "MODE_ID", 0); 1619 add_crtc_property(dispdata->atomic_req, dispdata->crtc , "active", 0); 1620 /**********************************************/ 1621 /* Wait for last issued pageflip to complete. */ 1622 /**********************************************/ 1623 KMSDRM_WaitPageFlip(_this, windata, -1); 1624 1625 plane_info.plane = dispdata->display_plane; 1626 plane_info.crtc_id = 0; 1627 plane_info.fb_id = 0; 1628 /************************************************************************/ 1629 /* Restore the original CRTC configuration: configure the crtc with the */ 1630 /* original video mode and make it point to the original TTY buffer. */ 1631 /************************************************************************/ 1632 1633 drm_atomic_set_plane_props(dispdata, &plane_info); 1634 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, 1635 dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, 1636 &dispdata->original_mode); 1637 1638 /* Issue atomic commit that is blocking and allows modesetting. */ 1639 if (drm_atomic_commit(_this, dispdata, true, true)) { 1640 SDL_SetError("Failed to issue atomic commit on surfaces destruction."); 1641 /* If we failed to set the original mode, try to set the connector preferred mode. */ 1642 if (ret && (dispdata->crtc->mode_valid == 0)) { 1643 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, 1644 dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, 1645 &dispdata->original_mode); 1646 } 1647#endif 1648 1649#if 1 1650 /************************************************************/ 1651 /* Make the display plane point to the original TTY buffer. */ 1652 /* We have to configure it's input and output scaling */ 1653 /* parameters accordingly. */ 1654 /************************************************************/ 1655 1656 KMSDRM_PlaneInfo plane_info; 1657 SDL_zero(plane_info); 1658 plane_info.plane = dispdata->display_plane; 1659 plane_info.crtc_id = dispdata->crtc.crtc->crtc_id; 1660 plane_info.fb_id = dispdata->crtc.crtc->buffer_id; 1661 plane_info.src_w = dispdata->original_mode.hdisplay; 1662 plane_info.src_h = dispdata->original_mode.vdisplay; 1663 plane_info.crtc_w = dispdata->original_mode.hdisplay; 1664 plane_info.crtc_h = dispdata->original_mode.vdisplay; 1665 1666 drm_atomic_set_plane_props(dispdata, &plane_info); 1667 1668 if (drm_atomic_commit(_this, dispdata, true, false)) { 1669 SDL_SetError("Failed to issue atomic commit on surfaces destruction."); 1670 } 1671 } 1672#endif 1673 1674 /************************************************************************/ 1675 // Restore the original CRTC configuration: configure the crtc with the 1676 // original video mode and make it point to the original TTY buffer. 1677 /************************************************************************/ 1678 1679 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 1680 dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, 1681 &dispdata->original_mode); 1682 1683 // If we failed to set the original mode, try to set the connector preferred mode. 1684 if (ret && (dispdata->crtc.crtc->mode_valid == 0)) { 1685 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 1686 dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, 1687 &dispdata->original_mode); 1688 } 1689 1690 if (ret) { 1691 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not restore CRTC"); 1692 } 1693 1694 /***************************/ 1695 // Destroy the EGL surface 1696 /***************************/ 1697 1698 SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1699 1700 if (windata->egl_surface != EGL_NO_SURFACE) { 1701 SDL_EGL_DestroySurface(_this, windata->egl_surface); 1702 windata->egl_surface = EGL_NO_SURFACE; 1703 } 1704 1705 /***************************/ 1706 // Destroy the GBM buffers 1707 /***************************/ 1708 1709 if (windata->bo) { 1710 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); 1711 windata->bo = NULL; 1712 } 1713 1714 if (windata->next_bo) { 1715 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); 1716 windata->next_bo = NULL; 1717 } 1718 1719 /***************************/ 1720 // Destroy the GBM surface 1721 /***************************/ 1722 1723 if (windata->gs) { 1724 KMSDRM_gbm_surface_destroy(windata->gs); 1725 windata->gs = NULL; 1726 } 1727} 1728 1729static void KMSDRM_GetModeToSet(SDL_Window *window, drmModeModeInfo *out_mode) 1730{ 1731 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 1732 SDL_DisplayData *dispdata = display->internal; 1733 1734 if (window->fullscreen_exclusive) { 1735 *out_mode = dispdata->fullscreen_mode; 1736 } else { 1737 drmModeModeInfo *mode = KMSDRM_GetClosestDisplayMode(display, window->windowed.w, window->windowed.h); 1738 if (mode) { 1739 *out_mode = *mode; 1740 } else { 1741 *out_mode = dispdata->original_mode; 1742 } 1743 } 1744} 1745 1746static void KMSDRM_DirtySurfaces(SDL_Window *window) 1747{ 1748 SDL_WindowData *windata = window->internal; 1749 drmModeModeInfo mode; 1750 1751 /* Can't recreate EGL surfaces right now, need to wait until SwapWindow 1752 so the correct thread-local surface and context state are available */ 1753 windata->egl_surface_dirty = true; 1754 1755 /* The app may be waiting for the resize event after calling SetWindowSize 1756 or SetWindowFullscreen, send a fake event for now since the actual 1757 recreation is deferred */ 1758 KMSDRM_GetModeToSet(window, &mode); 1759 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode.hdisplay, mode.vdisplay); 1760} 1761 1762/* This determines the size of the fb, which comes from the GBM surface 1763 that we create here. */ 1764bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window) 1765{ 1766 SDL_VideoData *viddata = _this->internal; 1767 SDL_WindowData *windata = window->internal; 1768 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 1769 SDL_DisplayData *dispdata = display->internal; 1770 1771 uint32_t surface_fmt = GBM_FORMAT_ARGB8888; 1772 uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; 1773 1774 EGLContext egl_context; 1775 1776 bool result = true; 1777 1778 // If the current window already has surfaces, destroy them before creating other. 1779 if (windata->gs) { 1780 KMSDRM_DestroySurfaces(_this, window); 1781 } 1782 1783 if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, 1784 surface_fmt, surface_flags)) { 1785 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, 1786 "GBM surface format not supported. Trying anyway."); 1787 } 1788 1789 /* The KMSDRM backend doesn't always set the mode the higher-level code in 1790 SDL_video.c expects. Hulk-smash the display's current_mode to keep the 1791 mode that's set in sync with what SDL_video.c thinks is set 1792 1793 FIXME: How do we do that now? Can we get a better idea at the higher level? 1794 */ 1795 KMSDRM_GetModeToSet(window, &dispdata->mode); 1796 1797 windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, 1798 dispdata->mode.hdisplay, dispdata->mode.vdisplay, 1799 surface_fmt, surface_flags); 1800 if (!windata->gs && errno == ENOSYS) { 1801 // Try again without the scanout flags, needed on NVIDIA drivers 1802 windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, 1803 dispdata->mode.hdisplay, dispdata->mode.vdisplay, 1804 surface_fmt, 0); 1805 } 1806 if (!windata->gs) { 1807 return SDL_SetError("Could not create GBM surface: %s", strerror(errno)); 1808 } 1809 1810 /* We can't get the EGL context yet because SDL_CreateRenderer has not been called, 1811 but we need an EGL surface NOW, or GL won't be able to render into any surface 1812 and we won't see the first frame. */ 1813 SDL_EGL_SetRequiredVisualId(_this, surface_fmt); 1814 windata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)windata->gs); 1815 1816 if (windata->egl_surface == EGL_NO_SURFACE) { 1817 result = SDL_SetError("Could not create EGL window surface"); 1818 goto cleanup; 1819 } 1820 1821 /* Current context passing to EGL is now done here. If something fails, 1822 go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */ 1823 egl_context = (EGLContext)SDL_GL_GetCurrentContext(); 1824 result = SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); 1825 1826 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, 1827 dispdata->mode.hdisplay, dispdata->mode.vdisplay); 1828 1829 windata->egl_surface_dirty = false; 1830 1831cleanup: 1832 1833 if (!result) { 1834 // Error (complete) cleanup. 1835 if (windata->gs) { 1836 KMSDRM_gbm_surface_destroy(windata->gs); 1837 windata->gs = NULL; 1838 } 1839 } 1840 1841 return result; 1842} 1843 1844#ifdef SDL_INPUT_LINUXEV 1845static void KMSDRM_ReleaseVT(void *userdata) 1846{ 1847 SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata; 1848 SDL_VideoData *viddata = _this->internal; 1849 int i; 1850 1851 for (i = 0; i < viddata->num_windows; i++) { 1852 SDL_Window *window = viddata->windows[i]; 1853 if (!(window->flags & SDL_WINDOW_VULKAN)) { 1854 KMSDRM_DestroySurfaces(_this, window); 1855 } 1856 } 1857 KMSDRM_drmDropMaster(viddata->drm_fd); 1858} 1859 1860static void KMSDRM_AcquireVT(void *userdata) 1861{ 1862 SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata; 1863 SDL_VideoData *viddata = _this->internal; 1864 int i; 1865 1866 KMSDRM_drmSetMaster(viddata->drm_fd); 1867 for (i = 0; i < viddata->num_windows; i++) { 1868 SDL_Window *window = viddata->windows[i]; 1869 if (!(window->flags & SDL_WINDOW_VULKAN)) { 1870 KMSDRM_CreateSurfaces(_this, window); 1871 } 1872 } 1873} 1874#endif // defined SDL_INPUT_LINUXEV 1875 1876bool KMSDRM_VideoInit(SDL_VideoDevice *_this) 1877{ 1878 bool result = true; 1879 1880 SDL_VideoData *viddata = _this->internal; 1881 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()"); 1882 1883 viddata->video_init = false; 1884 viddata->gbm_init = false; 1885 1886 /* Get KMSDRM resources info and store what we need. Getting and storing 1887 this info isn't a problem for VK compatibility. 1888 For VK-incompatible initializations we have KMSDRM_GBMInit(), which is 1889 called on window creation, and only when we know it's not a VK window. */ 1890 if (!KMSDRM_InitDisplays(_this)) { 1891 result = SDL_SetError("error getting KMSDRM displays information"); 1892 } 1893 1894#ifdef SDL_INPUT_LINUXEV 1895 SDL_EVDEV_Init(); 1896 SDL_EVDEV_SetVTSwitchCallbacks(KMSDRM_ReleaseVT, _this, KMSDRM_AcquireVT, _this); 1897#elif defined(SDL_INPUT_WSCONS) 1898 SDL_WSCONS_Init(); 1899#endif 1900 1901 viddata->video_init = true; 1902 1903 return result; 1904} 1905 1906/* The internal pointers, like dispdata, viddata, windata, etc... 1907 are freed by SDL internals, so not our job. */ 1908void KMSDRM_VideoQuit(SDL_VideoDevice *_this) 1909{ 1910 SDL_VideoData *viddata = _this->internal; 1911 1912 KMSDRM_DeinitDisplays(_this); 1913 1914#ifdef SDL_INPUT_LINUXEV 1915 SDL_EVDEV_SetVTSwitchCallbacks(NULL, NULL, NULL, NULL); 1916 SDL_EVDEV_Quit(); 1917#elif defined(SDL_INPUT_WSCONS) 1918 SDL_WSCONS_Quit(); 1919#endif 1920 1921 // Clear out the window list 1922 SDL_free(viddata->windows); 1923 viddata->windows = NULL; 1924 viddata->max_windows = 0; 1925 viddata->num_windows = 0; 1926 viddata->video_init = false; 1927} 1928 1929// Read modes from the connector modes, and store them in display->display_modes. 1930bool KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 1931{ 1932 SDL_DisplayData *dispdata = display->internal; 1933 drmModeConnector *conn = dispdata->connector.connector; 1934 SDL_DisplayMode mode; 1935 int i; 1936 1937 for (i = 0; i < conn->count_modes; i++) { 1938 SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData)); 1939 1940 if (modedata) { 1941 modedata->mode_index = i; 1942 } 1943 1944 SDL_zero(mode); 1945 mode.w = conn->modes[i].hdisplay; 1946 mode.h = conn->modes[i].vdisplay; 1947 CalculateRefreshRate(&conn->modes[i], &mode.refresh_rate_numerator, &mode.refresh_rate_denominator); 1948 mode.format = SDL_PIXELFORMAT_ARGB8888; 1949 mode.internal = modedata; 1950 1951 if (!SDL_AddFullscreenDisplayMode(display, &mode)) { 1952 SDL_free(modedata); 1953 } 1954 } 1955 return true; 1956} 1957 1958bool KMSDRM_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 1959{ 1960 /* Set the dispdata->mode to the new mode and leave actual modesetting 1961 pending to be done on SwapWindow() via drmModeSetCrtc() */ 1962 1963 SDL_VideoData *viddata = _this->internal; 1964 SDL_DisplayData *dispdata = display->internal; 1965 SDL_DisplayModeData *modedata = mode->internal; 1966 drmModeConnector *conn = dispdata->connector.connector; 1967 int i; 1968 1969 // Don't do anything if we are in Vulkan mode. 1970 if (viddata->vulkan_mode) { 1971 return true; 1972 } 1973 1974 if (!modedata) { 1975 return SDL_SetError("Mode doesn't have an associated index"); 1976 } 1977 1978 /* Take note of the new mode to be set, and leave the CRTC modeset pending 1979 so it's done in SwapWindow. */ 1980 dispdata->fullscreen_mode = conn->modes[modedata->mode_index]; 1981 1982 for (i = 0; i < viddata->num_windows; i++) { 1983 KMSDRM_DirtySurfaces(viddata->windows[i]); 1984 } 1985 1986 return true; 1987} 1988 1989void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) 1990{ 1991 SDL_WindowData *windata = window->internal; 1992 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 1993 SDL_VideoData *viddata; 1994 bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; // Is this a VK window? 1995 unsigned int i, j; 1996 1997 if (!windata) { 1998 return; 1999 } 2000 2001 // restore vrr state 2002 KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc.crtc->crtc_id, dispdata->saved_vrr); 2003 2004 viddata = windata->viddata; 2005 2006 if (!is_vulkan && viddata->gbm_init) { 2007 2008 // Destroy cursor GBM BO of the display of this window. 2009 KMSDRM_DestroyCursorBO(_this, SDL_GetVideoDisplayForWindow(window)); 2010 2011 // Destroy GBM surface and buffers. 2012 KMSDRM_DestroySurfaces(_this, window); 2013 2014 /* Unload library and deinit GBM, but only if this is the last window. 2015 Note that this is the right comparison because num_windows could be 1 2016 if there is a complete window, or 0 if we got here from SDL_CreateWindow() 2017 because KMSDRM_CreateWindow() returned an error so the window wasn't 2018 added to the windows list. */ 2019 if (viddata->num_windows <= 1) { 2020 2021 // Unload EGL/GL library and free egl_data. 2022 if (_this->egl_data) { 2023 SDL_EGL_UnloadLibrary(_this); 2024 _this->gl_config.driver_loaded = 0; 2025 } 2026 2027 // Free display plane, and destroy GBM device. 2028 KMSDRM_GBMDeinit(_this, dispdata); 2029 } 2030 2031 } else { 2032 2033 // If we were in Vulkan mode, get out of it. 2034 if (viddata->vulkan_mode) { 2035 viddata->vulkan_mode = false; 2036 } 2037 } 2038 2039 /********************************************/ 2040 // Remove from the internal SDL window list 2041 /********************************************/ 2042 2043 for (i = 0; i < viddata->num_windows; i++) { 2044 if (viddata->windows[i] == window) { 2045 viddata->num_windows--; 2046 2047 for (j = i; j < viddata->num_windows; j++) { 2048 viddata->windows[j] = viddata->windows[j + 1]; 2049 } 2050 2051 break; 2052 } 2053 } 2054 2055 /*********************************************************************/ 2056 // Free the window internal. Bye bye, surface and buffer pointers! 2057 /*********************************************************************/ 2058 SDL_free(window->internal); 2059 window->internal = NULL; 2060} 2061 2062/**********************************************************************/ 2063// We simply IGNORE if it's a fullscreen window, window->flags don't 2064// reflect it: if it's fullscreen, KMSDRM_SetWindowFullscreen() will 2065// be called by SDL later, and we can manage it there. 2066/**********************************************************************/ 2067bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) 2068{ 2069 SDL_WindowData *windata = NULL; 2070 SDL_VideoData *viddata = _this->internal; 2071 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 2072 SDL_DisplayData *dispdata = display->internal; 2073 bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; // Is this a VK window? 2074 bool vulkan_mode = viddata->vulkan_mode; // Do we have any Vulkan windows? 2075 NativeDisplayType egl_display; 2076 drmModeModeInfo *mode; 2077 bool result = true; 2078 2079 // Allocate window internal data 2080 windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); 2081 if (!windata) { 2082 return false; 2083 } 2084 2085 // Setup driver data for this window 2086 windata->viddata = viddata; 2087 window->internal = windata; 2088 2089 // Do we want a double buffering scheme to get low video lag? 2090 windata->double_buffer = false; 2091 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) { 2092 windata->double_buffer = true; 2093 } 2094 2095 if (!is_vulkan && !vulkan_mode) { // NON-Vulkan block. 2096 2097 /* Maybe you didn't ask for an OPENGL window, but that's what you will get. 2098 See following comments on why. */ 2099 window->flags |= SDL_WINDOW_OPENGL; 2100 2101 if (!(viddata->gbm_init)) { 2102 2103 /* After SDL_CreateWindow, most SDL programs will do SDL_CreateRenderer(), 2104 which will in turn call GL_CreateRenderer() or GLES2_CreateRenderer(). 2105 In order for the GL_CreateRenderer() or GLES2_CreateRenderer() call to 2106 succeed without an unnecessary window re-creation, we must: 2107 -Mark the window as being OPENGL 2108 -Load the GL library (which can't be done until the GBM device has been 2109 created, so we have to do it here instead of doing it on VideoInit()) 2110 and mark it as loaded by setting gl_config.driver_loaded to 1. 2111 So if you ever see KMSDRM_CreateWindow() to be called two times in tests, 2112 don't be shy to debug GL_CreateRenderer() or GLES2_CreateRenderer() 2113 to find out why! 2114 */ 2115 2116 /* Reopen FD, create gbm dev, setup display plane, etc,. 2117 but only when we come here for the first time, 2118 and only if it's not a VK window. */ 2119 if (!KMSDRM_GBMInit(_this, dispdata)) { 2120 return SDL_SetError("Can't init GBM on window creation."); 2121 } 2122 } 2123 2124 /* Manually load the GL library. KMSDRM_EGL_LoadLibrary() has already 2125 been called by SDL_CreateWindow() but we don't do anything there, 2126 our KMSDRM_EGL_LoadLibrary() is a dummy precisely to be able to load it here. 2127 If we let SDL_CreateWindow() load the lib, it would be loaded 2128 before we call KMSDRM_GBMInit(), causing all GLES programs to fail. */ 2129 if (!_this->egl_data) { 2130 egl_display = (NativeDisplayType)_this->internal->gbm_dev; 2131 if (!SDL_EGL_LoadLibrary(_this, NULL, egl_display)) { 2132 // Try again with OpenGL ES 2.0 2133 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; 2134 _this->gl_config.major_version = 2; 2135 _this->gl_config.minor_version = 0; 2136 if (!SDL_EGL_LoadLibrary(_this, NULL, egl_display)) { 2137 return SDL_SetError("Can't load EGL/GL library on window creation."); 2138 } 2139 } 2140 2141 _this->gl_config.driver_loaded = 1; 2142 } 2143 2144 /* Create the cursor BO for the display of this window, 2145 now that we know this is not a VK window. */ 2146 KMSDRM_CreateCursorBO(display); 2147 2148 /* Create and set the default cursor for the display 2149 of this window, now that we know this is not a VK window. */ 2150 KMSDRM_InitMouse(_this, display); 2151 2152 /* The FULLSCREEN flags are cut out from window->flags at this point, 2153 so we can't know if a window is fullscreen or not, hence all windows 2154 are considered "windowed" at this point of their life. 2155 If a window is fullscreen, SDL internals will call 2156 KMSDRM_SetWindowFullscreen() to reconfigure it if necessary. */ 2157 mode = KMSDRM_GetClosestDisplayMode(display, window->windowed.w, window->windowed.h); 2158 2159 if (mode) { 2160 dispdata->fullscreen_mode = *mode; 2161 } else { 2162 dispdata->fullscreen_mode = dispdata->original_mode; 2163 } 2164 2165 /* Create the window surfaces with the size we have just chosen. 2166 Needs the window driverdata in place. */ 2167 if (!KMSDRM_CreateSurfaces(_this, window)) { 2168 return false; 2169 } 2170 } // NON-Vulkan block ends. 2171 2172 /* Add window to the internal list of tracked windows. Note, while it may 2173 seem odd to support multiple fullscreen windows, some apps create an 2174 extra window as a dummy surface when working with multiple contexts */ 2175 if (viddata->num_windows >= viddata->max_windows) { 2176 unsigned int new_max_windows = viddata->max_windows + 1; 2177 SDL_Window **new_windows = (SDL_Window **)SDL_realloc(viddata->windows, 2178 new_max_windows * sizeof(SDL_Window *)); 2179 if (!new_windows) { 2180 return false; 2181 } 2182 viddata->windows = new_windows; 2183 viddata->max_windows = new_max_windows; 2184 2185 } 2186 2187 viddata->windows[viddata->num_windows++] = window; 2188 2189 // If we have just created a Vulkan window, establish that we are in Vulkan mode now. 2190 viddata->vulkan_mode = is_vulkan; 2191 2192 SDL_PropertiesID props = SDL_GetWindowProperties(window); 2193 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER, viddata->devindex); 2194 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER, viddata->drm_fd); 2195 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER, viddata->gbm_dev); 2196 2197 if ((window->flags & SDL_WINDOW_NOT_FOCUSABLE) == 0) { 2198 /* Focus on the newly created window. 2199 SDL_SetMouseFocus() also takes care of calling KMSDRM_ShowCursor() if necessary. */ 2200 SDL_SetMouseFocus(window); 2201 SDL_SetKeyboardFocus(window); 2202 } 2203 2204 // Tell the app that the window has moved to top-left. 2205 SDL_Rect display_bounds; 2206 SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &display_bounds); 2207 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display_bounds.x, display_bounds.y); 2208 2209 /* Allocated windata will be freed in KMSDRM_DestroyWindow, 2210 and KMSDRM_DestroyWindow() will be called by SDL_CreateWindow() 2211 if we return error on any of the previous returns of the function. */ 2212 return result; 2213} 2214 2215void KMSDRM_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) 2216{ 2217} 2218bool KMSDRM_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 2219{ 2220 return SDL_Unsupported(); 2221} 2222void KMSDRM_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 2223{ 2224 SDL_VideoData *viddata = _this->internal; 2225 if (!viddata->vulkan_mode) { 2226 KMSDRM_DirtySurfaces(window); 2227 } 2228} 2229SDL_FullscreenResult KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen) 2230{ 2231 SDL_VideoData *viddata = _this->internal; 2232 if (!viddata->vulkan_mode) { 2233 KMSDRM_DirtySurfaces(window); 2234 } 2235 return SDL_FULLSCREEN_SUCCEEDED; 2236} 2237void KMSDRM_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) 2238{ 2239} 2240void KMSDRM_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) 2241{ 2242} 2243void KMSDRM_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) 2244{ 2245} 2246void KMSDRM_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 2247{ 2248} 2249void KMSDRM_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 2250{ 2251} 2252void KMSDRM_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) 2253{ 2254} 2255bool KMSDRM_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) 2256{ 2257 return true; // this just has to exist or SDL_SetWindowFocusable() will refuse to change the window flag. 2258} 2259 2260#endif // SDL_VIDEO_DRIVER_KMSDRM 2261[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.