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