1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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 #if SDL_VIDEO_DRIVER_KMSDRM
25
26 /* SDL internals */
27 #include "../SDL_sysvideo.h"
28 #include "SDL_syswm.h"
29 #include "SDL_hints.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 <sys/stat.h>
45 #include <dirent.h>
46 #include <errno.h>
47 #include <poll.h>
48
49 #define KMSDRM_DRI_PATH "/dev/dri/"
50
51 static int
check_modestting(int devindex)52 check_modestting(int devindex)
53 {
54 SDL_bool available = SDL_FALSE;
55 char device[512];
56 int drm_fd;
57
58 SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
59
60 drm_fd = open(device, O_RDWR | O_CLOEXEC);
61 if (drm_fd >= 0) {
62 if (SDL_KMSDRM_LoadSymbols()) {
63 drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
64 if (resources) {
65 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
66 KMSDRM_DRI_PATH, devindex,
67 resources->count_connectors, resources->count_encoders, resources->count_crtcs);
68
69 if (resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) {
70 available = SDL_TRUE;
71 }
72 KMSDRM_drmModeFreeResources(resources);
73 }
74 SDL_KMSDRM_UnloadSymbols();
75 }
76 close(drm_fd);
77 }
78
79 return available;
80 }
81
get_dricount(void)82 static int get_dricount(void)
83 {
84 int devcount = 0;
85 struct dirent *res;
86 struct stat sb;
87 DIR *folder;
88
89 if (!(stat(KMSDRM_DRI_PATH, &sb) == 0
90 && S_ISDIR(sb.st_mode))) {
91 printf("The path %s cannot be opened or is not available\n",
92 KMSDRM_DRI_PATH);
93 return 0;
94 }
95
96 if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
97 printf("The path %s cannot be opened\n",
98 KMSDRM_DRI_PATH);
99 return 0;
100 }
101
102 folder = opendir(KMSDRM_DRI_PATH);
103 if (folder) {
104 while ((res = readdir(folder))) {
105 int len = SDL_strlen(res->d_name);
106 if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
107 devcount++;
108 }
109 }
110 closedir(folder);
111 }
112
113 return devcount;
114 }
115
116 static int
get_driindex(void)117 get_driindex(void)
118 {
119 const int devcount = get_dricount();
120 int i;
121
122 for (i = 0; i < devcount; i++) {
123 if (check_modestting(i)) {
124 return i;
125 }
126 }
127
128 return -ENOENT;
129 }
130
131 static int
KMSDRM_Available(void)132 KMSDRM_Available(void)
133 {
134 int ret = -ENOENT;
135
136 ret = get_driindex();
137 if (ret >= 0)
138 return 1;
139
140 return ret;
141 }
142
143 static void
KMSDRM_DeleteDevice(SDL_VideoDevice * device)144 KMSDRM_DeleteDevice(SDL_VideoDevice * device)
145 {
146 if (device->driverdata) {
147 SDL_free(device->driverdata);
148 device->driverdata = NULL;
149 }
150
151 SDL_free(device);
152
153 SDL_KMSDRM_UnloadSymbols();
154 }
155
156 static SDL_VideoDevice *
KMSDRM_CreateDevice(int devindex)157 KMSDRM_CreateDevice(int devindex)
158 {
159 SDL_VideoDevice *device;
160 SDL_VideoData *viddata;
161
162 if (!devindex || (devindex > 99)) {
163 devindex = get_driindex();
164 }
165
166 if (devindex < 0) {
167 SDL_SetError("devindex (%d) must be between 0 and 99.\n", devindex);
168 return NULL;
169 }
170
171 if (!SDL_KMSDRM_LoadSymbols()) {
172 return NULL;
173 }
174
175 device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
176 if (!device) {
177 SDL_OutOfMemory();
178 return NULL;
179 }
180
181 viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
182 if (!viddata) {
183 SDL_OutOfMemory();
184 goto cleanup;
185 }
186 viddata->devindex = devindex;
187 viddata->drm_fd = -1;
188
189 device->driverdata = viddata;
190
191 /* Setup all functions that can be handled from this backend. */
192 device->VideoInit = KMSDRM_VideoInit;
193 device->VideoQuit = KMSDRM_VideoQuit;
194 device->GetDisplayModes = KMSDRM_GetDisplayModes;
195 device->SetDisplayMode = KMSDRM_SetDisplayMode;
196 device->CreateSDLWindow = KMSDRM_CreateWindow;
197 device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
198 device->SetWindowTitle = KMSDRM_SetWindowTitle;
199 device->SetWindowIcon = KMSDRM_SetWindowIcon;
200 device->SetWindowPosition = KMSDRM_SetWindowPosition;
201 device->SetWindowSize = KMSDRM_SetWindowSize;
202 device->ShowWindow = KMSDRM_ShowWindow;
203 device->HideWindow = KMSDRM_HideWindow;
204 device->RaiseWindow = KMSDRM_RaiseWindow;
205 device->MaximizeWindow = KMSDRM_MaximizeWindow;
206 device->MinimizeWindow = KMSDRM_MinimizeWindow;
207 device->RestoreWindow = KMSDRM_RestoreWindow;
208 device->SetWindowGrab = KMSDRM_SetWindowGrab;
209 device->DestroyWindow = KMSDRM_DestroyWindow;
210 device->GetWindowWMInfo = KMSDRM_GetWindowWMInfo;
211 #if SDL_VIDEO_OPENGL_EGL
212 device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
213 device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
214 device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
215 device->GL_CreateContext = KMSDRM_GLES_CreateContext;
216 device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
217 device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
218 device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
219 device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
220 device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
221 #endif
222 device->PumpEvents = KMSDRM_PumpEvents;
223 device->free = KMSDRM_DeleteDevice;
224
225 return device;
226
227 cleanup:
228 if (device)
229 SDL_free(device);
230 if (viddata)
231 SDL_free(viddata);
232 return NULL;
233 }
234
235 VideoBootStrap KMSDRM_bootstrap = {
236 "KMSDRM",
237 "KMS/DRM Video Driver",
238 KMSDRM_Available,
239 KMSDRM_CreateDevice
240 };
241
242
243 static void
KMSDRM_FBDestroyCallback(struct gbm_bo * bo,void * data)244 KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
245 {
246 KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
247
248 if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
249 KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
250 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
251 }
252
253 SDL_free(fb_info);
254 }
255
256 KMSDRM_FBInfo *
KMSDRM_FBFromBO(_THIS,struct gbm_bo * bo)257 KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
258 {
259 SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
260 unsigned w,h;
261 int ret;
262 Uint32 stride, handle;
263
264 /* Check for an existing framebuffer */
265 KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
266
267 if (fb_info) {
268 return fb_info;
269 }
270
271 /* Create a structure that contains enough info to remove the framebuffer
272 when the backing buffer is destroyed */
273 fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
274
275 if (!fb_info) {
276 SDL_OutOfMemory();
277 return NULL;
278 }
279
280 fb_info->drm_fd = viddata->drm_fd;
281
282 /* Create framebuffer object for the buffer */
283 w = KMSDRM_gbm_bo_get_width(bo);
284 h = KMSDRM_gbm_bo_get_height(bo);
285 stride = KMSDRM_gbm_bo_get_stride(bo);
286 handle = KMSDRM_gbm_bo_get_handle(bo).u32;
287 ret = KMSDRM_drmModeAddFB(viddata->drm_fd, w, h, 24, 32, stride, handle,
288 &fb_info->fb_id);
289 if (ret) {
290 SDL_free(fb_info);
291 return NULL;
292 }
293
294 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "New DRM FB (%u): %ux%u, stride %u from BO %p",
295 fb_info->fb_id, w, h, stride, (void *)bo);
296
297 /* Associate our DRM framebuffer with this buffer object */
298 KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
299
300 return fb_info;
301 }
302
303 static void
KMSDRM_FlipHandler(int fd,unsigned int frame,unsigned int sec,unsigned int usec,void * data)304 KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
305 {
306 /* If the data pointer received here is the same passed as the user_data in drmModePageFlip()
307 then this is the event handler for the pageflip that was issued on drmPageFlip(): got here
308 because of that precise page flip, the while loop gets broken here because of the right event.
309 This knowledge will allow handling different issued pageflips if sometime in the future
310 managing different CRTCs in SDL2 is needed, for example (synchronous pageflips happen on vblank
311 and vblank is a CRTC thing). */
312 *((SDL_bool *) data) = SDL_FALSE;
313 }
314
315 SDL_bool
KMSDRM_WaitPageFlip(_THIS,SDL_WindowData * windata,int timeout)316 KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
317 SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
318 drmEventContext ev = {0};
319 struct pollfd pfd = {0};
320
321 ev.version = DRM_EVENT_CONTEXT_VERSION;
322 ev.page_flip_handler = KMSDRM_FlipHandler;
323
324 pfd.fd = viddata->drm_fd;
325 pfd.events = POLLIN;
326
327 while (windata->waiting_for_flip) {
328 pfd.revents = 0;
329
330 if (poll(&pfd, 1, timeout) < 0) {
331 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
332 return SDL_FALSE;
333 }
334
335 if (pfd.revents & (POLLHUP | POLLERR)) {
336 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
337 return SDL_FALSE;
338 }
339
340 /* Is the fd readable? Thats enough to call drmHandleEvent() on it. */
341 if (pfd.revents & POLLIN) {
342 /* Page flip? ONLY if the event that made the fd readable (=POLLIN state)
343 is a page flip, will drmHandleEvent call page_flip_handler, which will break the loop.
344 The drmHandleEvent() and subsequent page_flip_handler calls are both synchronous (blocking),
345 nothing runs on a different thread, so no need to protect waiting_for_flip access with mutexes. */
346 KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
347 } else {
348 /* Timed out and page flip didn't happen */
349 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
350 return SDL_FALSE;
351 }
352 }
353
354 return SDL_TRUE;
355 }
356
357 /*****************************************************************************/
358 /* SDL Video and Display initialization/handling functions */
359 /* _this is a SDL_VideoDevice * */
360 /*****************************************************************************/
361 static void
KMSDRM_DestroySurfaces(_THIS,SDL_Window * window)362 KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
363 {
364 SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
365
366 KMSDRM_WaitPageFlip(_this, windata, -1);
367
368 if (windata->curr_bo) {
369 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
370 windata->curr_bo = NULL;
371 }
372
373 if (windata->next_bo) {
374 KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
375 windata->next_bo = NULL;
376 }
377
378 #if SDL_VIDEO_OPENGL_EGL
379 SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
380
381 if (windata->egl_surface != EGL_NO_SURFACE) {
382 SDL_EGL_DestroySurface(_this, windata->egl_surface);
383 windata->egl_surface = EGL_NO_SURFACE;
384 }
385 #endif
386
387 if (windata->gs) {
388 KMSDRM_gbm_surface_destroy(windata->gs);
389 windata->gs = NULL;
390 }
391 }
392
393 int
KMSDRM_CreateSurfaces(_THIS,SDL_Window * window)394 KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
395 {
396 SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
397 SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
398 SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
399 Uint32 width = dispdata->mode.hdisplay;
400 Uint32 height = dispdata->mode.vdisplay;
401 Uint32 surface_fmt = GBM_FORMAT_XRGB8888;
402 Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
403 #if SDL_VIDEO_OPENGL_EGL
404 EGLContext egl_context;
405 #endif
406
407 if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, surface_fmt, surface_flags)) {
408 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
409 }
410
411 #if SDL_VIDEO_OPENGL_EGL
412 SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
413 egl_context = (EGLContext)SDL_GL_GetCurrentContext();
414 #endif
415
416 KMSDRM_DestroySurfaces(_this, window);
417
418 windata->gs = KMSDRM_gbm_surface_create(viddata->gbm, width, height, surface_fmt, surface_flags);
419
420 if (!windata->gs) {
421 return SDL_SetError("Could not create GBM surface");
422 }
423
424 #if SDL_VIDEO_OPENGL_EGL
425 windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
426
427 if (windata->egl_surface == EGL_NO_SURFACE) {
428 return SDL_SetError("Could not create EGL window surface");
429 }
430
431 SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
432
433 windata->egl_surface_dirty = 0;
434 #endif
435
436 return 0;
437 }
438
439 int
KMSDRM_VideoInit(_THIS)440 KMSDRM_VideoInit(_THIS)
441 {
442 int ret = 0;
443 SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
444 SDL_DisplayData *dispdata = NULL;
445 drmModeRes *resources = NULL;
446 drmModeEncoder *encoder = NULL;
447 char devname[32];
448 SDL_VideoDisplay display = {0};
449
450 dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
451
452 if (!dispdata) {
453 return SDL_OutOfMemory();
454 }
455
456 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
457
458 /* Open /dev/dri/cardNN */
459 SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
460
461 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
462 viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
463
464 if (viddata->drm_fd < 0) {
465 ret = SDL_SetError("Could not open %s", devname);
466 goto cleanup;
467 }
468
469 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
470
471 viddata->gbm = KMSDRM_gbm_create_device(viddata->drm_fd);
472 if (!viddata->gbm) {
473 ret = SDL_SetError("Couldn't create gbm device.");
474 goto cleanup;
475 }
476
477 /* Get all of the available connectors / devices / crtcs */
478 resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
479 if (!resources) {
480 ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
481 goto cleanup;
482 }
483
484 for (int i = 0; i < resources->count_connectors; i++) {
485 drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
486
487 if (!conn) {
488 continue;
489 }
490
491 if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
492 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
493 conn->connector_id, conn->count_modes);
494 dispdata->conn = conn;
495 break;
496 }
497
498 KMSDRM_drmModeFreeConnector(conn);
499 }
500
501 if (!dispdata->conn) {
502 ret = SDL_SetError("No currently active connector found.");
503 goto cleanup;
504 }
505
506 /* Try to find the connector's current encoder */
507 for (int i = 0; i < resources->count_encoders; i++) {
508 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
509
510 if (!encoder) {
511 continue;
512 }
513
514 if (encoder->encoder_id == dispdata->conn->encoder_id) {
515 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
516 break;
517 }
518
519 KMSDRM_drmModeFreeEncoder(encoder);
520 encoder = NULL;
521 }
522
523 if (!encoder) {
524 /* No encoder was connected, find the first supported one */
525 for (int i = 0, j; i < resources->count_encoders; i++) {
526 encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
527
528 if (!encoder) {
529 continue;
530 }
531
532 for (j = 0; j < dispdata->conn->count_encoders; j++) {
533 if (dispdata->conn->encoders[j] == encoder->encoder_id) {
534 break;
535 }
536 }
537
538 if (j != dispdata->conn->count_encoders) {
539 break;
540 }
541
542 KMSDRM_drmModeFreeEncoder(encoder);
543 encoder = NULL;
544 }
545 }
546
547 if (!encoder) {
548 ret = SDL_SetError("No connected encoder found.");
549 goto cleanup;
550 }
551
552 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
553
554 /* Try to find a CRTC connected to this encoder */
555 dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
556
557 if (!dispdata->saved_crtc) {
558 /* No CRTC was connected, find the first CRTC that can be connected */
559 for (int i = 0; i < resources->count_crtcs; i++) {
560 if (encoder->possible_crtcs & (1 << i)) {
561 encoder->crtc_id = resources->crtcs[i];
562 dispdata->saved_crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
563 break;
564 }
565 }
566 }
567
568 if (!dispdata->saved_crtc) {
569 ret = SDL_SetError("No CRTC found.");
570 goto cleanup;
571 }
572
573 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Saved crtc_id %u, fb_id %u, (%u,%u), %ux%u",
574 dispdata->saved_crtc->crtc_id, dispdata->saved_crtc->buffer_id, dispdata->saved_crtc->x,
575 dispdata->saved_crtc->y, dispdata->saved_crtc->width, dispdata->saved_crtc->height);
576
577 dispdata->crtc_id = encoder->crtc_id;
578
579 /* Figure out the default mode to be set. If the current CRTC's mode isn't
580 valid, select the first mode supported by the connector
581
582 FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
583 dispdata->mode = dispdata->saved_crtc->mode;
584
585 if (dispdata->saved_crtc->mode_valid == 0) {
586 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
587 "Current mode is invalid, selecting connector's mode #0.");
588 dispdata->mode = dispdata->conn->modes[0];
589 }
590
591 /* Setup the single display that's available */
592
593 display.desktop_mode.w = dispdata->mode.hdisplay;
594 display.desktop_mode.h = dispdata->mode.vdisplay;
595 display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
596 #if 1
597 display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
598 #else
599 /* FIXME */
600 drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->saved_crtc->buffer_id);
601 display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
602 drmModeFreeFB(fb);
603 #endif
604
605 /* DRM mode index for the desktop mode is needed to complete desktop mode init NOW,
606 so look for it in the DRM modes array. */
607 for (int i = 0; i < dispdata->conn->count_modes; i++) {
608 if (!SDL_memcmp(dispdata->conn->modes + i, &dispdata->saved_crtc->mode, sizeof(drmModeModeInfo))) {
609 SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
610 if (modedata) {
611 modedata->mode_index = i;
612 display.desktop_mode.driverdata = modedata;
613 }
614 }
615 }
616
617 display.current_mode = display.desktop_mode;
618 display.driverdata = dispdata;
619 SDL_AddVideoDisplay(&display);
620
621 #ifdef SDL_INPUT_LINUXEV
622 SDL_EVDEV_Init();
623 #endif
624
625 KMSDRM_InitMouse(_this);
626
627 return ret;
628
629 cleanup:
630 if (encoder)
631 KMSDRM_drmModeFreeEncoder(encoder);
632 if (resources)
633 KMSDRM_drmModeFreeResources(resources);
634
635 if (ret != 0) {
636 /* Error (complete) cleanup */
637 if (dispdata->conn) {
638 KMSDRM_drmModeFreeConnector(dispdata->conn);
639 dispdata->conn = NULL;
640 }
641 if (dispdata->saved_crtc) {
642 KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
643 dispdata->saved_crtc = NULL;
644 }
645 if (viddata->gbm) {
646 KMSDRM_gbm_device_destroy(viddata->gbm);
647 viddata->gbm = NULL;
648 }
649 if (viddata->drm_fd >= 0) {
650 close(viddata->drm_fd);
651 viddata->drm_fd = -1;
652 }
653 SDL_free(dispdata);
654 }
655 return ret;
656 }
657
658 void
KMSDRM_VideoQuit(_THIS)659 KMSDRM_VideoQuit(_THIS)
660 {
661 SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
662 SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
663
664 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
665
666 if (_this->gl_config.driver_loaded) {
667 SDL_GL_UnloadLibrary();
668 }
669
670 /* Clear out the window list */
671 SDL_free(viddata->windows);
672 viddata->windows = NULL;
673 viddata->max_windows = 0;
674 viddata->num_windows = 0;
675
676 /* Restore saved CRTC settings */
677 if (viddata->drm_fd >= 0 && dispdata && dispdata->conn && dispdata->saved_crtc) {
678 drmModeConnector *conn = dispdata->conn;
679 drmModeCrtc *crtc = dispdata->saved_crtc;
680
681 int ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, crtc->crtc_id, crtc->buffer_id,
682 crtc->x, crtc->y, &conn->connector_id, 1, &crtc->mode);
683
684 if (ret != 0) {
685 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original CRTC mode");
686 }
687 }
688 if (dispdata && dispdata->conn) {
689 KMSDRM_drmModeFreeConnector(dispdata->conn);
690 dispdata->conn = NULL;
691 }
692 if (dispdata && dispdata->saved_crtc) {
693 KMSDRM_drmModeFreeCrtc(dispdata->saved_crtc);
694 dispdata->saved_crtc = NULL;
695 }
696 if (viddata->gbm) {
697 KMSDRM_gbm_device_destroy(viddata->gbm);
698 viddata->gbm = NULL;
699 }
700 if (viddata->drm_fd >= 0) {
701 close(viddata->drm_fd);
702 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
703 viddata->drm_fd = -1;
704 }
705 #ifdef SDL_INPUT_LINUXEV
706 SDL_EVDEV_Quit();
707 #endif
708 }
709
710 void
KMSDRM_GetDisplayModes(_THIS,SDL_VideoDisplay * display)711 KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
712 {
713 SDL_DisplayData *dispdata = display->driverdata;
714 drmModeConnector *conn = dispdata->conn;
715 SDL_DisplayMode mode;
716
717 for (int i = 0; i < conn->count_modes; i++) {
718 SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
719
720 if (modedata) {
721 modedata->mode_index = i;
722 }
723
724 mode.w = conn->modes[i].hdisplay;
725 mode.h = conn->modes[i].vdisplay;
726 mode.refresh_rate = conn->modes[i].vrefresh;
727 mode.format = SDL_PIXELFORMAT_ARGB8888;
728 mode.driverdata = modedata;
729
730 if (!SDL_AddDisplayMode(display, &mode)) {
731 SDL_free(modedata);
732 }
733 }
734 }
735
736 int
KMSDRM_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)737 KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
738 {
739 SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
740 SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
741 SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
742 drmModeConnector *conn = dispdata->conn;
743
744 if (!modedata) {
745 return SDL_SetError("Mode doesn't have an associated index");
746 }
747
748 dispdata->mode = conn->modes[modedata->mode_index];
749
750 for (int i = 0; i < viddata->num_windows; i++) {
751 SDL_Window *window = viddata->windows[i];
752 SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
753
754 #if SDL_VIDEO_OPENGL_EGL
755 /* Can't recreate EGL surfaces right now, need to wait until SwapWindow
756 so the correct thread-local surface and context state are available */
757 windata->egl_surface_dirty = 1;
758 #else
759 if (KMSDRM_CreateSurfaces(_this, window)) {
760 return -1;
761 }
762 #endif
763
764 /* Tell app about the resize */
765 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
766 }
767
768 return 0;
769 }
770
771 int
KMSDRM_CreateWindow(_THIS,SDL_Window * window)772 KMSDRM_CreateWindow(_THIS, SDL_Window * window)
773 {
774 SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
775 SDL_WindowData *windata;
776
777 #if SDL_VIDEO_OPENGL_EGL
778 if (!_this->egl_data) {
779 if (SDL_GL_LoadLibrary(NULL) < 0) {
780 goto error;
781 }
782 }
783 #endif
784
785 /* Allocate window internal data */
786 windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
787
788 if (!windata) {
789 SDL_OutOfMemory();
790 goto error;
791 }
792
793 /* In case low-latency is wanted, double-buffered video will be used. We take note here */
794 windata->double_buffer = SDL_FALSE;
795
796 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
797 windata->double_buffer = SDL_TRUE;
798 }
799
800 /* Setup driver data for this window */
801 windata->viddata = viddata;
802 window->driverdata = windata;
803
804 if (KMSDRM_CreateSurfaces(_this, window)) {
805 goto error;
806 }
807
808 /* Add window to the internal list of tracked windows. Note, while it may
809 seem odd to support multiple fullscreen windows, some apps create an
810 extra window as a dummy surface when working with multiple contexts */
811 if (viddata->num_windows >= viddata->max_windows) {
812 int new_max_windows = viddata->max_windows + 1;
813 viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
814 new_max_windows * sizeof(SDL_Window *));
815 viddata->max_windows = new_max_windows;
816
817 if (!viddata->windows) {
818 SDL_OutOfMemory();
819 goto error;
820 }
821 }
822
823 viddata->windows[viddata->num_windows++] = window;
824
825 /* Focus on the newly created window */
826 SDL_SetMouseFocus(window);
827 SDL_SetKeyboardFocus(window);
828
829 return 0;
830
831 error:
832 KMSDRM_DestroyWindow(_this, window);
833
834 return -1;
835 }
836
837 void
KMSDRM_DestroyWindow(_THIS,SDL_Window * window)838 KMSDRM_DestroyWindow(_THIS, SDL_Window * window)
839 {
840 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
841 SDL_VideoData *viddata;
842 if (!windata) {
843 return;
844 }
845
846 /* Remove from the internal window list */
847 viddata = windata->viddata;
848
849 for (int i = 0; i < viddata->num_windows; i++) {
850 if (viddata->windows[i] == window) {
851 viddata->num_windows--;
852
853 for (int j = i; j < viddata->num_windows; j++) {
854 viddata->windows[j] = viddata->windows[j + 1];
855 }
856
857 break;
858 }
859 }
860
861 KMSDRM_DestroySurfaces(_this, window);
862
863 window->driverdata = NULL;
864
865 SDL_free(windata);
866 }
867
868 int
KMSDRM_CreateWindowFrom(_THIS,SDL_Window * window,const void * data)869 KMSDRM_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
870 {
871 return -1;
872 }
873
874 void
KMSDRM_SetWindowTitle(_THIS,SDL_Window * window)875 KMSDRM_SetWindowTitle(_THIS, SDL_Window * window)
876 {
877 }
878 void
KMSDRM_SetWindowIcon(_THIS,SDL_Window * window,SDL_Surface * icon)879 KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
880 {
881 }
882 void
KMSDRM_SetWindowPosition(_THIS,SDL_Window * window)883 KMSDRM_SetWindowPosition(_THIS, SDL_Window * window)
884 {
885 }
886 void
KMSDRM_SetWindowSize(_THIS,SDL_Window * window)887 KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
888 {
889 }
890 void
KMSDRM_ShowWindow(_THIS,SDL_Window * window)891 KMSDRM_ShowWindow(_THIS, SDL_Window * window)
892 {
893 }
894 void
KMSDRM_HideWindow(_THIS,SDL_Window * window)895 KMSDRM_HideWindow(_THIS, SDL_Window * window)
896 {
897 }
898 void
KMSDRM_RaiseWindow(_THIS,SDL_Window * window)899 KMSDRM_RaiseWindow(_THIS, SDL_Window * window)
900 {
901 }
902 void
KMSDRM_MaximizeWindow(_THIS,SDL_Window * window)903 KMSDRM_MaximizeWindow(_THIS, SDL_Window * window)
904 {
905 }
906 void
KMSDRM_MinimizeWindow(_THIS,SDL_Window * window)907 KMSDRM_MinimizeWindow(_THIS, SDL_Window * window)
908 {
909 }
910 void
KMSDRM_RestoreWindow(_THIS,SDL_Window * window)911 KMSDRM_RestoreWindow(_THIS, SDL_Window * window)
912 {
913 }
914 void
KMSDRM_SetWindowGrab(_THIS,SDL_Window * window,SDL_bool grabbed)915 KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
916 {
917
918 }
919
920 /*****************************************************************************/
921 /* SDL Window Manager function */
922 /*****************************************************************************/
923 SDL_bool
KMSDRM_GetWindowWMInfo(_THIS,SDL_Window * window,struct SDL_SysWMinfo * info)924 KMSDRM_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
925 {
926 if (info->version.major <= SDL_MAJOR_VERSION) {
927 return SDL_TRUE;
928 } else {
929 SDL_SetError("application not compiled with SDL %d.%d\n",
930 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
931 return SDL_FALSE;
932 }
933
934 /* Failed to get window manager information */
935 return SDL_FALSE;
936 }
937
938 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
939
940 /* vi: set ts=4 sw=4 expandtab: */
941