Atlas - SDL_kmsdrmvideo.c

Home / ext / SDL / src / video / kmsdrm Lines: 11 | Size: 81848 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_in_fence_fd = -1; 1129 dispdata->kms_out_fence_fd = -1; 1130 1131 /* Since we create and show the default cursor on KMSDRM_InitMouse(), 1132 and we call KMSDRM_InitMouse() when we create a window, we have to know 1133 if the display used by the window already has a default cursor or not. 1134 If we don't, new default cursors would stack up on mouse->cursors and SDL 1135 would have to hide and delete them at quit, not to mention the memory leak... */ 1136 dispdata->default_cursor_init = false; 1137 1138 // Try to find the connector's current encoder 1139 for (i = 0; i < resources->count_encoders; i++) { 1140 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]); 1141 1142 if (!encoder) { 1143 continue; 1144 } 1145 1146 if (encoder->encoder_id == conn->encoder_id) { 1147 break; 1148 } 1149 1150 KMSDRM_drmModeFreeEncoder(encoder); 1151 encoder = NULL; 1152 } 1153 1154 if (!encoder) { 1155 // No encoder was connected, find the first supported one 1156 for (i = 0; i < resources->count_encoders; i++) { 1157 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, 1158 resources->encoders[i]); 1159 1160 if (!encoder) { 1161 continue; 1162 } 1163 1164 for (j = 0; j < conn->count_encoders; j++) { 1165 if (conn->encoders[j] == encoder->encoder_id) { 1166 break; 1167 } 1168 } 1169 1170 if (j != conn->count_encoders) { 1171 break; 1172 } 1173 1174 KMSDRM_drmModeFreeEncoder(encoder); 1175 encoder = NULL; 1176 } 1177 } 1178 1179 if (!encoder) { 1180 ret = SDL_SetError("No connected encoder found for connector."); 1181 goto cleanup; 1182 } 1183 1184 // Try to find a CRTC connected to this encoder 1185 crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); 1186 1187 /* If no CRTC was connected to the encoder, find the first CRTC 1188 that is supported by the encoder, and use that. */ 1189 if (!crtc) { 1190 for (i = 0; i < resources->count_crtcs; i++) { 1191 if (encoder->possible_crtcs & (1 << i)) { 1192 encoder->crtc_id = resources->crtcs[i]; 1193 crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id); 1194 break; 1195 } 1196 } 1197 } 1198 1199 if (!crtc) { 1200 ret = SDL_SetError("No CRTC found for connector."); 1201 goto cleanup; 1202 } 1203 1204 // Find the index of the mode attached to this CRTC 1205 mode_index = -1; 1206 1207 for (i = 0; i < conn->count_modes; i++) { 1208 drmModeModeInfo *mode = &conn->modes[i]; 1209 1210 if (!SDL_memcmp(mode, &crtc->mode, sizeof(crtc->mode))) { 1211 mode_index = i; 1212 break; 1213 } 1214 } 1215 1216 if (mode_index == -1) { 1217 int current_area, largest_area = 0; 1218 1219 // Find the preferred mode or the highest resolution mode 1220 for (i = 0; i < conn->count_modes; i++) { 1221 drmModeModeInfo *mode = &conn->modes[i]; 1222 1223 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 1224 mode_index = i; 1225 break; 1226 } 1227 1228 current_area = mode->hdisplay * mode->vdisplay; 1229 if (current_area > largest_area) { 1230 mode_index = i; 1231 largest_area = current_area; 1232 } 1233 } 1234 if (mode_index != -1) { 1235 crtc->mode = conn->modes[mode_index]; 1236 } 1237 } 1238 1239 if (mode_index == -1) { 1240 ret = SDL_SetError("Failed to find index of mode attached to the CRTC."); 1241 goto cleanup; 1242 } 1243 1244 /*********************************************/ 1245 // Create an SDL Display for this connector. 1246 /*********************************************/ 1247 1248 /*********************************************/ 1249 // Part 1: setup the SDL_Display internal. 1250 /*********************************************/ 1251 1252 /* Get the mode currently setup for this display, 1253 which is the mode currently setup on the CRTC 1254 we found for the active connector. */ 1255 dispdata->mode = crtc->mode; 1256 dispdata->original_mode = crtc->mode; 1257 dispdata->fullscreen_mode = crtc->mode; 1258 1259 if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0) { 1260 ret = SDL_SetError("Couldn't get a valid connector videomode."); 1261 goto cleanup; 1262 } 1263 1264 // Store the connector and crtc for this display. 1265 dispdata->connector.connector = conn; 1266 dispdata->crtc.crtc = crtc; 1267 1268 // save previous vrr state 1269 dispdata->saved_vrr = KMSDRM_CrtcGetVrr(viddata->drm_fd, crtc->crtc_id); 1270 // try to enable vrr 1271 if (KMSDRM_ConnectorCheckVrrCapable(viddata->drm_fd, conn->connector_id)) { 1272 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Enabling VRR"); 1273 KMSDRM_CrtcSetVrr(viddata->drm_fd, crtc->crtc_id, true); 1274 } 1275 1276 // Set the name by the connector type, if possible 1277 if (KMSDRM_drmModeGetConnectorTypeName) { 1278 connector_type = KMSDRM_drmModeGetConnectorTypeName(conn->connector_type); 1279 if (connector_type == NULL) { 1280 connector_type = "Unknown"; 1281 } 1282 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s-%u", connector_type, conn->connector_type_id); 1283 } 1284 1285 dispdata->crtc.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); 1286 dispdata->crtc.props_info = SDL_calloc(dispdata->crtc.props->count_props, sizeof(*dispdata->crtc.props_info)); 1287 if (!dispdata->crtc.props_info) { 1288 ret = false; 1289 goto cleanup; 1290 } 1291 1292 for (i = 0; i < dispdata->crtc.props->count_props; i++) { 1293 dispdata->crtc.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, dispdata->crtc.props->props[i]); 1294 } 1295 1296 /* Get connector properties */ 1297 dispdata->connector.props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd, conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); 1298 dispdata->connector.props_info = SDL_calloc(dispdata->connector.props->count_props, sizeof(*dispdata->connector.props_info)); 1299 if (!dispdata->connector.props_info) { 1300 ret = false; 1301 goto cleanup; 1302 } 1303 1304 for (i = 0; i < dispdata->connector.props->count_props; i++) { 1305 dispdata->connector.props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd, 1306 dispdata->connector.props->props[i]); 1307 } 1308 1309 /*****************************************/ 1310 // Part 2: setup the SDL_Display itself. 1311 /*****************************************/ 1312 1313 /* Setup the display. 1314 There's no problem with it being still incomplete. */ 1315 modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData)); 1316 1317 if (!modedata) { 1318 ret = -1; 1319 goto cleanup; 1320 } 1321 1322 modedata->mode_index = mode_index; 1323 1324 display.internal = dispdata; 1325 display.desktop_mode.w = dispdata->mode.hdisplay; 1326 display.desktop_mode.h = dispdata->mode.vdisplay; 1327 CalculateRefreshRate(&dispdata->mode, &display.desktop_mode.refresh_rate_numerator, &display.desktop_mode.refresh_rate_denominator); 1328 display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888; 1329 display.desktop_mode.internal = modedata; 1330 if (connector_type) { 1331 display.name = name_fmt; 1332 } 1333 1334 // Add the display to the list of SDL displays. 1335 display_id = SDL_AddVideoDisplay(&display, false); 1336 if (!display_id) { 1337 ret = -1; 1338 goto cleanup; 1339 } 1340 1341 orientation = KMSDRM_CrtcGetOrientation(viddata->drm_fd, crtc->crtc_id); 1342 display_properties = SDL_GetDisplayProperties(display_id); 1343 SDL_SetNumberProperty(display_properties, SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER, orientation); 1344 1345#ifdef DEBUG_KMSDRM // Use this if you ever need to see info on all available planes. 1346 get_planes_info(_this, dispdata); 1347#endif 1348 1349cleanup: 1350 if (encoder) { 1351 KMSDRM_drmModeFreeEncoder(encoder); 1352 } 1353 if (ret) { 1354 // Error (complete) cleanup 1355 if (dispdata) { 1356 if (dispdata->connector.connector) { 1357 KMSDRM_drmModeFreeConnector(dispdata->connector.connector); 1358 } 1359 if (dispdata->crtc.crtc) { 1360 KMSDRM_drmModeFreeCrtc(dispdata->crtc.crtc); 1361 } 1362 SDL_free(dispdata->connector.props_info); 1363 SDL_free(dispdata->crtc.props_info); 1364 SDL_free(dispdata->display_plane); 1365 SDL_free(dispdata); 1366 } 1367 } 1368} // NOLINT(clang-analyzer-unix.Malloc): If no error `dispdata` is saved in the display 1369 1370static void KMSDRM_SortDisplays(SDL_VideoDevice *_this) 1371{ 1372 const char *name_hint = SDL_GetHint(SDL_HINT_VIDEO_DISPLAY_PRIORITY); 1373 1374 if (name_hint) { 1375 char *saveptr; 1376 char *str = SDL_strdup(name_hint); 1377 SDL_VideoDisplay **sorted_list = SDL_malloc(sizeof(SDL_VideoDisplay *) * _this->num_displays); 1378 1379 if (str && sorted_list) { 1380 int sorted_index = 0; 1381 1382 // Sort the requested displays to the front of the list. 1383 const char *token = SDL_strtok_r(str, ",", &saveptr); 1384 while (token) { 1385 for (int i = 0; i < _this->num_displays; ++i) { 1386 SDL_VideoDisplay *d = _this->displays[i]; 1387 if (d && SDL_strcmp(token, d->name) == 0) { 1388 sorted_list[sorted_index++] = d; 1389 _this->displays[i] = NULL; 1390 break; 1391 } 1392 } 1393 1394 token = SDL_strtok_r(NULL, ",", &saveptr); 1395 } 1396 1397 // Append the remaining displays to the end of the list. 1398 for (int i = 0; i < _this->num_displays; ++i) { 1399 if (_this->displays[i]) { 1400 sorted_list[sorted_index++] = _this->displays[i]; 1401 } 1402 } 1403 1404 // Copy the sorted list back to the display list. 1405 SDL_memcpy(_this->displays, sorted_list, sizeof(SDL_VideoDisplay *) * _this->num_displays); 1406 } 1407 1408 SDL_free(str); 1409 SDL_free(sorted_list); 1410 } 1411} 1412 1413static bool set_client_atomic_caps(int fd) 1414{ 1415 if (!SDL_GetHintBoolean(SDL_HINT_KMSDRM_ATOMIC, true)) { 1416 return false; // emergency escape hatch. 1417 } else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) { 1418 return false; // no atomic modesetting support. 1419 } else if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { 1420 KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 0); // turn this back off again. 1421 return false; // no universal planes support. 1422 } 1423 return true; 1424} 1425 1426/* Initializes the list of SDL displays: we build a new display for each 1427 connecter connector we find. 1428 This is to be called early, in VideoInit(), because it gets us 1429 the videomode information, which SDL needs immediately after VideoInit(). */ 1430static bool KMSDRM_InitDisplays(SDL_VideoDevice *_this) 1431{ 1432 SDL_VideoData *viddata = _this->internal; 1433 drmModeRes *resources = NULL; 1434 uint64_t async_pageflip = 0; 1435 int i; 1436 bool result = true; 1437 1438 // Open /dev/dri/cardNN (/dev/drmN if on OpenBSD version less than 6.9) 1439 (void)SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "%s%d", 1440 kmsdrm_dri_cardpath, viddata->devindex); 1441 1442 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", viddata->devpath); 1443 viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); 1444 1445 if (viddata->drm_fd < 0) { 1446 result = SDL_SetError("Could not open %s", viddata->devpath); 1447 goto cleanup; 1448 } 1449 1450 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd); 1451 1452 // Set ATOMIC & UNIVERSAL PLANES compatibility 1453 viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); 1454 1455 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "DRM FD (%d) %s atomic", viddata->drm_fd, viddata->is_atomic ? "SUPPORTS" : "DOES NOT SUPPORT"); 1456 1457 // Get all of the available connectors / devices / crtcs 1458 resources = KMSDRM_drmModeGetResources(viddata->drm_fd); 1459 if (!resources) { 1460 result = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd); 1461 goto cleanup; 1462 } 1463 1464 /* Iterate on the available connectors. For every connected connector, 1465 we create an SDL_Display and add it to the list of SDL Displays. */ 1466 for (i = 0; i < resources->count_connectors; i++) { 1467 drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]); 1468 if (!conn) { 1469 continue; 1470 } 1471 1472 if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) { 1473 /* If it's a connected connector with available videomodes, try to add 1474 an SDL Display representing it. KMSDRM_AddDisplay() is purposely void, 1475 so if it fails (no encoder for connector, no valid video mode for 1476 connector etc...) we can keep looking for connected connectors. */ 1477 KMSDRM_AddDisplay(_this, conn, resources); 1478 } else { 1479 // If it's not, free it now. 1480 KMSDRM_drmModeFreeConnector(conn); 1481 } 1482 } 1483 1484 // Have we added any SDL displays? 1485 if (SDL_GetPrimaryDisplay() == 0) { 1486 result = SDL_SetError("No connected displays found."); 1487 goto cleanup; 1488 } 1489 1490 // Sort the displays, if necessary 1491 KMSDRM_SortDisplays(_this); 1492 1493 // Determine if video hardware supports async pageflips. 1494 if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_ASYNC_PAGE_FLIP, &async_pageflip) != 0) { 1495 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not determine async page flip capability."); 1496 } 1497 viddata->async_pageflip_support = async_pageflip ? true : false; 1498 1499 /***********************************/ 1500 // Block for Vulkan compatibility. 1501 /***********************************/ 1502 1503 /* Vulkan requires DRM master on its own FD to work, so try to drop master 1504 on our FD. This will only work without root on kernels v5.8 and later. 1505 If it doesn't work, just close the FD and we'll reopen it later. */ 1506 if (!KMSDRM_DropMaster(_this)) { 1507 close(viddata->drm_fd); 1508 viddata->drm_fd = -1; 1509 } 1510 1511cleanup: 1512 if (resources) { 1513 KMSDRM_drmModeFreeResources(resources); 1514 } 1515 if (!result) { 1516 if (viddata->drm_fd >= 0) { 1517 close(viddata->drm_fd); 1518 viddata->drm_fd = -1; 1519 } 1520 } 1521 return result; 1522} 1523 1524/* Init the Vulkan-INCOMPATIBLE stuff: 1525 Reopen FD, create gbm dev, create dumb buffer and setup display plane. 1526 This is to be called late, in WindowCreate(), and ONLY if this is not 1527 a Vulkan window. 1528 We are doing this so late to allow Vulkan to work if we build a VK window. 1529 These things are incompatible with Vulkan, which accesses the same resources 1530 internally so they must be free when trying to build a Vulkan surface. 1531*/ 1532static bool KMSDRM_GBMInit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 1533{ 1534 SDL_VideoData *viddata = _this->internal; 1535 bool result = true; 1536 1537 // Reopen the FD if we weren't able to drop master on the original one 1538 if (viddata->drm_fd < 0) { 1539 viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC); 1540 if (viddata->drm_fd < 0) { 1541 return SDL_SetError("Could not reopen %s", viddata->devpath); 1542 } 1543 } 1544 1545 viddata->is_atomic = set_client_atomic_caps(viddata->drm_fd); 1546 1547 // Set the FD as current DRM master. 1548 KMSDRM_drmSetMaster(viddata->drm_fd); 1549 1550 // Create the GBM device. 1551 viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd); 1552 if (!viddata->gbm_dev) { 1553 result = SDL_SetError("Couldn't create gbm device."); 1554 } else { 1555 // Setup the display plane. ONLY do this after dispdata has the right 1556 // crtc and connector, because these are used in this function. 1557 result = setup_plane(_this, dispdata, &dispdata->display_plane, DRM_PLANE_TYPE_PRIMARY); 1558 if (!result) { 1559 SDL_SetError("can't find suitable display plane."); 1560 } 1561 } 1562 1563 viddata->gbm_init = true; 1564 1565 return result; 1566} 1567 1568// Deinit the Vulkan-incompatible KMSDRM stuff. 1569static void KMSDRM_GBMDeinit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata) 1570{ 1571 SDL_VideoData *viddata = _this->internal; 1572 1573 // Free display plane 1574 free_plane(&dispdata->display_plane); 1575 1576 // Free cursor plane (if still not freed) 1577 free_plane(&dispdata->cursor_plane); 1578 1579 /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(), 1580 already called when we get here. */ 1581 if (viddata->gbm_dev) { 1582 KMSDRM_gbm_device_destroy(viddata->gbm_dev); 1583 viddata->gbm_dev = NULL; 1584 } 1585 1586 /* Finally drop DRM master if possible, otherwise close DRM FD. 1587 May be reopened on next non-vulkan window creation. */ 1588 if (viddata->drm_fd >= 0 && !KMSDRM_DropMaster(_this)) { 1589 close(viddata->drm_fd); 1590 viddata->drm_fd = -1; 1591 } 1592 1593 viddata->gbm_init = false; 1594} 1595 1596static void KMSDRM_DestroySurfaces(SDL_VideoDevice *_this, SDL_Window *window) 1597{ 1598 SDL_VideoData *viddata = _this->internal; 1599 SDL_WindowData *windata = window->internal; 1600 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 1601 int ret; 1602 1603 /**********************************************/ 1604 // Wait for last issued pageflip to complete. 1605 /**********************************************/ 1606 // KMSDRM_WaitPageflip(_this, windata); 1607 1608 if (viddata->is_atomic) { 1609 1610 /* TODO : Continue investigating why this doesn't work. We should do this instead 1611 of making the display plane point to the TTY console, which isn't there 1612 after creating and destroying a Vulkan window. */ 1613 1614#if 0 // (note that this code has bitrotted a little, in addition to TODO comment above.) 1615 /* Disconnect the connector from the CRTC (remember: several connectors 1616 can read a CRTC), deactivate the CRTC, and set the PRIMARY PLANE props 1617 CRTC_ID and FB_ID to 0. Then we can destroy the GBM buffers and surface. */ 1618 add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0); 1619 add_crtc_property(dispdata->atomic_req, dispdata->crtc , "MODE_ID", 0); 1620 add_crtc_property(dispdata->atomic_req, dispdata->crtc , "active", 0); 1621 /**********************************************/ 1622 /* Wait for last issued pageflip to complete. */ 1623 /**********************************************/ 1624 KMSDRM_WaitPageFlip(_this, windata, -1); 1625 1626 plane_info.plane = dispdata->display_plane; 1627 plane_info.crtc_id = 0; 1628 plane_info.fb_id = 0; 1629 /************************************************************************/ 1630 /* Restore the original CRTC configuration: configure the crtc with the */ 1631 /* original video mode and make it point to the original TTY buffer. */ 1632 /************************************************************************/ 1633 1634 drm_atomic_set_plane_props(dispdata, &plane_info); 1635 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, 1636 dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, 1637 &dispdata->original_mode); 1638 1639 /* Issue atomic commit that is blocking and allows modesetting. */ 1640 if (drm_atomic_commit(_this, dispdata, true, true)) { 1641 SDL_SetError("Failed to issue atomic commit on surfaces destruction."); 1642 /* If we failed to set the original mode, try to set the connector preferred mode. */ 1643 if (ret && (dispdata->crtc->mode_valid == 0)) { 1644 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc->crtc_id, 1645 dispdata->crtc->buffer_id, 0, 0, &dispdata->connector->connector_id, 1, 1646 &dispdata->original_mode); 1647 } 1648#endif 1649 1650#if 1 1651 /************************************************************/ 1652 /* Make the display plane point to the original TTY buffer. */ 1653 /* We have to configure it's input and output scaling */ 1654 /* parameters accordingly. */ 1655 /************************************************************/ 1656 1657 KMSDRM_PlaneInfo plane_info; 1658 SDL_zero(plane_info); 1659 plane_info.plane = dispdata->display_plane; 1660 plane_info.crtc_id = dispdata->crtc.crtc->crtc_id; 1661 plane_info.fb_id = dispdata->crtc.crtc->buffer_id; 1662 plane_info.src_w = dispdata->original_mode.hdisplay; 1663 plane_info.src_h = dispdata->original_mode.vdisplay; 1664 plane_info.crtc_w = dispdata->original_mode.hdisplay; 1665 plane_info.crtc_h = dispdata->original_mode.vdisplay; 1666 1667 drm_atomic_set_plane_props(dispdata, &plane_info); 1668 1669 if (drm_atomic_commit(_this, dispdata, true, false)) { 1670 SDL_SetError("Failed to issue atomic commit on surfaces destruction."); 1671 } 1672 } 1673#endif 1674 1675 /************************************************************************/ 1676 // Restore the original CRTC configuration: configure the crtc with the 1677 // original video mode and make it point to the original TTY buffer. 1678 /************************************************************************/ 1679 1680 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 1681 dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, 1682 &dispdata->original_mode); 1683 1684 // If we failed to set the original mode, try to set the connector preferred mode. 1685 if (ret && (dispdata->crtc.crtc->mode_valid == 0)) { 1686 ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 1687 dispdata->crtc.crtc->buffer_id, 0, 0, &dispdata->connector.connector->connector_id, 1, 1688 &dispdata->original_mode); 1689 } 1690 1691 if (ret) { 1692 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not restore CRTC"); 1693 } 1694 1695 /***************************/ 1696 // Destroy the GBM buffers 1697 /***************************/ 1698 1699 if (windata->bo) { 1700 if (windata->bo != windata->next_bo) { 1701 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); 1702 } 1703 windata->bo = NULL; 1704 } 1705 1706 if (windata->next_bo) { 1707 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo); 1708 windata->next_bo = NULL; 1709 } 1710 1711 /***************************/ 1712 // Destroy the EGL surface 1713 /***************************/ 1714 1715 SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1716 1717 if (windata->egl_surface != EGL_NO_SURFACE) { 1718 SDL_EGL_DestroySurface(_this, windata->egl_surface); 1719 windata->egl_surface = EGL_NO_SURFACE; 1720 } 1721 1722 /***************************/ 1723 // Destroy the GBM surface 1724 /***************************/ 1725 1726 if (windata->gs) { 1727 KMSDRM_gbm_surface_destroy(windata->gs); 1728 windata->gs = NULL; 1729 } 1730} 1731 1732static void KMSDRM_GetModeToSet(SDL_Window *window, drmModeModeInfo *out_mode) 1733{ 1734 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 1735 SDL_DisplayData *dispdata = display->internal; 1736 1737 if (window->fullscreen_exclusive) { 1738 *out_mode = dispdata->fullscreen_mode; 1739 } else { 1740 drmModeModeInfo *mode = KMSDRM_GetClosestDisplayMode(display, window->windowed.w, window->windowed.h); 1741 if (mode) { 1742 *out_mode = *mode; 1743 } else { 1744 *out_mode = dispdata->original_mode; 1745 } 1746 } 1747} 1748 1749static void KMSDRM_DirtySurfaces(SDL_Window *window) 1750{ 1751 SDL_WindowData *windata = window->internal; 1752 drmModeModeInfo mode; 1753 1754 /* Can't recreate EGL surfaces right now, need to wait until SwapWindow 1755 so the correct thread-local surface and context state are available */ 1756 windata->egl_surface_dirty = true; 1757 1758 /* The app may be waiting for the resize event after calling SetWindowSize 1759 or SetWindowFullscreen, send a fake event for now since the actual 1760 recreation is deferred */ 1761 KMSDRM_GetModeToSet(window, &mode); 1762 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode.hdisplay, mode.vdisplay); 1763} 1764 1765/* This determines the size of the fb, which comes from the GBM surface 1766 that we create here. */ 1767bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window) 1768{ 1769 SDL_VideoData *viddata = _this->internal; 1770 SDL_WindowData *windata = window->internal; 1771 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 1772 SDL_DisplayData *dispdata = display->internal; 1773 1774 uint32_t surface_fmt = GBM_FORMAT_ARGB8888; 1775 uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; 1776 1777 EGLContext egl_context; 1778 1779 bool result = true; 1780 1781 // If the current window already has surfaces, destroy them before creating other. 1782 if (windata->gs) { 1783 KMSDRM_DestroySurfaces(_this, window); 1784 } 1785 1786 if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, 1787 surface_fmt, surface_flags)) { 1788 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, 1789 "GBM surface format not supported. Trying anyway."); 1790 } 1791 1792 /* The KMSDRM backend doesn't always set the mode the higher-level code in 1793 SDL_video.c expects. Hulk-smash the display's current_mode to keep the 1794 mode that's set in sync with what SDL_video.c thinks is set 1795 1796 FIXME: How do we do that now? Can we get a better idea at the higher level? 1797 */ 1798 KMSDRM_GetModeToSet(window, &dispdata->mode); 1799 1800 windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, 1801 dispdata->mode.hdisplay, dispdata->mode.vdisplay, 1802 surface_fmt, surface_flags); 1803 if (!windata->gs && errno == ENOSYS) { 1804 // Try again without the scanout flags, needed on NVIDIA drivers 1805 windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, 1806 dispdata->mode.hdisplay, dispdata->mode.vdisplay, 1807 surface_fmt, 0); 1808 } 1809 if (!windata->gs) { 1810 return SDL_SetError("Could not create GBM surface: %s", strerror(errno)); 1811 } 1812 1813 /* We can't get the EGL context yet because SDL_CreateRenderer has not been called, 1814 but we need an EGL surface NOW, or GL won't be able to render into any surface 1815 and we won't see the first frame. */ 1816 SDL_EGL_SetRequiredVisualId(_this, surface_fmt); 1817 windata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)windata->gs); 1818 1819 if (windata->egl_surface == EGL_NO_SURFACE) { 1820 result = SDL_SetError("Could not create EGL window surface"); 1821 goto cleanup; 1822 } 1823 1824 /* Current context passing to EGL is now done here. If something fails, 1825 go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */ 1826 egl_context = (EGLContext)SDL_GL_GetCurrentContext(); 1827 result = SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); 1828 1829 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, 1830 dispdata->mode.hdisplay, dispdata->mode.vdisplay); 1831 1832 windata->egl_surface_dirty = false; 1833 1834cleanup: 1835 1836 if (!result) { 1837 // Error (complete) cleanup. 1838 if (windata->gs) { 1839 KMSDRM_gbm_surface_destroy(windata->gs); 1840 windata->gs = NULL; 1841 } 1842 } 1843 1844 return result; 1845} 1846 1847#ifdef SDL_INPUT_LINUXEV 1848static void KMSDRM_ReleaseVT(void *userdata) 1849{ 1850 SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata; 1851 SDL_VideoData *viddata = _this->internal; 1852 int i; 1853 1854 for (i = 0; i < viddata->num_windows; i++) { 1855 SDL_Window *window = viddata->windows[i]; 1856 if (!(window->flags & SDL_WINDOW_VULKAN)) { 1857 KMSDRM_DestroySurfaces(_this, window); 1858 } 1859 } 1860 KMSDRM_drmDropMaster(viddata->drm_fd); 1861} 1862 1863static void KMSDRM_AcquireVT(void *userdata) 1864{ 1865 SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata; 1866 SDL_VideoData *viddata = _this->internal; 1867 int i; 1868 1869 KMSDRM_drmSetMaster(viddata->drm_fd); 1870 for (i = 0; i < viddata->num_windows; i++) { 1871 SDL_Window *window = viddata->windows[i]; 1872 if (!(window->flags & SDL_WINDOW_VULKAN)) { 1873 KMSDRM_CreateSurfaces(_this, window); 1874 } 1875 } 1876} 1877#endif // defined SDL_INPUT_LINUXEV 1878 1879bool KMSDRM_VideoInit(SDL_VideoDevice *_this) 1880{ 1881 bool result = true; 1882 1883 SDL_VideoData *viddata = _this->internal; 1884 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()"); 1885 1886 viddata->video_init = false; 1887 viddata->gbm_init = false; 1888 1889 /* Get KMSDRM resources info and store what we need. Getting and storing 1890 this info isn't a problem for VK compatibility. 1891 For VK-incompatible initializations we have KMSDRM_GBMInit(), which is 1892 called on window creation, and only when we know it's not a VK window. */ 1893 if (!KMSDRM_InitDisplays(_this)) { 1894 result = SDL_SetError("error getting KMSDRM displays information"); 1895 } 1896 1897#ifdef SDL_INPUT_LINUXEV 1898 SDL_EVDEV_Init(); 1899 SDL_EVDEV_SetVTSwitchCallbacks(KMSDRM_ReleaseVT, _this, KMSDRM_AcquireVT, _this); 1900#elif defined(SDL_INPUT_WSCONS) 1901 SDL_WSCONS_Init(); 1902#endif 1903 1904 viddata->video_init = true; 1905 1906 return result; 1907} 1908 1909/* The internal pointers, like dispdata, viddata, windata, etc... 1910 are freed by SDL internals, so not our job. */ 1911void KMSDRM_VideoQuit(SDL_VideoDevice *_this) 1912{ 1913 SDL_VideoData *viddata = _this->internal; 1914 1915 KMSDRM_DeinitDisplays(_this); 1916 1917#ifdef SDL_INPUT_LINUXEV 1918 SDL_EVDEV_SetVTSwitchCallbacks(NULL, NULL, NULL, NULL); 1919 SDL_EVDEV_Quit(); 1920#elif defined(SDL_INPUT_WSCONS) 1921 SDL_WSCONS_Quit(); 1922#endif 1923 1924 // Clear out the window list 1925 SDL_free(viddata->windows); 1926 viddata->windows = NULL; 1927 viddata->max_windows = 0; 1928 viddata->num_windows = 0; 1929 viddata->video_init = false; 1930} 1931 1932// Read modes from the connector modes, and store them in display->display_modes. 1933bool KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 1934{ 1935 SDL_DisplayData *dispdata = display->internal; 1936 drmModeConnector *conn = dispdata->connector.connector; 1937 SDL_DisplayMode mode; 1938 int i; 1939 1940 for (i = 0; i < conn->count_modes; i++) { 1941 SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData)); 1942 1943 if (modedata) { 1944 modedata->mode_index = i; 1945 } 1946 1947 SDL_zero(mode); 1948 mode.w = conn->modes[i].hdisplay; 1949 mode.h = conn->modes[i].vdisplay; 1950 CalculateRefreshRate(&conn->modes[i], &mode.refresh_rate_numerator, &mode.refresh_rate_denominator); 1951 mode.format = SDL_PIXELFORMAT_ARGB8888; 1952 mode.internal = modedata; 1953 1954 if (!SDL_AddFullscreenDisplayMode(display, &mode)) { 1955 SDL_free(modedata); 1956 } 1957 } 1958 return true; 1959} 1960 1961bool KMSDRM_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 1962{ 1963 /* Set the dispdata->mode to the new mode and leave actual modesetting 1964 pending to be done on SwapWindow() via drmModeSetCrtc() */ 1965 1966 SDL_VideoData *viddata = _this->internal; 1967 SDL_DisplayData *dispdata = display->internal; 1968 SDL_DisplayModeData *modedata = mode->internal; 1969 drmModeConnector *conn = dispdata->connector.connector; 1970 int i; 1971 1972 // Don't do anything if we are in Vulkan mode. 1973 if (viddata->vulkan_mode) { 1974 return true; 1975 } 1976 1977 if (!modedata) { 1978 return SDL_SetError("Mode doesn't have an associated index"); 1979 } 1980 1981 /* Take note of the new mode to be set, and leave the CRTC modeset pending 1982 so it's done in SwapWindow. */ 1983 dispdata->fullscreen_mode = conn->modes[modedata->mode_index]; 1984 1985 for (i = 0; i < viddata->num_windows; i++) { 1986 KMSDRM_DirtySurfaces(viddata->windows[i]); 1987 } 1988 1989 return true; 1990} 1991 1992void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) 1993{ 1994 SDL_WindowData *windata = window->internal; 1995 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 1996 SDL_VideoData *viddata; 1997 bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; // Is this a VK window? 1998 unsigned int i, j; 1999 2000 if (!windata) { 2001 return; 2002 } 2003 2004 // restore vrr state 2005 KMSDRM_CrtcSetVrr(windata->viddata->drm_fd, dispdata->crtc.crtc->crtc_id, dispdata->saved_vrr); 2006 2007 viddata = windata->viddata; 2008 2009 if (!is_vulkan && viddata->gbm_init) { 2010 2011 // Destroy cursor GBM BO of the display of this window. 2012 KMSDRM_DestroyCursorBO(_this, SDL_GetVideoDisplayForWindow(window)); 2013 2014 // Destroy GBM surface and buffers. 2015 KMSDRM_DestroySurfaces(_this, window); 2016 2017 /* Unload library and deinit GBM, but only if this is the last window. 2018 Note that this is the right comparison because num_windows could be 1 2019 if there is a complete window, or 0 if we got here from SDL_CreateWindow() 2020 because KMSDRM_CreateWindow() returned an error so the window wasn't 2021 added to the windows list. */ 2022 if (viddata->num_windows <= 1) { 2023 2024 // Unload EGL/GL library and free egl_data. 2025 if (_this->egl_data) { 2026 SDL_EGL_UnloadLibrary(_this); 2027 _this->gl_config.driver_loaded = 0; 2028 } 2029 2030 // Free display plane, and destroy GBM device. 2031 KMSDRM_GBMDeinit(_this, dispdata); 2032 } 2033 2034 } else { 2035 2036 // If we were in Vulkan mode, get out of it. 2037 if (viddata->vulkan_mode) { 2038 viddata->vulkan_mode = false; 2039 } 2040 } 2041 2042 /********************************************/ 2043 // Remove from the internal SDL window list 2044 /********************************************/ 2045 2046 for (i = 0; i < viddata->num_windows; i++) { 2047 if (viddata->windows[i] == window) { 2048 viddata->num_windows--; 2049 2050 for (j = i; j < viddata->num_windows; j++) { 2051 viddata->windows[j] = viddata->windows[j + 1]; 2052 } 2053 2054 break; 2055 } 2056 } 2057 2058 /*********************************************************************/ 2059 // Free the window internal. Bye bye, surface and buffer pointers! 2060 /*********************************************************************/ 2061 SDL_free(window->internal); 2062 window->internal = NULL; 2063} 2064 2065/**********************************************************************/ 2066// We simply IGNORE if it's a fullscreen window, window->flags don't 2067// reflect it: if it's fullscreen, KMSDRM_SetWindowFullscreen() will 2068// be called by SDL later, and we can manage it there. 2069/**********************************************************************/ 2070bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) 2071{ 2072 SDL_WindowData *windata = NULL; 2073 SDL_VideoData *viddata = _this->internal; 2074 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 2075 SDL_DisplayData *dispdata = display->internal; 2076 bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; // Is this a VK window? 2077 bool vulkan_mode = viddata->vulkan_mode; // Do we have any Vulkan windows? 2078 NativeDisplayType egl_display; 2079 drmModeModeInfo *mode; 2080 bool result = true; 2081 2082 // Allocate window internal data 2083 windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); 2084 if (!windata) { 2085 return false; 2086 } 2087 2088 // Setup driver data for this window 2089 windata->viddata = viddata; 2090 window->internal = windata; 2091 2092 // Do we want a double buffering scheme to get low video lag? 2093 windata->double_buffer = false; 2094 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) { 2095 windata->double_buffer = true; 2096 } 2097 2098 if (!is_vulkan && !vulkan_mode) { // NON-Vulkan block. 2099 2100 /* Maybe you didn't ask for an OPENGL window, but that's what you will get. 2101 See following comments on why. */ 2102 window->flags |= SDL_WINDOW_OPENGL; 2103 2104 if (!(viddata->gbm_init)) { 2105 2106 /* After SDL_CreateWindow, most SDL programs will do SDL_CreateRenderer(), 2107 which will in turn call GL_CreateRenderer() or GLES2_CreateRenderer(). 2108 In order for the GL_CreateRenderer() or GLES2_CreateRenderer() call to 2109 succeed without an unnecessary window re-creation, we must: 2110 -Mark the window as being OPENGL 2111 -Load the GL library (which can't be done until the GBM device has been 2112 created, so we have to do it here instead of doing it on VideoInit()) 2113 and mark it as loaded by setting gl_config.driver_loaded to 1. 2114 So if you ever see KMSDRM_CreateWindow() to be called two times in tests, 2115 don't be shy to debug GL_CreateRenderer() or GLES2_CreateRenderer() 2116 to find out why! 2117 */ 2118 2119 /* Reopen FD, create gbm dev, setup display plane, etc,. 2120 but only when we come here for the first time, 2121 and only if it's not a VK window. */ 2122 if (!KMSDRM_GBMInit(_this, dispdata)) { 2123 return SDL_SetError("Can't init GBM on window creation."); 2124 } 2125 } 2126 2127 /* Manually load the GL library. KMSDRM_EGL_LoadLibrary() has already 2128 been called by SDL_CreateWindow() but we don't do anything there, 2129 our KMSDRM_EGL_LoadLibrary() is a dummy precisely to be able to load it here. 2130 If we let SDL_CreateWindow() load the lib, it would be loaded 2131 before we call KMSDRM_GBMInit(), causing all GLES programs to fail. */ 2132 if (!_this->egl_data) { 2133 egl_display = (NativeDisplayType)_this->internal->gbm_dev; 2134 if (!SDL_EGL_LoadLibrary(_this, NULL, egl_display)) { 2135 // Try again with OpenGL ES 2.0 2136 _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES; 2137 _this->gl_config.major_version = 2; 2138 _this->gl_config.minor_version = 0; 2139 if (!SDL_EGL_LoadLibrary(_this, NULL, egl_display)) { 2140 return SDL_SetError("Can't load EGL/GL library on window creation."); 2141 } 2142 } 2143 2144 _this->gl_config.driver_loaded = 1; 2145 } 2146 2147 /* Create the cursor BO for the display of this window, 2148 now that we know this is not a VK window. */ 2149 KMSDRM_CreateCursorBO(display); 2150 2151 /* Create and set the default cursor for the display 2152 of this window, now that we know this is not a VK window. */ 2153 KMSDRM_InitMouse(_this, display); 2154 2155 /* The FULLSCREEN flags are cut out from window->flags at this point, 2156 so we can't know if a window is fullscreen or not, hence all windows 2157 are considered "windowed" at this point of their life. 2158 If a window is fullscreen, SDL internals will call 2159 KMSDRM_SetWindowFullscreen() to reconfigure it if necessary. */ 2160 mode = KMSDRM_GetClosestDisplayMode(display, window->windowed.w, window->windowed.h); 2161 2162 if (mode) { 2163 dispdata->fullscreen_mode = *mode; 2164 } else { 2165 dispdata->fullscreen_mode = dispdata->original_mode; 2166 } 2167 2168 /* Create the window surfaces with the size we have just chosen. 2169 Needs the window driverdata in place. */ 2170 if (!KMSDRM_CreateSurfaces(_this, window)) { 2171 return false; 2172 } 2173 } // NON-Vulkan block ends. 2174 2175 /* Add window to the internal list of tracked windows. Note, while it may 2176 seem odd to support multiple fullscreen windows, some apps create an 2177 extra window as a dummy surface when working with multiple contexts */ 2178 if (viddata->num_windows >= viddata->max_windows) { 2179 unsigned int new_max_windows = viddata->max_windows + 1; 2180 SDL_Window **new_windows = (SDL_Window **)SDL_realloc(viddata->windows, 2181 new_max_windows * sizeof(SDL_Window *)); 2182 if (!new_windows) { 2183 return false; 2184 } 2185 viddata->windows = new_windows; 2186 viddata->max_windows = new_max_windows; 2187 2188 } 2189 2190 viddata->windows[viddata->num_windows++] = window; 2191 2192 // If we have just created a Vulkan window, establish that we are in Vulkan mode now. 2193 viddata->vulkan_mode = is_vulkan; 2194 2195 SDL_PropertiesID props = SDL_GetWindowProperties(window); 2196 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER, viddata->devindex); 2197 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER, viddata->drm_fd); 2198 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER, viddata->gbm_dev); 2199 2200 if ((window->flags & SDL_WINDOW_NOT_FOCUSABLE) == 0) { 2201 /* Focus on the newly created window. 2202 SDL_SetMouseFocus() also takes care of calling KMSDRM_ShowCursor() if necessary. */ 2203 SDL_SetMouseFocus(window); 2204 SDL_SetKeyboardFocus(window); 2205 } 2206 2207 // Tell the app that the window has moved to top-left. 2208 SDL_Rect display_bounds; 2209 SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &display_bounds); 2210 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display_bounds.x, display_bounds.y); 2211 2212 /* Allocated windata will be freed in KMSDRM_DestroyWindow, 2213 and KMSDRM_DestroyWindow() will be called by SDL_CreateWindow() 2214 if we return error on any of the previous returns of the function. */ 2215 return result; 2216} 2217 2218void KMSDRM_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) 2219{ 2220} 2221bool KMSDRM_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 2222{ 2223 return SDL_Unsupported(); 2224} 2225void KMSDRM_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 2226{ 2227 SDL_VideoData *viddata = _this->internal; 2228 if (!viddata->vulkan_mode) { 2229 KMSDRM_DirtySurfaces(window); 2230 } 2231} 2232SDL_FullscreenResult KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen) 2233{ 2234 SDL_VideoData *viddata = _this->internal; 2235 if (!viddata->vulkan_mode) { 2236 KMSDRM_DirtySurfaces(window); 2237 } 2238 return SDL_FULLSCREEN_SUCCEEDED; 2239} 2240void KMSDRM_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) 2241{ 2242} 2243void KMSDRM_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) 2244{ 2245} 2246void KMSDRM_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) 2247{ 2248} 2249void KMSDRM_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 2250{ 2251} 2252void KMSDRM_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 2253{ 2254} 2255void KMSDRM_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) 2256{ 2257} 2258bool KMSDRM_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) 2259{ 2260 return true; // this just has to exist or SDL_SetWindowFocusable() will refuse to change the window flag. 2261} 2262 2263#endif // SDL_VIDEO_DRIVER_KMSDRM 2264
[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.