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