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