chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1,161 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM
#define DEBUG_DYNAMIC_KMSDRM 0
#include "SDL_kmsdrmdyn.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
typedef struct
{
void *lib;
const char *libname;
} kmsdrmdynlib;
#ifndef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM
#define SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM NULL
#endif
static kmsdrmdynlib kmsdrmlibs[] = {
{ NULL, SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM },
{ NULL, SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC }
};
static void *KMSDRM_GetSym(const char *fnname, int *pHasModule, bool required)
{
int i;
void *fn = NULL;
for (i = 0; i < SDL_arraysize(kmsdrmlibs); i++) {
if (kmsdrmlibs[i].lib) {
fn = SDL_LoadFunction(kmsdrmlibs[i].lib, fnname);
if (fn) {
break;
}
}
}
#if DEBUG_DYNAMIC_KMSDRM
if (fn)
SDL_Log("KMSDRM: Found '%s' in %s (%p)", fnname, kmsdrmlibs[i].libname, fn);
else
SDL_Log("KMSDRM: Symbol '%s' NOT FOUND!", fnname);
#endif
if (!fn && required) {
*pHasModule = 0; // kill this module.
}
return fn;
}
#endif // SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
// Define all the function pointers and wrappers...
#define SDL_KMSDRM_MODULE(modname) int SDL_KMSDRM_HAVE_##modname = 0;
#define SDL_KMSDRM_SYM(rc, fn, params) SDL_DYNKMSDRMFN_##fn KMSDRM_##fn = NULL;
#define SDL_KMSDRM_SYM_CONST(type, name) SDL_DYNKMSDRMCONST_##name KMSDRM_##name = NULL;
#define SDL_KMSDRM_SYM_OPT(rc, fn, params) SDL_DYNKMSDRMFN_##fn KMSDRM_##fn = NULL;
#include "SDL_kmsdrmsym.h"
static int kmsdrm_load_refcount = 0;
void SDL_KMSDRM_UnloadSymbols(void)
{
// Don't actually unload if more than one module is using the libs...
if (kmsdrm_load_refcount > 0) {
if (--kmsdrm_load_refcount == 0) {
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
int i;
#endif
// set all the function pointers to NULL.
#define SDL_KMSDRM_MODULE(modname) SDL_KMSDRM_HAVE_##modname = 0;
#define SDL_KMSDRM_SYM(rc, fn, params) KMSDRM_##fn = NULL;
#define SDL_KMSDRM_SYM_CONST(type, name) KMSDRM_##name = NULL;
#define SDL_KMSDRM_SYM_OPT(rc, fn, params) KMSDRM_##fn = NULL;
#include "SDL_kmsdrmsym.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
for (i = 0; i < SDL_arraysize(kmsdrmlibs); i++) {
if (kmsdrmlibs[i].lib) {
SDL_UnloadObject(kmsdrmlibs[i].lib);
kmsdrmlibs[i].lib = NULL;
}
}
#endif
}
}
}
// returns non-zero if all needed symbols were loaded.
bool SDL_KMSDRM_LoadSymbols(void)
{
bool result = true; // always succeed if not using Dynamic KMSDRM stuff.
// deal with multiple modules needing these symbols...
if (kmsdrm_load_refcount++ == 0) {
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
int i;
int *thismod = NULL;
for (i = 0; i < SDL_arraysize(kmsdrmlibs); i++) {
if (kmsdrmlibs[i].libname) {
kmsdrmlibs[i].lib = SDL_LoadObject(kmsdrmlibs[i].libname);
}
}
#define SDL_KMSDRM_MODULE(modname) SDL_KMSDRM_HAVE_##modname = 1; // default yes
#include "SDL_kmsdrmsym.h"
#define SDL_KMSDRM_MODULE(modname) thismod = &SDL_KMSDRM_HAVE_##modname;
#define SDL_KMSDRM_SYM(rc, fn, params) KMSDRM_##fn = (SDL_DYNKMSDRMFN_##fn)KMSDRM_GetSym(#fn, thismod, true);
#define SDL_KMSDRM_SYM_CONST(type, name) KMSDRM_##name = *(SDL_DYNKMSDRMCONST_##name *)KMSDRM_GetSym(#name, thismod, true);
#define SDL_KMSDRM_SYM_OPT(rc, fn, params) KMSDRM_##fn = (SDL_DYNKMSDRMFN_##fn)KMSDRM_GetSym(#fn, thismod, false);
#include "SDL_kmsdrmsym.h"
if ((SDL_KMSDRM_HAVE_LIBDRM) && (SDL_KMSDRM_HAVE_GBM)) {
// all required symbols loaded.
SDL_ClearError();
} else {
// in case something got loaded...
SDL_KMSDRM_UnloadSymbols();
result = false;
}
#else // no dynamic KMSDRM
#define SDL_KMSDRM_MODULE(modname) SDL_KMSDRM_HAVE_##modname = 1; // default yes
#define SDL_KMSDRM_SYM(rc, fn, params) KMSDRM_##fn = fn;
#define SDL_KMSDRM_SYM_CONST(type, name) KMSDRM_##name = name;
#define SDL_KMSDRM_SYM_OPT(rc, fn, params) KMSDRM_##fn = fn;
#include "SDL_kmsdrmsym.h"
#endif
}
return result;
}
#endif // SDL_VIDEO_DRIVER_KMSDRM

View file

@ -0,0 +1,54 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_kmsdrmdyn_h_
#define SDL_kmsdrmdyn_h_
#include "SDL_internal.h"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#ifdef __cplusplus
extern "C" {
#endif
extern bool SDL_KMSDRM_LoadSymbols(void);
extern void SDL_KMSDRM_UnloadSymbols(void);
// Declare all the function pointers and wrappers...
#define SDL_KMSDRM_SYM(rc, fn, params) \
typedef rc(*SDL_DYNKMSDRMFN_##fn) params; \
extern SDL_DYNKMSDRMFN_##fn KMSDRM_##fn;
#define SDL_KMSDRM_SYM_CONST(type, name) \
typedef type SDL_DYNKMSDRMCONST_##name; \
extern SDL_DYNKMSDRMCONST_##name KMSDRM_##name;
#define SDL_KMSDRM_SYM_OPT(rc, fn, params) \
typedef rc(*SDL_DYNKMSDRMFN_##fn) params; \
extern SDL_DYNKMSDRMFN_##fn KMSDRM_##fn;
#include "SDL_kmsdrmsym.h"
#ifdef __cplusplus
}
#endif
#endif // SDL_kmsdrmdyn_h_

View file

@ -0,0 +1,44 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmevents.h"
#ifdef SDL_INPUT_LINUXEV
#include "../../core/linux/SDL_evdev.h"
#elif defined SDL_INPUT_WSCONS
#include "../../core/openbsd/SDL_wscons.h"
#endif
void KMSDRM_PumpEvents(SDL_VideoDevice *_this)
{
#ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Poll();
#elif defined SDL_INPUT_WSCONS
SDL_WSCONS_PumpEvents();
#endif
}
#endif // SDL_VIDEO_DRIVER_KMSDRM

View file

@ -0,0 +1,29 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_kmsdrmevents_h_
#define SDL_kmsdrmevents_h_
extern void KMSDRM_PumpEvents(SDL_VideoDevice *_this);
#endif // SDL_kmsdrmevents_h_

View file

@ -0,0 +1,410 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmmouse.h"
#include "SDL_kmsdrmdyn.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/default_cursor.h"
#include "../SDL_pixels_c.h"
static SDL_Cursor *KMSDRM_CreateDefaultCursor(void);
static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y);
static bool KMSDRM_ShowCursor(SDL_Cursor *cursor);
static bool KMSDRM_MoveCursor(SDL_Cursor *cursor);
static void KMSDRM_FreeCursor(SDL_Cursor *cursor);
/**************************************************************************************/
// BEFORE CODING ANYTHING MOUSE/CURSOR RELATED, REMEMBER THIS.
// How does SDL manage cursors internally? First, mouse =! cursor. The mouse can have
// many cursors in mouse->cursors.
// -SDL tells us to create a cursor with KMSDRM_CreateCursor(). It can create many
// cursosr with this, not only one.
// -SDL stores those cursors in a cursors array, in mouse->cursors.
// -Whenever it wants (or the programmer wants) takes a cursor from that array
// and shows it on screen with KMSDRM_ShowCursor().
// KMSDRM_ShowCursor() simply shows or hides the cursor it receives: it does NOT
// mind if it's mouse->cur_cursor, etc.
// -If KMSDRM_ShowCursor() returns successfully, that cursor becomes
// mouse->cur_cursor and mouse->cursor_shown is 1.
/**************************************************************************************/
static SDL_Cursor *KMSDRM_CreateDefaultCursor(void)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
/* Given a display's internal, destroy the cursor BO for it.
To be called from KMSDRM_DestroyWindow(), as that's where we
destroy the internal for the window's display. */
void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
{
SDL_DisplayData *dispdata = display->internal;
// Destroy the curso GBM BO.
if (dispdata->cursor_bo) {
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
dispdata->cursor_bo = NULL;
dispdata->cursor_bo_drm_fd = -1;
}
}
/* Given a display's internal, create the cursor BO for it.
To be called from KMSDRM_CreateWindow(), as that's where we
build a window and assign a display to it. */
bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display)
{
SDL_VideoDevice *dev = SDL_GetVideoDevice();
SDL_VideoData *viddata = dev->internal;
SDL_DisplayData *dispdata = display->internal;
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
return SDL_SetError("Unsupported pixel format for cursor");
}
if (KMSDRM_drmGetCap(viddata->drm_fd,
DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) ||
KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT,
&dispdata->cursor_h)) {
return SDL_SetError("Could not get the recommended GBM cursor size");
}
if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) {
return SDL_SetError("Could not get an usable GBM cursor size");
}
dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev,
dispdata->cursor_w, dispdata->cursor_h,
GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR);
if (!dispdata->cursor_bo) {
return SDL_SetError("Could not create GBM cursor BO");
}
dispdata->cursor_bo_drm_fd = viddata->drm_fd;
return true;
}
// Remove a cursor buffer from a display's DRM cursor BO.
static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
{
bool result = true;
SDL_DisplayData *dispdata = display->internal;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = video_device->internal;
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, 0, 0, 0);
if (rc < 0) {
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
}
return result;
}
// Dump a cursor buffer to a display's DRM cursor BO.
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
{
SDL_DisplayData *dispdata = display->internal;
SDL_CursorData *curdata = cursor->internal;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = video_device->internal;
uint32_t bo_handle;
size_t bo_stride;
size_t bufsize;
uint8_t *ready_buffer = NULL;
uint8_t *src_row;
int i, rc;
bool result = true;
if (!curdata || !dispdata->cursor_bo) {
return SDL_SetError("Cursor or display not initialized properly.");
}
/* Prepare a buffer we can dump to our GBM BO (different
size, alpha premultiplication...) */
bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
bufsize = bo_stride * dispdata->cursor_h;
ready_buffer = (uint8_t *)SDL_calloc(1, bufsize);
if (!ready_buffer) {
result = false;
goto cleanup;
}
// Copy from the cursor buffer to a buffer that we can dump to the GBM BO.
for (i = 0; i < curdata->h; i++) {
src_row = &((uint8_t *)curdata->buffer)[i * curdata->w * 4];
SDL_memcpy(ready_buffer + (i * bo_stride), src_row, (size_t)4 * curdata->w);
}
// Dump the cursor buffer to our GBM BO.
if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) {
result = SDL_SetError("Could not write to GBM cursor BO");
goto cleanup;
}
// Put the GBM BO buffer on screen using the DRM interface.
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
} else {
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
}
if (rc < 0) {
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
goto cleanup;
}
cleanup:
if (ready_buffer) {
SDL_free(ready_buffer);
}
return result;
}
// This is only for freeing the SDL_cursor.
static void KMSDRM_FreeCursor(SDL_Cursor *cursor)
{
SDL_CursorData *curdata;
// Even if the cursor is not ours, free it.
if (cursor) {
curdata = cursor->internal;
// Free cursor buffer
if (curdata->buffer) {
SDL_free(curdata->buffer);
curdata->buffer = NULL;
}
// Free cursor itself
if (cursor->internal) {
SDL_free(cursor->internal);
}
SDL_free(cursor);
}
}
/* This simply gets the cursor soft-buffer ready.
We don't copy it to a GBO BO until ShowCursor() because the cusor GBM BO (living
in dispata) is destroyed and recreated when we recreate windows, etc. */
static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
SDL_CursorData *curdata;
SDL_Cursor *cursor, *result;
curdata = NULL;
result = NULL;
cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
goto cleanup;
}
curdata = (SDL_CursorData *)SDL_calloc(1, sizeof(*curdata));
if (!curdata) {
goto cleanup;
}
// hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base.
curdata->hot_x = hot_x;
curdata->hot_y = hot_y;
curdata->w = surface->w;
curdata->h = surface->h;
curdata->buffer = NULL;
/* Configure the cursor buffer info.
This buffer has the original size of the cursor surface we are given. */
curdata->buffer_pitch = surface->w;
curdata->buffer_size = (size_t)surface->w * surface->h * 4;
curdata->buffer = (uint32_t *)SDL_malloc(curdata->buffer_size);
if (!curdata->buffer) {
goto cleanup;
}
/* All code below assumes ARGB8888 format for the cursor surface,
like other backends do. Also, the GBM BO pixels have to be
alpha-premultiplied, but the SDL surface we receive has
straight-alpha pixels, so we always have to convert. */
SDL_PremultiplyAlpha(surface->w, surface->h,
surface->format, surface->pixels, surface->pitch,
SDL_PIXELFORMAT_ARGB8888, curdata->buffer, surface->w * 4, true);
cursor->internal = curdata;
result = cursor;
cleanup:
if (!result) {
if (curdata) {
if (curdata->buffer) {
SDL_free(curdata->buffer);
}
SDL_free(curdata);
}
if (cursor) {
SDL_free(cursor);
}
}
return result;
}
// Show the specified cursor, or hide if cursor is NULL or has no focus.
static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
{
SDL_VideoDisplay *display;
SDL_Window *window;
SDL_Mouse *mouse = SDL_GetMouse();
int i;
bool result = true;
// Get the mouse focused window, if any.
window = mouse->focus;
if (!window || !cursor) {
/* If no window is focused by mouse or cursor is NULL,
since we have no window (no mouse->focus) and hence
we have no display, we simply hide mouse on all displays.
This happens on video quit, where we get here after
the mouse focus has been unset, yet SDL wants to
restore the system default cursor (makes no sense here). */
SDL_DisplayID *displays = SDL_GetDisplays(NULL);
if (displays) {
// Iterate on the displays, hiding the cursor.
for (i = 0; i < displays[i]; i++) {
display = SDL_GetVideoDisplay(displays[i]);
result = KMSDRM_RemoveCursorFromBO(display);
}
SDL_free(displays);
}
} else {
display = SDL_GetVideoDisplayForWindow(window);
if (display) {
if (cursor) {
/* Dump the cursor to the display DRM cursor BO so it becomes visible
on that display. */
result = KMSDRM_DumpCursorToBO(display, cursor);
} else {
// Hide the cursor on that display.
result = KMSDRM_RemoveCursorFromBO(display);
}
}
}
return result;
}
static bool KMSDRM_WarpMouseGlobal(float x, float y)
{
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse && mouse->cur_cursor && mouse->focus) {
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
// Update internal mouse position.
SDL_SendMouseMotion(0, mouse->focus, SDL_GLOBAL_MOUSE_ID, false, x, y);
// And now update the cursor graphic position on screen.
if (dispdata->cursor_bo) {
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)x, (int)y);
if (rc < 0) {
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
}
return true;
} else {
return SDL_SetError("Cursor not initialized properly.");
}
} else {
return SDL_SetError("No mouse or current cursor.");
}
}
static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y)
{
// Only one global/fullscreen window is supported
return KMSDRM_WarpMouseGlobal(x, y);
}
void KMSDRM_InitMouse(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
{
SDL_Mouse *mouse = SDL_GetMouse();
SDL_DisplayData *dispdata = display->internal;
mouse->CreateCursor = KMSDRM_CreateCursor;
mouse->ShowCursor = KMSDRM_ShowCursor;
mouse->MoveCursor = KMSDRM_MoveCursor;
mouse->FreeCursor = KMSDRM_FreeCursor;
mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
/* Only create the default cursor for this display if we haven't done so before,
we don't want several cursors to be created for the same display. */
if (!dispdata->default_cursor_init) {
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
dispdata->default_cursor_init = true;
}
}
void KMSDRM_QuitMouse(SDL_VideoDevice *_this)
{
// TODO: ?
}
// This is called when a mouse motion event occurs
static bool KMSDRM_MoveCursor(SDL_Cursor *cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
/* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
That's why we move the cursor graphic ONLY. */
if (mouse && mouse->cur_cursor && mouse->focus) {
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
if (!dispdata->cursor_bo) {
return SDL_SetError("Cursor not initialized properly.");
}
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)mouse->x, (int)mouse->y);
if (rc < 0) {
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
}
}
return true;
}
#endif // SDL_VIDEO_DRIVER_KMSDRM

View file

@ -0,0 +1,53 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_KMSDRM_mouse_h_
#define SDL_KMSDRM_mouse_h_
#include <gbm.h>
#define MAX_CURSOR_W 512
#define MAX_CURSOR_H 512
struct SDL_CursorData
{
int hot_x, hot_y;
int w, h;
/* The buffer where we store the mouse bitmap ready to be used.
We get it ready and filled in CreateCursor(), and copy it
to a GBM BO in ShowCursor().*/
uint32_t *buffer;
size_t buffer_size;
size_t buffer_pitch;
};
extern void KMSDRM_InitMouse(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern void KMSDRM_QuitMouse(SDL_VideoDevice *_this);
extern bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display);
extern void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern void KMSDRM_InitCursor(void);
#endif // SDL_KMSDRM_mouse_h_

View file

@ -0,0 +1,203 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmopengles.h"
#include "SDL_kmsdrmdyn.h"
#include <errno.h>
#ifndef EGL_PLATFORM_GBM_MESA
#define EGL_PLATFORM_GBM_MESA 0x31D7
#endif
// EGL implementation of SDL OpenGL support
void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor)
{
/* if SDL was _also_ built with the Raspberry Pi driver (so we're
definitely a Pi device) or with the ROCKCHIP video driver
(it's a ROCKCHIP device), default to GLES2. */
#if defined(SDL_VIDEO_DRIVER_RPI) || defined(SDL_VIDEO_DRIVER_ROCKCHIP)
*mask = SDL_GL_CONTEXT_PROFILE_ES;
*major = 2;
*minor = 0;
#endif
}
bool KMSDRM_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
/* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(),
where we do the same library load we would normally do here.
because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(),
so gbm dev isn't yet created when this is called, AND we can't alter the
call order in SDL_CreateWindow(). */
#if 0
NativeDisplayType display = (NativeDisplayType)_this->internal->gbm_dev;
return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA);
#endif
return true;
}
void KMSDRM_GLES_UnloadLibrary(SDL_VideoDevice *_this)
{
/* As with KMSDRM_GLES_LoadLibrary(), we define our own "dummy" unloading function
so we manually unload the library whenever we want. */
}
SDL_EGL_CreateContext_impl(KMSDRM)
bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval)
{
if (!_this->egl_data) {
return SDL_SetError("EGL not initialized");
}
if (interval == 0 || interval == 1) {
_this->egl_data->egl_swapinterval = interval;
} else {
return SDL_SetError("Only swap intervals of 0 or 1 are supported");
}
return true;
}
bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *windata = window->internal;
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
SDL_VideoData *viddata = _this->internal;
KMSDRM_FBInfo *fb_info;
int ret = 0;
/* Always wait for the previous issued flip before issuing a new one,
even if you do async flips. */
uint32_t flip_flags = DRM_MODE_PAGE_FLIP_EVENT;
// Skip the swap if we've switched away to another VT
if (windata->egl_surface == EGL_NO_SURFACE) {
// Wait a bit, throttling to ~100 FPS
SDL_Delay(10);
return true;
}
// Recreate the GBM / EGL surfaces if the display mode has changed
if (windata->egl_surface_dirty) {
KMSDRM_CreateSurfaces(_this, window);
}
/* Wait for confirmation that the next front buffer has been flipped, at which
point the previous front buffer can be released */
if (!KMSDRM_WaitPageflip(_this, windata)) {
return SDL_SetError("Wait for previous pageflip failed");
}
// Release the previous front buffer
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL;
}
windata->bo = windata->next_bo;
/* Mark a buffer to become the next front buffer.
This won't happen until pagelip completes. */
if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display,
windata->egl_surface))) {
return SDL_SetError("eglSwapBuffers failed");
}
/* From the GBM surface, get the next BO to become the next front buffer,
and lock it so it can't be allocated as a back buffer (to prevent EGL
from drawing into it!) */
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
return SDL_SetError("Could not lock front buffer on GBM surface");
}
// Get an actual usable fb for the next front buffer.
fb_info = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb_info) {
return SDL_SetError("Could not get a framebuffer");
}
if (!windata->bo) {
/* On the first swap, immediately present the new front buffer. Before
drmModePageFlip can be used the CRTC has to be configured to use
the current connector and mode with drmModeSetCrtc */
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0,
&dispdata->connector->connector_id, 1, &dispdata->mode);
if (ret) {
return SDL_SetError("Could not set videomode on CRTC.");
}
} else {
/* On subsequent swaps, queue the new front buffer to be flipped during
the next vertical blank
Remember: drmModePageFlip() never blocks, it just issues the flip,
which will be done during the next vblank, or immediately if
we pass the DRM_MODE_PAGE_FLIP_ASYNC flag.
Since calling drmModePageFlip() will return EBUSY if we call it
without having completed the last issued flip, we must pass the
DRM_MODE_PAGE_FLIP_ASYNC if we don't block on EGL (egl_swapinterval = 0).
That makes it flip immediately, without waiting for the next vblank
to do so, so even if we don't block on EGL, the flip will have completed
when we get here again. */
if (_this->egl_data->egl_swapinterval == 0 && viddata->async_pageflip_support) {
flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
}
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
fb_info->fb_id, flip_flags, &windata->waiting_for_flip);
if (ret == 0) {
windata->waiting_for_flip = true;
} else {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
}
/* Wait immediately for vsync (as if we only had two buffers).
Even if we are already doing a WaitPageflip at the beginning of this
function, this is NOT redundant because here we wait immediately
after submitting the image to the screen, reducing lag, and if
we have waited here, there won't be a pending pageflip so the
WaitPageflip at the beginning of this function will be a no-op.
Just leave it here and don't worry.
Run your SDL program with "SDL_VIDEO_DOUBLE_BUFFER=1 <program_name>"
to enable this. */
if (windata->double_buffer) {
if (!KMSDRM_WaitPageflip(_this, windata)) {
return SDL_SetError("Immediate wait for previous pageflip failed");
}
}
}
return true;
}
SDL_EGL_MakeCurrent_impl(KMSDRM)
#endif // SDL_VIDEO_DRIVER_KMSDRM

View file

@ -0,0 +1,42 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_kmsdrmopengles_h_
#define SDL_kmsdrmopengles_h_
#include "../SDL_egl_c.h"
// OpenGLES functions
#define KMSDRM_GLES_GetAttribute SDL_EGL_GetAttribute
#define KMSDRM_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define KMSDRM_GLES_DestroyContext SDL_EGL_DestroyContext
#define KMSDRM_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
extern void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor);
extern bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool KMSDRM_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void KMSDRM_GLES_UnloadLibrary(SDL_VideoDevice *_this);
extern SDL_GLContext KMSDRM_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool KMSDRM_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
#endif // SDL_kmsdrmopengles_h_

View file

@ -0,0 +1,145 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* *INDENT-OFF* */ // clang-format off
#ifndef SDL_KMSDRM_MODULE
#define SDL_KMSDRM_MODULE(modname)
#endif
#ifndef SDL_KMSDRM_SYM
#define SDL_KMSDRM_SYM(rc,fn,params)
#endif
#ifndef SDL_KMSDRM_SYM_CONST
#define SDL_KMSDRM_SYM_CONST(type, name)
#endif
#ifndef SDL_KMSDRM_SYM_OPT
#define SDL_KMSDRM_SYM_OPT(rc,fn,params)
#endif
SDL_KMSDRM_MODULE(LIBDRM)
SDL_KMSDRM_SYM(void,drmModeFreeResources,(drmModeResPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeFB,(drmModeFBPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeCrtc,(drmModeCrtcPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeConnector,(drmModeConnectorPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeEncoder,(drmModeEncoderPtr ptr))
SDL_KMSDRM_SYM(int,drmGetCap,(int fd, uint64_t capability, uint64_t *value))
SDL_KMSDRM_SYM(int,drmSetMaster,(int fd))
SDL_KMSDRM_SYM(int,drmDropMaster,(int fd))
SDL_KMSDRM_SYM(int,drmAuthMagic,(int fd, drm_magic_t magic))
SDL_KMSDRM_SYM(drmModeResPtr,drmModeGetResources,(int fd))
SDL_KMSDRM_SYM(int,drmModeAddFB,(int fd, uint32_t width, uint32_t height, uint8_t depth,
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
uint32_t *buf_id))
SDL_KMSDRM_SYM_OPT(int,drmModeAddFB2,(int fd, uint32_t width, uint32_t height,
uint32_t pixel_format, const uint32_t bo_handles[4],
const uint32_t pitches[4], const uint32_t offsets[4],
uint32_t *buf_id, uint32_t flags))
SDL_KMSDRM_SYM_OPT(int,drmModeAddFB2WithModifiers,(int fd, uint32_t width,
uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4],
const uint32_t pitches[4], const uint32_t offsets[4],
const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags))
SDL_KMSDRM_SYM_OPT(const char *,drmModeGetConnectorTypeName,(uint32_t connector_type))
SDL_KMSDRM_SYM(int,drmModeRmFB,(int fd, uint32_t bufferId))
SDL_KMSDRM_SYM(drmModeFBPtr,drmModeGetFB,(int fd, uint32_t buf))
SDL_KMSDRM_SYM(drmModeCrtcPtr,drmModeGetCrtc,(int fd, uint32_t crtcId))
SDL_KMSDRM_SYM(int,drmModeSetCrtc,(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode))
SDL_KMSDRM_SYM(int,drmModeSetCursor,(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height))
SDL_KMSDRM_SYM(int,drmModeSetCursor2,(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height,
int32_t hot_x, int32_t hot_y))
SDL_KMSDRM_SYM(int,drmModeMoveCursor,(int fd, uint32_t crtcId, int x, int y))
SDL_KMSDRM_SYM(drmModeEncoderPtr,drmModeGetEncoder,(int fd, uint32_t encoder_id))
SDL_KMSDRM_SYM(drmModeConnectorPtr,drmModeGetConnector,(int fd, uint32_t connector_id))
SDL_KMSDRM_SYM(int,drmHandleEvent,(int fd,drmEventContextPtr evctx))
SDL_KMSDRM_SYM(int,drmModePageFlip,(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data))
// Planes stuff.
SDL_KMSDRM_SYM(int,drmSetClientCap,(int fd, uint64_t capability, uint64_t value))
SDL_KMSDRM_SYM(drmModePlaneResPtr,drmModeGetPlaneResources,(int fd))
SDL_KMSDRM_SYM(drmModePlanePtr,drmModeGetPlane,(int fd, uint32_t plane_id))
SDL_KMSDRM_SYM(drmModeObjectPropertiesPtr,drmModeObjectGetProperties,(int fd,uint32_t object_id,uint32_t object_type))
SDL_KMSDRM_SYM(int,drmModeObjectSetProperty,(int fd, uint32_t object_id,
uint32_t object_type, uint32_t property_id,
uint64_t value))
SDL_KMSDRM_SYM(drmModePropertyPtr,drmModeGetProperty,(int fd, uint32_t propertyId))
SDL_KMSDRM_SYM(void,drmModeFreeProperty,(drmModePropertyPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeObjectProperties,(drmModeObjectPropertiesPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreePlane,(drmModePlanePtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreePlaneResources,(drmModePlaneResPtr ptr))
SDL_KMSDRM_SYM(int,drmModeSetPlane,(int fd, uint32_t plane_id, uint32_t crtc_id,
uint32_t fb_id, uint32_t flags,
int32_t crtc_x, int32_t crtc_y,
uint32_t crtc_w, uint32_t crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h))
// Planes stuff ends.
SDL_KMSDRM_MODULE(GBM)
SDL_KMSDRM_SYM(int,gbm_device_is_format_supported,(struct gbm_device *gbm,
uint32_t format, uint32_t usage))
SDL_KMSDRM_SYM(void,gbm_device_destroy,(struct gbm_device *gbm))
SDL_KMSDRM_SYM(struct gbm_device *,gbm_create_device,(int fd))
SDL_KMSDRM_SYM(unsigned int,gbm_bo_get_width,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(unsigned int,gbm_bo_get_height,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_stride,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_format,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(union gbm_bo_handle,gbm_bo_get_handle,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(int,gbm_bo_write,(struct gbm_bo *bo, const void *buf, size_t count))
SDL_KMSDRM_SYM(struct gbm_device *,gbm_bo_get_device,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(void,gbm_bo_set_user_data,(struct gbm_bo *bo, void *data,
void (*destroy_user_data)(struct gbm_bo *, void *)))
SDL_KMSDRM_SYM(void *,gbm_bo_get_user_data,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(void,gbm_bo_destroy,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(struct gbm_bo *,gbm_bo_create,(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format, uint32_t usage))
SDL_KMSDRM_SYM(struct gbm_surface *,gbm_surface_create,(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format, uint32_t flags))
SDL_KMSDRM_SYM(void,gbm_surface_destroy,(struct gbm_surface *surf))
SDL_KMSDRM_SYM(struct gbm_bo *,gbm_surface_lock_front_buffer,(struct gbm_surface *surf))
SDL_KMSDRM_SYM(void,gbm_surface_release_buffer,(struct gbm_surface *surf, struct gbm_bo *bo))
SDL_KMSDRM_SYM_OPT(uint64_t,gbm_bo_get_modifier,(struct gbm_bo *bo))
SDL_KMSDRM_SYM_OPT(int,gbm_bo_get_plane_count,(struct gbm_bo *bo))
SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_offset,(struct gbm_bo *bo, int plane))
SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_stride_for_plane,(struct gbm_bo *bo, int plane))
SDL_KMSDRM_SYM_OPT(union gbm_bo_handle,gbm_bo_get_handle_for_plane,(struct gbm_bo *bo, int plane))
#undef SDL_KMSDRM_MODULE
#undef SDL_KMSDRM_SYM
#undef SDL_KMSDRM_SYM_CONST
#undef SDL_KMSDRM_SYM_OPT
/* *INDENT-ON* */ // clang-format on

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,176 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_kmsdrmvideo_h
#define SDL_kmsdrmvideo_h
#include "../SDL_sysvideo.h"
#include <fcntl.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <EGL/egl.h>
#ifndef DRM_FORMAT_MOD_INVALID
#define DRM_FORMAT_MOD_INVALID 0x00ffffffffffffffULL
#endif
#ifndef DRM_MODE_FB_MODIFIERS
#define DRM_MODE_FB_MODIFIERS 2
#endif
#ifndef DRM_MODE_PAGE_FLIP_ASYNC
#define DRM_MODE_PAGE_FLIP_ASYNC 2
#endif
#ifndef DRM_MODE_OBJECT_CONNECTOR
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
#endif
#ifndef DRM_MODE_OBJECT_CRTC
#define DRM_MODE_OBJECT_CRTC 0xcccccccc
#endif
#ifndef DRM_CAP_ASYNC_PAGE_FLIP
#define DRM_CAP_ASYNC_PAGE_FLIP 7
#endif
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 8
#endif
#ifndef DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_HEIGHT 9
#endif
#ifndef GBM_FORMAT_ARGB8888
#define GBM_FORMAT_ARGB8888 ((uint32_t)('A') | ((uint32_t)('R') << 8) | ((uint32_t)('2') << 16) | ((uint32_t)('4') << 24))
#define GBM_BO_USE_CURSOR (1 << 1)
#define GBM_BO_USE_WRITE (1 << 3)
#define GBM_BO_USE_LINEAR (1 << 4)
#endif
struct SDL_VideoData
{
int devindex; // device index that was passed on creation
int drm_fd; // DRM file desc
char devpath[32]; // DRM dev path.
struct gbm_device *gbm_dev;
bool video_init; // Has VideoInit succeeded?
bool vulkan_mode; // Are we in Vulkan mode? One VK window is enough to be.
bool async_pageflip_support; // Does the hardware support async. pageflips?
SDL_Window **windows;
int max_windows;
int num_windows;
/* Even if we have several displays, we only have to
open 1 FD and create 1 gbm device. */
bool gbm_init;
};
struct SDL_DisplayModeData
{
int mode_index;
};
struct SDL_DisplayData
{
drmModeConnector *connector;
drmModeCrtc *crtc;
drmModeModeInfo mode;
drmModeModeInfo original_mode;
drmModeModeInfo fullscreen_mode;
drmModeCrtc *saved_crtc; // CRTC to restore on quit
bool saved_vrr;
/* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's internal struct,
because setting/unsetting up these is done on window creation/destruction,
where we may not have an SDL_Cursor at all (so no SDL_Cursor internal).
There's only one cursor GBM BO because we only support one cursor. */
struct gbm_bo *cursor_bo;
int cursor_bo_drm_fd;
uint64_t cursor_w, cursor_h;
bool default_cursor_init;
};
struct SDL_WindowData
{
SDL_VideoData *viddata;
/* SDL internals expect EGL surface to be here, and in KMSDRM the GBM surface is
what supports the EGL surface on the driver side, so all these surfaces and buffers
are expected to be here, in the struct pointed by SDL_Window internal pointer:
this one. So don't try to move these to dispdata! */
struct gbm_surface *gs;
struct gbm_bo *bo;
struct gbm_bo *next_bo;
bool waiting_for_flip;
bool double_buffer;
EGLSurface egl_surface;
bool egl_surface_dirty;
};
typedef struct KMSDRM_FBInfo
{
int drm_fd; // DRM file desc
uint32_t fb_id; // DRM framebuffer ID
} KMSDRM_FBInfo;
// Helper functions
extern bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window);
extern KMSDRM_FBInfo *KMSDRM_FBFromBO(SDL_VideoDevice *_this, struct gbm_bo *bo);
extern KMSDRM_FBInfo *KMSDRM_FBFromBO2(SDL_VideoDevice *_this, struct gbm_bo *bo, int w, int h);
extern bool KMSDRM_WaitPageflip(SDL_VideoDevice *_this, SDL_WindowData *windata);
/****************************************************************************/
// SDL_VideoDevice functions declaration
/****************************************************************************/
// Display and window functions
extern bool KMSDRM_VideoInit(SDL_VideoDevice *_this);
extern void KMSDRM_VideoQuit(SDL_VideoDevice *_this);
extern bool KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool KMSDRM_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void KMSDRM_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool KMSDRM_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_FullscreenResult KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen);
extern void KMSDRM_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void KMSDRM_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_kmsdrmvideo_h

View file

@ -0,0 +1,520 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
* Based on Jacob Lifshay's SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM)
#include "../SDL_vulkan_internal.h"
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmdyn.h"
#include "SDL_kmsdrmvulkan.h"
#include <sys/ioctl.h>
#ifdef SDL_PLATFORM_OPENBSD
#define DEFAULT_VULKAN "libvulkan.so"
#else
#define DEFAULT_VULKAN "libvulkan.so.1"
#endif
bool KMSDRM_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 i, extensionCount = 0;
bool hasSurfaceExtension = false;
bool hasDisplayExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan already loaded");
}
// Load the Vulkan library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
path = DEFAULT_VULKAN;
}
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
if (!_this->vulkan_config.loader_handle) {
return false;
}
SDL_strlcpy(_this->vulkan_config.loader_path, path,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr) {
goto fail;
}
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
goto fail;
}
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (!extensions) {
goto fail;
}
for (i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = true;
} else if (SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasDisplayExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasDisplayExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_DISPLAY_EXTENSION_NAME "extension");
goto fail;
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void KMSDRM_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
if (_this->vulkan_config.loader_handle) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
/*********************************************************************/
// Here we can put whatever Vulkan extensions we want to be enabled
// at instance creation, which is done in the programs, not in SDL.
// So: programs call SDL_Vulkan_GetInstanceExtensions() and here
// we put the extensions specific to this backend so the programs
// get a list with the extension we want, so they can include that
// list in the ppEnabledExtensionNames and EnabledExtensionCount
// members of the VkInstanceCreateInfo struct passed to
// vkCreateInstance().
/*********************************************************************/
char const* const* KMSDRM_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count)
{
static const char *const extensionsForKMSDRM[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
};
if (count) {
*count = SDL_arraysize(extensionsForKMSDRM);
}
return extensionsForKMSDRM;
}
/***********************************************************************/
// First thing to know is that we don't call vkCreateInstance() here.
// Instead, programs using SDL and Vulkan create their Vulkan instance
// and we get it here, ready to use.
// Extensions specific for this platform are activated in
// KMSDRM_Vulkan_GetInstanceExtensions(), like we do with
// VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK.
/***********************************************************************/
bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
VkPhysicalDevice gpu = NULL;
uint32_t gpu_count;
uint32_t display_count;
uint32_t mode_count;
uint32_t plane_count;
uint32_t plane = UINT32_MAX;
VkPhysicalDevice *physical_devices = NULL;
VkPhysicalDeviceProperties *device_props = NULL;
VkDisplayPropertiesKHR *display_props = NULL;
VkDisplayModePropertiesKHR *mode_props = NULL;
VkDisplayPlanePropertiesKHR *plane_props = NULL;
VkDisplayPlaneCapabilitiesKHR plane_caps;
VkDisplayModeCreateInfoKHR display_mode_create_info;
VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
VkExtent2D image_size;
VkDisplayKHR display;
VkDisplayModeKHR display_mode = (VkDisplayModeKHR)0;
VkDisplayModePropertiesKHR display_mode_props = { 0 };
VkDisplayModeParametersKHR new_mode_parameters = { { 0, 0 }, 0 };
// Prefer a plane that supports per-pixel alpha.
VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
VkResult result;
bool ret = false;
bool valid_gpu = false;
bool mode_found = false;
bool plane_supports_display = false;
// Get the display index from the display being used by the window.
int display_index = SDL_GetDisplayIndex(SDL_GetDisplayForWindow(window));
int i, j;
// Get the function pointers for the functions we will use.
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR =
(PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr(
instance, "vkCreateDisplayPlaneSurfaceKHR");
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
(PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
instance, "vkEnumeratePhysicalDevices");
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties =
(PFN_vkGetPhysicalDeviceProperties)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceProperties");
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
(PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR =
(PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayModePropertiesKHR");
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR =
(PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
(PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
(PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
instance, "vkGetDisplayPlaneCapabilitiesKHR");
PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR =
(PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr(
instance, "vkCreateDisplayModeKHR");
if (!_this->vulkan_config.loader_handle) {
SDL_SetError("Vulkan is not loaded");
goto clean;
}
/*************************************/
// Block for vulkan surface creation
/*************************************/
/****************************************************************/
// If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means
// that the VK_KHR_Display extension is active on the instance.
// That's the central extension we need for x-less VK!
/****************************************************************/
if (!vkCreateDisplayPlaneSurfaceKHR) {
SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
" extension is not enabled in the Vulkan instance.");
goto clean;
}
/* A GPU (or physical_device, in vkcube terms) is a physical GPU.
A machine with more than one video output doesn't need to have more than one GPU,
like the Pi4 which has 1 GPU and 2 video outputs.
Just in case, we test that the GPU we choose is Vulkan-capable.
If there are new reports about VK init failures, hardcode
gpu = physical_devices[0], instead of probing, and go with that.
*/
// Get the physical device count.
vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
if (gpu_count == 0) {
SDL_SetError("Vulkan can't find physical devices (gpus).");
goto clean;
}
// Get the physical devices.
physical_devices = SDL_malloc(sizeof(VkPhysicalDevice) * gpu_count);
device_props = SDL_malloc(sizeof(VkPhysicalDeviceProperties));
vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
// Iterate on the physical devices.
for (i = 0; i < gpu_count; i++) {
// Get the physical device properties.
vkGetPhysicalDeviceProperties(
physical_devices[i],
device_props);
// Is this device a real GPU that supports API version 1 at least?
if (device_props->apiVersion >= 1 &&
(device_props->deviceType == 1 || device_props->deviceType == 2)) {
gpu = physical_devices[i];
valid_gpu = true;
break;
}
}
if (!valid_gpu) {
SDL_SetError("Vulkan can't find a valid physical device (gpu).");
goto clean;
}
/* A display is a video output. 1 GPU can have N displays.
Vulkan only counts the connected displays.
Get the display count of the GPU. */
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
if (display_count == 0) {
SDL_SetError("Vulkan can't find any displays.");
goto clean;
}
// Get the props of the displays of the physical device.
display_props = (VkDisplayPropertiesKHR *)SDL_malloc(display_count * sizeof(*display_props));
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
&display_count,
display_props);
// Get the chosen display based on the display index.
display = display_props[display_index].display;
// Get the list of the display videomodes.
vkGetDisplayModePropertiesKHR(gpu,
display,
&mode_count, NULL);
if (mode_count == 0) {
SDL_SetError("Vulkan can't find any video modes for display %i (%s)", 0,
display_props[display_index].displayName);
goto clean;
}
mode_props = (VkDisplayModePropertiesKHR *)SDL_malloc(mode_count * sizeof(*mode_props));
vkGetDisplayModePropertiesKHR(gpu,
display,
&mode_count, mode_props);
/* Get a video mode equal to the window size among the predefined ones,
if possible.
REMEMBER: We have to get a small enough videomode for the window size,
because videomode determines how big the scanout region is and we can't
scanout a region bigger than the window (we would be reading past the
buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */
for (i = 0; i < mode_count; i++) {
if (mode_props[i].parameters.visibleRegion.width == window->w &&
mode_props[i].parameters.visibleRegion.height == window->h) {
display_mode_props = mode_props[i];
mode_found = true;
break;
}
}
if (mode_found &&
display_mode_props.parameters.visibleRegion.width > 0 &&
display_mode_props.parameters.visibleRegion.height > 0) {
// Found a suitable mode among the predefined ones. Use that.
display_mode = display_mode_props.displayMode;
} else {
/* Couldn't find a suitable mode among the predefined ones, so try to create our own.
This won't work for some video chips atm (like Pi's VideoCore) so these are limited
to supported resolutions. Don't try to use "closest" resolutions either, because
those are often bigger than the window size, thus causing out-of-bunds scanout. */
new_mode_parameters.visibleRegion.width = window->w;
new_mode_parameters.visibleRegion.height = window->h;
/* SDL (and DRM, if we look at drmModeModeInfo vrefresh) uses plain integer Hz for
display mode refresh rate, but Vulkan expects higher precision. */
new_mode_parameters.refreshRate = (uint32_t)(window->current_fullscreen_mode.refresh_rate * 1000);
SDL_zero(display_mode_create_info);
display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
display_mode_create_info.parameters = new_mode_parameters;
result = vkCreateDisplayModeKHR(gpu,
display,
&display_mode_create_info,
NULL, &display_mode);
if (result != VK_SUCCESS) {
SDL_SetError("Vulkan couldn't find a predefined mode for that window size and couldn't create a suitable mode.");
goto clean;
}
}
// Just in case we get here without a display_mode.
if (!display_mode) {
SDL_SetError("Vulkan couldn't get a display mode.");
goto clean;
}
// Get the list of the physical device planes.
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL);
if (plane_count == 0) {
SDL_SetError("Vulkan can't find any planes.");
goto clean;
}
plane_props = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, plane_props);
/* Iterate on the list of planes of the physical device
to find a plane that matches these criteria:
-It must be compatible with the chosen display + mode.
-It isn't currently bound to another display.
-It supports per-pixel alpha, if possible. */
for (i = 0; i < plane_count; i++) {
uint32_t supported_displays_count = 0;
VkDisplayKHR *supported_displays;
// See if the plane is compatible with the current display.
vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_displays_count, NULL);
if (supported_displays_count == 0) {
// This plane doesn't support any displays. Continue to the next plane.
continue;
}
// Get the list of displays supported by this plane.
supported_displays = (VkDisplayKHR *)SDL_malloc(sizeof(VkDisplayKHR) * supported_displays_count);
vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i,
&supported_displays_count, supported_displays);
/* The plane must be bound to the chosen display, or not in use.
If none of these is true, iterate to another plane. */
if (!((plane_props[i].currentDisplay == display) || (plane_props[i].currentDisplay == VK_NULL_HANDLE))) {
continue;
}
/* Iterate the list of displays supported by this plane
in order to find out if the chosen display is among them. */
plane_supports_display = false;
for (j = 0; j < supported_displays_count; j++) {
if (supported_displays[j] == display) {
plane_supports_display = true;
break;
}
}
// Free the list of displays supported by this plane.
if (supported_displays) {
SDL_free(supported_displays);
}
// If the display is not supported by this plane, iterate to the next plane.
if (!plane_supports_display) {
continue;
}
// Want a plane that supports the alpha mode we have chosen.
vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps);
if (plane_caps.supportedAlpha == alpha_mode) {
// Yep, this plane is alright.
plane = i;
break;
}
}
// If we couldn't find an appropriate plane, error out.
if (plane == UINT32_MAX) {
SDL_SetError("Vulkan couldn't find an appropriate plane.");
goto clean;
}
/********************************************/
// Let's finally create the Vulkan surface!
/********************************************/
image_size.width = window->w;
image_size.height = window->h;
SDL_zero(display_plane_surface_create_info);
display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
display_plane_surface_create_info.displayMode = display_mode;
display_plane_surface_create_info.planeIndex = plane;
display_plane_surface_create_info.imageExtent = image_size;
display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
display_plane_surface_create_info.alphaMode = alpha_mode;
result = vkCreateDisplayPlaneSurfaceKHR(instance,
&display_plane_surface_create_info,
allocator,
surface);
if (result != VK_SUCCESS) {
SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
SDL_Vulkan_GetResultString(result));
goto clean;
}
ret = true; // success!
clean:
if (physical_devices) {
SDL_free(physical_devices);
}
if (display_props) {
SDL_free(display_props);
}
if (device_props) {
SDL_free(device_props);
}
if (plane_props) {
SDL_free(plane_props);
}
if (mode_props) {
SDL_free(mode_props);
}
return ret;
}
void KMSDRM_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator)
{
if (_this->vulkan_config.loader_handle) {
SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
}
}
#endif

View file

@ -0,0 +1,51 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
* Based on Jacob Lifshay's SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#ifndef SDL_kmsdrm_vulkan_h_
#define SDL_kmsdrm_vulkan_h_
#include <SDL3/SDL_vulkan.h>
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM)
extern bool KMSDRM_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void KMSDRM_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const* const* KMSDRM_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void KMSDRM_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
#endif
#endif // SDL_kmsdrm_vulkan_h_